Skip to content

Archive

Python

119 articles
Python 09 Sep 2026 9 min read

Update Immutable Records with Python copy.replace()

I like immutable value objects because they make state changes explicit. The awkward part is that Python has historically offered several different ways to create a slightly modified version of one. A dataclass has dataclasses.replace(). A named tuple has _replace(). A custom class usually needs its own helper. The operations are conceptually similar, but generic code has no single interface to target. Python 3.13 adds copy.replace() to close that gap. It creates a new object of the same type while replacing selected fields, and it works with dataclasses, named tuples, and classes that implement __replace__().

Python 09 Sep 2026 11 min read

Stop Stuck Process Pools with Python 3.14

ProcessPoolExecutor is a convenient way to spread CPU-bound Python work across multiple processes. Most of the time, its normal shutdown behavior is exactly what an application wants: stop accepting work, let running tasks finish, and clean up worker processes. Some failures do not fit that model. A worker can become stuck in native code, wait indefinitely on an external resource, or execute a task whose runtime has exceeded the application’s operational deadline. At that point, cancelling a Future may not stop work that is already running, and waiting for an orderly pool shutdown may take too long.

Python 09 Sep 2026 9 min read

Shut Down asyncio Worker Queues Cleanly

Asynchronous worker pools often start with a simple pattern: producers put jobs into an asyncio.Queue, consumers loop over get(), and the application waits for join() before exiting. The awkward part is shutdown. Older designs commonly put one sentinel value into the queue for each worker, cancel consumers after join(), or maintain a separate stop event. Each approach can work, but each adds a second protocol beside the queue itself. Python 3.13 added asyncio.Queue.shutdown() and the asyncio.QueueShutDown exception. They let the queue represent its own lifecycle: open for producers, shutting down while existing work drains, and finally closed to consumers.

Python 09 Sep 2026 10 min read

Run CPU-Bound Python with InterpreterPoolExecutor

For CPU-heavy Python work, I usually reach for ProcessPoolExecutor. Threads are convenient, but ordinary CPython threads do not give CPU-bound Python code the kind of multi-core parallelism people often expect. Python 3.14 adds another option: concurrent.futures.InterpreterPoolExecutor. It looks deliberately familiar. You still submit callables and receive futures, but every worker thread owns a separate Python interpreter. Each interpreter has its own GIL, so Python code in different workers can execute on different CPU cores at the same time.

Python 09 Sep 2026 10 min read

Reload Process Environment Variables with Python 3.14

Python applications often treat environment variables as if every read goes straight to the operating system. In CPython, that mental model is incomplete. os.environ is a mapping captured when the os module is first imported, normally during interpreter startup. If the process environment later changes through something outside that mapping, Python’s cached view can become stale. Python 3.14 adds os.reload_environ() for the unusual cases where an application really needs to refresh that view.

Python 09 Sep 2026 13 min read

Reduce Filesystem Stat Calls with Python Path.info

Filesystem code often looks cheap until it runs over a directory with hundreds of thousands of entries. A loop that asks whether every path is a file, directory, or symbolic link can translate into a large number of metadata queries. On a local SSD that cost may be tolerable. On network filesystems, container-mounted volumes, or very large trees, repeated metadata lookups can become a noticeable part of runtime. Python 3.14 adds Path.info, a cached file-type information interface on pathlib.Path. It is especially useful when paths come from Path.iterdir(), because Python may initialize the cache with information already obtained while scanning the directory.

Python 09 Sep 2026 12 min read

Model UUID Sentinels with uuid.NIL and uuid.MAX

UUIDs are often treated as ordinary identifiers: generate one, store it, compare it, and pass it between services. But some systems also need boundary or sentinel UUID values. A protocol may reserve an all-zero identifier for “no object.” A range query may need the lowest or highest possible UUID. Test fixtures may need deterministic endpoints without inventing magic strings. Python 3.14 makes those cases explicit with two constants from RFC 9562:

Python 09 Sep 2026 13 min read

Migrate Time-Based Identifiers from UUIDv1 to UUIDv6 in Python 3.14

UUID version 1 has been around for a long time. It combines a timestamp, a clock sequence, and a node identifier into a 128-bit value, which makes it useful when applications need identifiers that can be generated without coordinating through a central database sequence. Its layout has an awkward property, though: the timestamp bits are not arranged from most significant to least significant in the same order that ordinary UUID comparison uses.

Python 09 Sep 2026 11 min read

Manage Subinterpreters Directly with Python 3.14

