Python 3.9

3.9 released 5 October 2020, latest 3.9.25.

Python 3.9 reached end of life on 31 October 2025.

What changed

Python 3.9 added several language and standard-library improvements over Python 3.8. Dictionaries gained merge and update operators, so d1 | d2 creates a merged dictionary and d1 |= d2 updates one in place. Built-in collection types became usable directly in type annotations, such as list[str] and dict[str, int], reducing the need to import equivalent names from typing. str gained removeprefix() and removesuffix(), and annotations can carry metadata through typing.Annotated. The parser changed from the older LL(1) implementation to a PEG parser, enabling grammar improvements while retaining broad source compatibility. The standard library added zoneinfo for IANA time-zone data, graphlib.TopologicalSorter for dependency ordering, and native support for Apple Silicon. Python 3.9 also introduced a more predictable annual feature-release cadence.

What staying costs

Python 3.9 has reached end of life, so upstream no longer provides routine bug fixes or security fixes for this line. Remaining on it leaves teams responsible for assessing and mitigating newly discovered interpreter and standard-library vulnerabilities, or for obtaining fixes from a third party. Dependency risk also grows as package maintainers drop 3.9 from their supported test matrices, publish versions requiring a newer interpreter, or stop validating compatibility. This can constrain upgrades to security-sensitive libraries and increase the chance that CI, build tooling, operating-system images, or deployment platforms no longer provide a tested 3.9 runtime. The longer a codebase remains pinned, the more migration work accumulates across dependencies, typing behavior, packaging tools, and infrastructure.

What to do

Plan an upgrade to a currently supported Python line selected for your operating-system, vendor, and dependency requirements. Inventory every runtime environment, including developer machines, CI workers, containers, serverless functions, build images, and embedded Python consumers. Create a clean environment on the target interpreter, upgrade packaging tools, resolve dependency constraints, and use the project test suite to identify incompatibilities. Test production-representative workloads, especially time-zone handling, native extensions, serialization boundaries, subprocess behavior, and deployment packaging. Review code that relies on deprecated APIs or undocumented interpreter behavior, and update type-checker and linter configurations to match the target version. Roll out through CI and non-production environments first, retain a rollback path, and update base images and runtime policies so new deployments cannot silently return to Python 3.9.

We can tell you what moving off Python 3.9 involves.

What it takes to move off Python 3.9 depends on what you built on it — the version you are on, how much depends on it, and how much of the work is mechanical. Leave your email with this version and we can tell you what that looks like for you.

Not sure yet? Get my plan