Planet Python
Last update: October 18, 2026 04:48 AM UTC
October 17, 2026
The Python Coding Stack
2. Who actually does the work, the model or the software?
Agents Unpacked • Chapter 2: The model chooses, the tools act, and the harness connects them.
EuroPython
Humans of EuroPython: Emma Cooke
“[The] first time I attended EuroPython I was just in awe of the dedication of the volunteer team, and I wanted to know more about how it all came together.” An interview with Emma Cooke.
Talk Python Blog
Introducing Talk Python Membership
For years, I’ve wanted to offer something premium for podcast listeners. The obvious option was an ad-free feed. That never sat right with me. No ads is not much of a bonus. So over the last few months, I’ve been building something that goes beyond the podcast and beyond the courses. Today I’m thrilled to announce Talk Python Membership.
In short, membership gets you community, live tech meetups, episode previews, and yes an ad-free feed.
Talk Python to Me
#566: State of the art in Python 2026
Which of your Python tools would you still pick if you started fresh today? For a long time, the answer was easy. The stack felt settled. AI has reopened almost every one of those choices. Some tools got faster. Others got replaced outright. On this episode, a panel takes stock of the state of the art in Python for 2026. Calvin Hendryx-Parker is CTO and co-founder of Six Feet Up. Pat Decker works in DevOps at Hibu. Reuven Lerner has trained Python developers for 15 years. Sydney Runkle does product management for open source at LangChain. One of them barely opens an editor anymore. Listen in to hear the surprising replacement.
October 16, 2026
LernerPython blog, from Reuven Lerner
Pandas Grouper: Group datetime columns by custom time periods
Want to change the granularity when grouping on a Python Pandas data frame’s datetime column? Use Grouper. For example: Here we get the mean trip_distance per 14-day period:
The post Pandas Grouper: Group datetime columns by custom time periods appeared first on LernerPython.
October 15, 2026
Artem Golubin
Python's binary wheels and supply chain attacks
Python is known for fast packages despite being a slow language. The fastest Python packages are written in compiled languages such as C, C++, or Rust, with Python acting only as a glue.
Python users usually don't even notice that, because PyPI (Python's package index) provides
precompiled binaries.
They are called wheels, and when you run pip install, it prefers them over the source distribution.
For example, each release of my packages ships up to 90 different wheels.
You need a separate binary wheel for each Python version, platform, and architecture
combination. The regular distribution only contains the source code.
Without Python wheels, the distribution would be much harder. When wheels are not available, a user has to compile the code when installing a package. That requires the correct compiler and system dependencies.
Since I've spent quite a while analyzing malicious packages on PyPI, this got me thinking about wheels.[......]
Rodrigo Girão Serrão
Unpacking inside comprehensions
Learn how to use the unpacking operator inside list comprehensions.
Starting with Python 3.15, you can use the asterisk * to unpack inside list comprehensions:
nested = [[0, 1, 2], [3, 4], [], [5, 6, 7]]
flattened = [*sublist for sublist in nested]
print(flattened) # [0, 1, 2, 3, 4, 5, 6, 7]
This new unpacking feature also works with set comprehensions, dictionary comprehensions, and generator expressions. This article explains the syntax, the alternatives in Python 3.14 or earlier, and shows real world example applications.
15 days of Python 3.15
To celebrate the release of Python 3.15, over the next 15 business days I'll be writing about a new 3.15 feature every day. Explained clearly and with examples so you don't have to sift through the changelog.
Subscribe to receive this free email series:
If you want, you can also check the schedule of the upcoming emails.
Unpacking inside a comprehension
If you have a nested list of lists, you can flatten them out by unpacking inside a comprehension:
nested = [[0, 1, 2], [3, 4], [], [5, 6, 7]]
flattened = [*sublist for sublist in nested]
print(flattened) # [0, 1, 2, 3, 4, 5, 6, 7]
Before the unpacking syntax in 3.15, you'd have to write a nested list comprehension:
nested = [[0, 1, 2], [3, 4], [], [5, 6, 7]]
flattened = [
element
for sublist in nested
for element in sublist
]
print(flattened) # [0, 1, 2, 3, 4, 5, 6, 7]
The structure of the nested list comprehension mirrors the structure of the equivalent nested for loop:
nested = [[0, 1, 2], [3, 4], [], [5, 6, 7]]
for sublist in nested:...
Bob Belderbos
My bookkeeping runs on code now. AI only reads the PDFs.
I paid for a popular bookkeeping tool and it worked well. But the UI kept getting slower, and I wasn't using its reporting, so I had no real view of my numbers.
So I did what I usually do: I scoped a solution, built the smallest thing that worked and tested it.
October 14, 2026
Rodrigo Girão Serrão
frozendict built-in
Learn how to use the new frozendict built-in to create and manipulate immutable dictionaries.
Python 3.15 introduced a new built-in data type: the frozendict. A frozendict is an immutable dictionary that can be created with the built-in of the same name:
version_info = frozendict(
{"major": 3, "minor": 15, "patch": 0}
)
The dictionary literal syntax using the curly braces {} creates a regular dictionary, so you must always use the built-in frozendict to create a new frozen dictionary.
In this article you'll learn about the characteristics of this immutable dictionary like its hashability, along with how to use this in conjunction with dict in typing contexts.
15 days of Python 3.15
To celebrate the release of Python 3.15, over the next 15 business days I'll be writing about a new 3.15 feature every day. Explained clearly and with examples so you don't have to sift through the changelog.
Subscribe to receive this free email series:
If you want, you can also check the schedule of the upcoming emails.
Creation and usage
You create immutable dictionaries with the built-in frozendict and you can access key/value pairs with the regular dictionary syntax:
version_info = frozendict({"major": 3, "minor": 15, "patch": 0})
print(version_info["major"]) # 3
You can also iterate through a frozendict's keys, values, or items:
for name, number in version_info.items():
print(name, number)
"""
major 3
minor 15
patch 0
"""
If you don't have to support Python 3.14 or earlier, you should use frozendict whenever you're creating or returning a dictionary that isn't supposed to be explicitly and clearly used as mutable.
Immutability of a frozendict
The main premise of frozendict is that it's immutable:
version_info = frozendict({"major":...
PyPy
PyPy v8.0.1 release
PyPy v8.0.1: release of python 2.7, 3.11, and 3.12 beta released 2026-09-14
The PyPy team is proud to release version 8.0.1 of PyPy after the previous
release on Sept 25, 2026. Some problems with buffer memory leaks were fixed,
and problems around the new strategy to tie RPython objects to non-managed C
PyObject objects were also fixed. The stdlib for 3.11 and 3.12 was updated,
as was the vendored libexpat (to 2.8.5). Internally, we now use pytest3.10
rather than 2.9.2.
The release includes three different interpreters:
PyPy2.7, supporting the syntax and the features of Python 2.7 including the stdlib for CPython 2.7.18+ (the
+is for backported security updates)PyPy3.11, supporting the syntax and the features of Python 3.11, including the stdlib for CPython 3.11.17. Barring security issues, this will be the last release to support 3.11.
PyPy3.12, supporting the syntax and features of Python 3.12, including the stdlib for CPython 3.12.15.
The interpreters are based on much the same codebase, thus the triple release.
We recommend updating. You can find links to download the releases here:
We would like to thank our donors for the continued support of the PyPy project. If PyPy is not quite good enough for your needs, we are available for direct consulting work. If PyPy is helping you out, we would love to hear about it and encourage submissions to our blog via a pull request to https://github.com/pypy/pypy.org
We would also like to thank our contributors and encourage new people to join the project. PyPy has many layers and we need help with all of them: bug fixes, PyPy and RPython documentation improvements, or general help with making RPython's JIT even better.
If you are a python library maintainer and use C-extensions, please consider making a CFFI version of your library that would be performant on PyPy. Failing that, PyPy will soon support the cp312-abi3 tag for limited ABI wheels. In any case, cibuildwheel supports building wheels for PyPy.
What is PyPy?
PyPy is a Python interpreter, a drop-in replacement for CPython. It's fast (PyPy and CPython performance comparison) due to its integrated tracing JIT compiler.
We also welcome developers of other dynamic languages to see what RPython can do for them.
We provide binary builds for:
x86 machines on most common operating systems (Linux 32/64 bits, Mac OS 64 bits, Windows 64 bits)
64-bit ARM machines running Linux (
aarch64) and macos (macos_arm64).
PyPy supports Windows 32-bit, Linux PPC64 big- and little-endian, Linux ARM 32 bit, RISC-V RV64IMAFD Linux, and s390x Linux but does not release binaries. Please reach out to us if you wish to sponsor binary releases for those platforms. Downstream packagers provide binary builds for debian, Fedora, conda, OpenBSD, FreeBSD, Gentoo, and more.
What else is new?
For more information about the release, see the full changelog.
Please update, and continue to help us make pypy better.
Cheers, The PyPy Team
Python Bytes
#500 Episode 500!
Topics include and.
LernerPython blog, from Reuven Lerner
Pandas groupby on datetime parts with the dt accessor
If your Python Pandas data frame has a datetime column, you can group on a portion of that via the “dt” accessor. For example, let’s get mean trip_distance for each […]
The post Pandas groupby on datetime parts with the dt accessor appeared first on LernerPython.
Mark Dufour
CPython 3.15 (JIT) Performance Showdown
PyCon Ireland
Call for sponsors PyCon Ireland 2026
We're excited to announce PyCon Ireland 2026, taking place on 21st Nov at Chartered Accountants House, 47-49 Pearse St, Dublin 2, D02 YN40, Ireland. Be part of this event and make use of this opportunity for coporate outreach and brand promotion
October 13, 2026
PyCoder’s Weekly
Issue #756: 3.15 Released For Real This Time, Muse, and More (2026-10-13)
PyCharm
Two things are true about Python: It’s the most popular programming language in the world, and it’s the most common companion language for Go developers. How do we know that? From the Developer Ecosystem 2026 survey by JetBrains. But we also know a lot more about the developers who use these two languages – both […]
Rodrigo Girão Serrão
sentinel built-in
Learn how to use the sentinel built-in from Python 3.15 for placeholder values.
Python 3.15 introduced the new sentinel built-in.
The sentinel built-in can be used to create placeholder values:
NOTHING = sentinel("NOTHING")
A placeholder created by sentinel is the more idiomatic way to provide default arguments or return values where None wouldn't be usable.
15 days of Python 3.15
To celebrate the release of Python 3.15, over the next 15 business days I'll be writing about a new 3.15 feature every day. Explained clearly and with examples so you don't have to sift through the changelog.
Subscribe to receive this free email series:
If you want, you can also check the schedule of the upcoming emails.
Creating and using sentinels
The built-in sentinel accepts a string argument which is the name of the sentinel:
NOTHING = sentinel("NOTHING")
print(NOTHING) # NOTHING
The name of the sentinel is used as its default string representation.
Alternatively, you can provide a second named argument to be used as the representation of the sentinel:
NOTHING = sentinel("NOTHING", repr="__main__.NOTHING")
print(NOTHING) # __main__.NOTHING
While there's no runtime restriction imposed, the name of the sentinel and the name of the variable that the sentinel is assigned to should match:
# Correct:
NOTHING = sentinel("NOTHING")
# Wrong:
NOTHING = sentinel("nothing")
N = sentinel("NOTHING")
BANANAS = sentinel("APPLES")
Linters and type checkers may complain if there's a mismatch. Since sentinels are typically constants you won't update, it's also common for sentinels to have names in all uppercase.
Sentinels are typically used as placeholders and the most common operation is to check if a value is a specific sentinel.
For example, imagine a function find that looks...
PyCharm
Python vs Go: A Practical Comparison for Developers
Python Insider
Python 3.16.0 alpha 1 is now available
The first developer preview of Python 3.16 is ready for testing.
Seth Michael Larson
Gaming’s all-digital future doesn’t include “Sonic the Hedgehog 3” and “Pikmin 2”
October 12, 2026
Rodrigo Girão Serrão
Explicit lazy imports
Learn how to use lazy imports to speed up the startup time of your Python applications.
Python 3.15 introduced explicit lazy imports.
An explicit lazy import is an import statement that starts with the keyword lazy:
import json
lazy import math
lazy from pathlib import Path
The module json is imported normally, but math and Path are imported lazily.
If an import is lazy, the module will only be loaded when it is needed instead of being loaded immediately when Python encounters the import statement. This article explains how to use lazy imports and how to write backwards-compatible code with these features.
15 days of Python 3.15
To celebrate the release of Python 3.15, over the next 15 business days I'll be writing about a new 3.15 feature every day. Explained clearly and with examples so you don't have to sift through the changelog.
Subscribe to receive this free email series:
If you want, you can also check the schedule of the upcoming emails.
Why does Python need lazy imports?
The import system in Python loads a module when it encounters the import statement.
That means that when Python finds a statement import some_module it looks for the corresponding Python module file and runs it.
In turn, that file might import other modules that will run at that point in time, and this branching can be arbitrarily deep.
Since Python files usually collect all imports at the top, this means that you may spend an arbitrarily long amount of time taking care of your imports even before your start running your own code. If there are situations where you won't even end up needing those...
LernerPython blog, from Reuven Lerner
Pandas groupby: Aggregating multiple columns with multiple functions
Does Python Pandas allow multiple data columns and multiple methods? Yes! The above returns a data frame. Its 4 columns are a 2-level multi-index with mean/std in the inner level.
The post Pandas groupby: Aggregating multiple columns with multiple functions appeared first on LernerPython.
Mike C. Fletcher
PyOpenGL 4.0.0a7 -- Cleanup and Preparation
4.0.0a7 is closing in on the final form for the 4.0.0 release. There's a raft of bug-fixes from testing and tickets, the Windows GLUT binaries are now a separate python package, documentation system updates, and OpenGL_accelerate now pins the OpenGL version exactly.
At this point it's likely worth testing your PyOpenGL codebases with the alpha to report any regressions. This is a major release with a raft of changes. It's also tested far more than any PyOpenGL release has ever been tested, so I'm relatively confident that it works in the sense of letting you write 3D rendering engines with modern features, but there are potentially going to be regressions, particularly if you have older code (the majority of testing is all on OpenGL 3.3+ contexts with legacy-free feature-sets), or Tk based apps (where there's a new widget implementation).
We're still missing some Mac work, porting the new Tk widget to use CGL. We've also not yet got confirmation that we've got real-world (as opposed to CI) operation all working on Macs.
October 11, 2026
Kay Hayen
Nuitka Release 4.3
This is to inform you about the new stable release of Nuitka. It is the extremely compatible Python compiler, “download now”.
Ned Batchelder
Making git hooks visible
Frank Wiles wrote about an attack he nearly fell victim to: a downloaded git repo had active git hooks that would have compromised his system. Seeing the risks, I wanted to make git hooks more visible on my own machine.
A git hook is a script in the .git directory of a git repo on your machine. They run during various git operations. The important detail to understand about hooks is that they do not get installed locally when you clone a repo. A newly cloned repo never has active hooks. Hooks have to be enabled after cloning, usually by you.
The attack Frank saw was dangerous because it wasn’t a cloned repo: it was a local checkout that had hooks installed then shared as a Dropbox folder. So when it landed on Frank’s machine, the hooks were active without him realizing it.
Git hooks are not apparent: they are meant to sit quietly until they run and ideally to be unobtrusive even then. But this attack made me want to know what hooks were in each of my git directories.
In zsh, you can define a function called chpwd, and it will execute whenever the current directory changes. Using this feature, I could create a script that lists the active hooks when I change into a git directory:
git_show_hooks() {
local git_dir=$(git rev-parse --absolute-git-dir 2>/dev/null)
if [[ -z $git_dir ]]; then
_git_shown_hooks_dir=
return
fi
local hooks_dir=$(git rev-parse --git-path hooks)
hooks_dir=${hooks_dir:a}
# Only announce once per repo, not for every cd inside it.
if [[ $hooks_dir == $_git_shown_hooks_dir ]]; then
return
fi
_git_shown_hooks_dir=$hooks_dir
local -a hooks
local hook
setopt localoptions nullglob
for hook in $hooks_dir/*; do
if [[ -f $hook && -x $hook && $hook != *.sample ]]; then
hooks+=(${hook:t})
fi
done
if (( $#hooks )); then
echo -e "\e[31mgit hooks:\e[0m $hooks (from $git_dir)"
fi
}
autoload -Uz add-zsh-hook
add-zsh-hook chpwd git_show_hooks
Now entering a git repo I get an announcement of active hooks, if any. It’s very rare to see a hook other than pre-commit, but if any are there, they will be reported. The message will only be shown the first time I cd into a repo to keep the noise down.
Once I had this in place, there was another git hook consideration I could address: it’s common to have a .pre-commit-config.yaml file in a repo, but it’s easy to forget to enable the pre-commit hook that will actually use the file. Many developers probably have cloned repos where they haven’t enabled the pre-commit hook, so the desired checks aren’t running.
A second chpwd function can look for the configuration file and let you know if you have the pre-commit hook enabled:
git_check_precommit() {
local toplevel=$(git rev-parse --show-toplevel 2>/dev/null)
if [[ -z $toplevel ]]; then
_git_shown_precommit_dir=
return
fi
# Only announce once per repo, not for every cd inside it.
if [[ $toplevel == $_git_shown_precommit_dir ]]; then
return
fi
_git_shown_precommit_dir=$toplevel
if [[ ! -f $toplevel/.pre-commit-config.yaml ]]; then
return
fi
local hooks_dir=$(git rev-parse --git-path hooks)
hooks_dir=${hooks_dir:a}
local pc_tool=
if [[ -f $hooks_dir/pre-commit ]]; then
if grep -q 'File generated by prek:' $hooks_dir/pre-commit 2>/dev/null; then
pc_tool=prek
elif grep -q 'File generated by pre-commit:' $hooks_dir/pre-commit 2>/dev/null; then
pc_tool=pre-commit
fi
fi
if [[ -n $pc_tool ]]; then
echo "$pc_tool installed"
else
echo -e "\e[31mpre-commit:\e[0m not installed," \
"run 'pre-commit install' or 'prek install'" \
"($toplevel/.pre-commit-config.yaml)"
fi
}
add-zsh-hook chpwd git_check_precommit
One more step, just to be extra safe: zsh doesn’t run the chpwd function when starting a new shell. So we run the functions explicitly to check our initial directory:
# Run them once on shell startup to check our starting directory.
git_show_hooks
git_check_precommit
These shell functions are keeping me aware of the enabled git hooks. The messages might appear too often, and I might stop noticing them after a while, but I think this is much better than not knowing what’s going on. I haven’t seen any attacks, but I found at least one of my projects where I forgot to install pre-commit.