Python 3.14 gives application code a new way to work directly with multiple interpreters in one process. The concurrent.interpreters module exposes a high-level API for creating interpreters, running code inside them, and communicating through cross-interpreter queues. It sits below InterpreterPoolExecutor: instead of submitting independent jobs to a ready-made pool, you own the interpreter lifecycle and decide how work reaches each isolated execution context. That extra control is useful, but it also removes several conveniences an executor normally provides. A subinterpreter is not a lightweight thread with shared globals, and creating one does not automatically create concurrency.

Python 09 Sep 2026 13 min read

Make Warning Tests Concurrency-Safe with Context-Aware Warnings

Python’s warnings.catch_warnings() is convenient in tests, compatibility shims, and small diagnostic scopes. It lets code temporarily change warning filters and then restore the previous state. That model becomes harder to reason about when several threads or asynchronous tasks use it at the same time. Historically, catch_warnings() manipulated process-global state in the warnings module. Two overlapping contexts could therefore interfere with each other. Python 3.14 adds an opt-in context-aware mode that changes this behavior. When sys.flags.context_aware_warnings is true, catch_warnings() stores its filtering state in a context variable instead of mutating the same global warning state for every concurrent execution path.

Python 09 Sep 2026 10 min read

Introspect Deferred Annotations with Python annotationlib

Python annotations are not only for static type checkers. Frameworks use them to build dependency graphs, validators inspect them to derive schemas, and documentation tools render them for humans. That makes annotation introspection part of the runtime behavior of many Python applications. Python 3.14 changes that behavior substantially. Annotations now use deferred evaluation by default, and the standard library adds annotationlib as the dedicated low-level interface for retrieving them. The important consequence is that annotation consumers should stop assuming there is one universally correct representation. Sometimes you want actual runtime values. Sometimes unresolved names must remain inspectable. Sometimes you only need readable text and should avoid resolving names entirely.

Python 09 Sep 2026 14 min read

Detect Python Packages with inspect.ispackage

Python tools often need to answer a deceptively simple question: is this imported object a package or an ordinary module? That distinction matters to plugin loaders, documentation generators, test discovery systems, command-line frameworks, code indexers, and developer tools. A package can contain importable children. An ordinary module cannot be traversed in the same way. Python 3.14 adds inspect.ispackage(), a small predicate that gives this question a standard-library name. import inspect import json import pathlib print(inspect.ispackage(json)) # True print(inspect.ispackage(pathlib)) # False The API is tiny. The surrounding import semantics are not.

Python 09 Sep 2026 12 min read

Debug Running Asyncio Services with pstree and ps in Python 3.14

An asynchronous service can be alive while making no useful progress. The process still responds to signals. CPU usage may be low. The event loop is still running. Yet a request, worker, or shutdown path appears stuck somewhere inside a chain of coroutines. Traditional stack traces are only part of the answer. An asyncio application is organized around tasks and await relationships, so the useful question is often not merely “where is this thread?” but “which task is waiting for which other task?”

Python 09 Sep 2026 10 min read

Control asyncio Task Startup with eager_start

Creating an asyncio task usually feels like a clean scheduling boundary: call asyncio.create_task(), keep the returned task, and let the event loop run the coroutine soon. Python also supports eager task execution, where a coroutine can begin running immediately during task creation. Python 3.14 makes that choice directly available through the eager_start keyword on asyncio.create_task() and through task-group task creation. That can remove scheduling overhead for coroutines that often complete without blocking. It can also change program ordering in ways that matter much more than the performance gain.

Python 09 Sep 2026 10 min read

Compare Python Syntax Trees with ast.compare

Tools that rewrite Python source often need to answer a deceptively simple question: did this transformation preserve the syntax tree that matters? Before Python 3.14, a common solution was to serialize both trees with ast.dump() and compare the resulting strings. That works in small tests, but it turns a structural question into a formatting contract. Python 3.14 adds ast.compare(), a recursive AST comparison helper that expresses the intent directly. This is especially useful for formatters, codemods, linters, source generators, refactoring tools, and tests that round-trip Python code.

Python 09 Sep 2026 9 min read

Build Reproducible ZIP Archives with SOURCE_DATE_EPOCH in Python 3.14

