skip to navigation
skip to content

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.

October 17, 2026 10:33 PM UTC


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.

October 17, 2026 08:36 PM UTC


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.

October 17, 2026 04:32 PM UTC


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 17, 2026 11:36 AM UTC

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 16, 2026 06:00 AM UTC

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.[......]

October 15, 2026 04:37 PM UTC


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:...

October 15, 2026 03:14 PM UTC


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 15, 2026 12:00 AM UTC

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":...

October 14, 2026 12:29 PM UTC


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:

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:

https://pypy.org/download.html

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:

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

October 14, 2026 07:42 AM UTC


Python Bytes

#500 Episode 500!

Topics include and.

October 14, 2026 07:30 AM UTC


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.

October 14, 2026 06:00 AM UTC


Mark Dufour

CPython 3.15 (JIT) Performance Showdown

October 14, 2026 04:30 AM UTC


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 14, 2026 12:00 AM UTC

October 13, 2026


PyCoder’s Weekly

Issue #756: 3.15 Released For Real This Time, Muse, and More (2026-10-13)

October 13, 2026 07:30 PM UTC


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 […]

October 13, 2026 01:41 PM UTC


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...

October 13, 2026 01:11 PM UTC


PyCharm

Python vs Go: A Practical Comparison for Developers

October 13, 2026 09:02 AM UTC


Python Insider

Python 3.16.0 alpha 1 is now available

The first developer preview of Python 3.16 is ready for testing.

October 13, 2026 12:00 AM UTC


Seth Michael Larson

Gaming’s all-digital future doesn’t include “Sonic the Hedgehog 3” and “Pikmin 2”

October 13, 2026 12:00 AM UTC

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...

October 12, 2026 12:46 PM UTC


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.

October 12, 2026 06:00 AM UTC


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 12, 2026 02:27 AM UTC

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”.

October 11, 2026 10:00 PM UTC


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.

October 11, 2026 12:25 PM UTC