A build artifact can contain exactly the same application bytes and still produce a different checksum every time it is built. ZIP timestamps are one common reason. That matters when checksums are used for release verification, artifact caching, provenance, binary transparency, or simply deciding whether a build changed. If irrelevant metadata changes on every run, byte-for-byte comparison stops being useful. Python 3.14 makes one important part of this easier: zipfile.ZipFile.writestr() now respects the SOURCE_DATE_EPOCH environment variable. When it is set, string-named entries written with writestr() can use the supplied epoch instead of the current time.

Python 09 Sep 2026 9 min read

Build Friendlier CLIs with Python 3.14 argparse Suggestions and Color

Command-line interfaces have an unusual usability constraint: when something goes wrong, the user is often staring at a terminal with no other interface to guide them. That makes small details matter. A typo in a subcommand should ideally produce a useful correction. Help output should be easy to scan interactively without becoming noisy when captured by scripts or logs. Python 3.14 adds two argparse.ArgumentParser options aimed directly at those details: suggest_on_error and color.

Database 09 Sep 2026 9 min read

Back Up a Live SQLite Database Safely with Python

Copying an SQLite file looks like an obvious backup strategy: find the .db file and copy it somewhere safe. That can be acceptable when the database is definitely idle, but it is the wrong abstraction for a database that may be changing while the copy runs. SQLite provides an Online Backup API specifically for this problem. Python exposes it as sqlite3.Connection.backup(), so an application can copy a live database into another SQLite database while preserving a consistent database snapshot.

Python 09 Sep 2026 12 min read

Attach to Running Python Processes with sys.remote_exec in Python 3.14

A production Python process can be healthy enough to stay alive while still being difficult to understand. Perhaps one thread appears stuck. Memory is growing but the application has no diagnostic endpoint. A profiler was not enabled before startup. Restarting the process would erase the state you need to inspect. Python 3.14 adds a new CPython capability for this situation: sys.remote_exec(). It lets one Python process request that a .py file be executed by another running CPython process. The target executes that file on its main thread at a safe execution point.

Python 09 Sep 2026 11 min read

Attach pdb to Running Python Processes in Python 3.14

A Python service can misbehave without crashing. A worker may loop unexpectedly, a request may remain in an odd state, or a long-running process may hold data that is difficult to reproduce in a development environment. Historically, using pdb in that situation usually required planning ahead. You could add breakpoint() to the code, start the program under the debugger, or restart it with extra instrumentation. Those approaches are useful, but they do not help much when the interesting state already exists inside a running process.

Python 09 Sep 2026 9 min read

Accept Numeric Protocols with Fraction.from_number

A function that accepts a number and a function that parses text are not quite the same API. I keep running into this distinction in configuration code, pricing tools, import pipelines, and small libraries. The caller may already have an int, float, Decimal, or another numeric object. In that case, accepting a string such as "0.25" just because it happens to look numeric can make the boundary less clear than it needs to be.

Python 08 Sep 2026 8 min read

Walk Directory Trees Safely with Python pathlib Path.walk

Python 3.12 added pathlib.Path.walk(), bringing directory-tree traversal directly to Path objects. It fills the same broad role as os.walk(), but keeping traversal and path manipulation in pathlib can make filesystem code easier to read. Walking a tree is deceptively simple, though. Production code needs to decide which subtrees to enter, what to do with unreadable directories, whether symbolic links should be followed, and whether the filesystem may change during traversal.

Python 08 Sep 2026 9 min read

Use Zstandard Compression with Python compression.zstd

Python applications have long had standard-library support for gzip, bzip2, LZMA, and zlib. Python 3.14 adds another important option: Zstandard support through compression.zstd. Zstandard is useful when a system needs a practical balance of compression ratio and throughput. The new module means many applications can read and write .zst data without adding a third-party Python package. But choosing a compression API is not only about calling compress() and decompress(). Production code also needs to think about streaming, memory limits, frame boundaries, dictionaries, compatibility, and untrusted input.

Python 08 Sep 2026 9 min read

Use UUIDv7 for Time-Ordered Identifiers in Python

Python 3.14 added uuid.uuid7(), giving applications a standard-library way to generate UUID version 7 identifiers defined by RFC 9562. UUIDv7 is useful when an application wants a globally shaped 128-bit identifier while also putting creation time near the front of the identifier. That property can make newly generated values naturally cluster by time in systems that sort UUIDs by their binary or canonical value. It is tempting to summarize UUIDv7 as “a sortable UUID.” That is directionally useful but incomplete. The timestamp has millisecond resolution, Python adds a counter for monotonicity within a millisecond, clocks can move, and separate processes do not become a distributed sequence generator merely because they all use UUIDv7.