On this page · 12 sections
- The Python 3.16 release schedule
- What feature freeze means for a test plan
- What is already in Python 3.16
- Removals in Python 3.16
- New deprecations that start warning in 3.16
- C API changes for extension maintainers
- Free-threaded builds: where things stand
- CI matrix for the 3.16 cycle
- Worked example: how many wheels a compiled library ships
- The test plan, step by step
- How eCorpIT can help
- References
Summary. PEP 826 schedules Python 3.16.0 beta 1, the feature freeze, for Tuesday 4 May 2027. Three more betas follow on 25 May, 15 June and 13 July, then release candidates on 27 July and 31 August, and the final release on Tuesday 5 October 2027. Development began on 7 May 2026; alpha 1 shipped on 13 October and alpha 2 on 10 November 2026, the second of seven planned alphas, with alpha 3 due on 15 December. Python 3.16 gets bugfix releases for two years and source-only security fixes until about October 2032. The draft What's New already records 13 removals across 12 standard library modules, including the asyncio event loop policy system and the array module's 'u' type code, and the C API loses 21 global configuration variables plus Py_SetProgramName() and PySys_SetArgv(). Source builds now need the system libmpdec library. One item in the 3.16 notes, the ssl block on compromised eMudhra intermediate certificates, is not a 2027 change: it shipped on 25 October 2026 in 3.15.2 and the other supported branches. Our recommendation: add allow-failure 3.16 and 3.16t CI jobs now, fix removals during the alphas, make the 3.16 job required at beta 1, and publish compiled wheels from the first release candidate.
About this article. It is for Python library maintainers, platform engineers and application teams who want 3.16 support on release day. We checked every date, removal and C API change on 11 November 2026 against PEP 826, the python.org release pages, the 3.15 and 3.16 documentation, the CPython developer guide, the CVE record and the managed-runtime pages of AWS, Microsoft and Google. The 3.16 What's New is a draft that the core team says will change substantially, so re-check it before each milestone. No vendor paid for inclusion. Researched with AI assistance and reviewed by our editorial team.
This guide is about testing 3.16 before it ships. If you are still moving off an older interpreter, start with our Python 3.11 end-of-life runbook, and for the current release see our Python 3.15 upgrade guide.
The Python 3.16 release schedule
PEP 826 uses the 17-month development period defined in PEP 602, which gives one feature release every 12 months. The release manager is Savannah Ostrowski. The dates below are from PEP 826, last modified on 10 November 2026; days are counted from 11 November 2026.
| Milestone | Date | Day | Days away | What it means for you |
|---|---|---|---|---|
| Development begins | 7 May 2026 | Thursday | Past | main accepts 3.16 features |
| 3.16.0a1 | 13 Oct 2026 | Tuesday | Past | First installable preview |
| 3.16.0a2 | 10 Nov 2026 | Tuesday | Released | Start an allow-failure CI job |
| 3.16.0a3 to a7 | 15 Dec 2026; 12 Jan, 9 Feb, 9 Mar, 13 Apr 2027 | Tuesdays | 34 to 153 | Features and removals still landing |
| 3.16.0b1 (feature freeze) | 4 May 2027 | Tuesday | 174 | Make the 3.16 job required |
| 3.16.0b2, b3, b4 | 25 May, 15 Jun, 13 Jul 2027 | Tuesdays | 195 to 244 | Report regressions upstream |
| 3.16.0rc1 | 27 Jul 2027 | Tuesday | 258 | Publish compiled wheels |
| 3.16.0rc2 | 31 Aug 2027 | Tuesday | 293 | Final compatibility pass |
| 3.16.0 final | 5 Oct 2027 | Tuesday | 328 | Release-day support for libraries |
The alpha 2 announcement states the rule that matters most for planning: features can be added until beta 1 on 4 May 2027, and can be modified or removed until the first release candidate on 27 July 2027. In other words, 4 May fixes the list of what can arrive; 27 July fixes the shape of what arrived.
After 5 October 2027, PEP 826 promises bugfix releases about every second month for two years, a final bugfix release around the time of 3.18.0, and then source-only security releases until approximately October 2032. One small mismatch: the CPython developer guide still lists the first 3.16 release as 6 October 2027, in italics, which marks a scheduled date that can move. PEP 826 and both alpha announcements say 5 October.
What feature freeze means for a test plan
Before beta 1, a passing 3.16 job can start failing because a new deprecation or removal lands. After beta 1, no new features arrive, though existing ones can still change until the release candidate. That gives four natural gates.
| Gate | Library maintainers | Application teams |
|---|---|---|
| Alphas (now to 13 Apr 2027) | Run 3.16 as allow-failure; fix removals in your own code; file CPython bugs | Run the test suite once against an alpha to size the work |
| Betas (4 May to 13 Jul 2027) | Make 3.16 required for pure-Python code; publish pre-release wheels for C, C++ or Rust extensions | Track which dependencies support 3.16; fix your own deprecations |
| Release candidates (27 Jul to 31 Aug 2027) | Publish cp316 and cp316t (or abi3t) wheels | Run staging on rc1 with real dependencies |
| Final (5 Oct 2027) | Add the 3.16 trove classifier; announce support | Upgrade production when dependencies and your platform are ready |
The wheel timing follows the 3.15 precedent. In that cycle the release team aimed for no ABI changes after beta 4, encouraged pre-release wheels during the betas, asked maintainers to hold regular production releases until rc1, and declared the ABI frozen from rc1. Expect the same for 3.16.
The application column is deliberately conservative. Python 3.15.0 shipped on 9 October 2026, and on 11 November 2026 it was still listed as a preview runtime on AWS Lambda, Azure Functions and Cloud Run functions. Frameworks lag too: Django 6.2 LTS, due in April 2027, supports Python 3.12 to 3.15 only, as our Django 6.2 LTS guide explains. Plan for a similar gap with 3.16.
What is already in Python 3.16
The draft What's New lists six highlights so far:
- Set operations and Unicode properties in regular expressions.
resupports set difference ([A--B]), intersection ([A&&B]) and union ([A||B]) with nested sets, following Unicode Technical Standard #18, plus property escapes such as\p{Lu}.
- Encrypted Client Hello in the ssl module, when built with OpenSSL 4.0 or later.
- Faster interpreter startup, with less garbage-collection work and faster construction of
list,dict,bytesandbytearrayobjects.
- Faster UUIDs.
uuid4()anduuid7()gain C implementations, about 15 and 30 times faster than the pure Python versions in CPython's own microbenchmarks.
- Build support for tvOS, watchOS and visionOS, outside the supported platform tiers of PEP 11.
- Better error messages, for example suggesting
matchforswitch x:andandororfor&&and||.
Two areas deserve a test, not just a read.
Regular expressions. Inside square brackets, --, && and || are now set operators, and a [ straight after one of them opens a nested set. A [ anywhere else, including at the start of a set, keeps its old meaning, and a literal ~~ still warns because symmetric difference is not supported yet. CPython has raised a FutureWarning for these ambiguous sequences since 3.7, so run your suite on 3.15 with warnings visible and escape anything it reports with a backslash.
TLS certificate chains. The 3.16 notes record that ssl now rejects chains containing any of a set of compromised eMudhra intermediate CA certificates. They are matched by public key, so cross-signed copies are caught, and the block applies even when an application supplies its own CA bundle. This is CVE-2026-115557, which the PSF rates 9.4 under CVSS 4.0, and it is not waiting for 2027: the same block shipped on 25 October 2026 in expedited security releases 3.15.2, 3.14.10, 3.13.18, 3.12.16 and 3.11.18. The PSF's advisory also tells users to upgrade pip to 26.2.2 or later and certifi to 2026.10.19, because the emSign root was removed from certifi. eMudhra is a certifying authority licensed by India's Controller of Certifying Authorities. If any partner endpoint you call chains to an emSign certificate, test it on a patched interpreter now.
Removals in Python 3.16
The 3.15 list of items pending removal in 3.16 was the best predictor of what breaks, and the 3.16 draft now confirms most of it. Apart from the ElementTree item, which never worked, everything below was deprecated in 3.13 or 3.14 or earlier.
| Module | Removed in 3.16 | Deprecated since | Replacement |
|---|---|---|---|
| annotationlib | ForwardRef._evaluate() | 3.14 | ForwardRef.evaluate() or typing.evaluate_forward_ref() |
| array | 'u' type code (wchar_t) | 3.13 at runtime | 'w' (Py_UCS4, always 4 bytes) |
| asyncio | iscoroutinefunction() | 3.14 | inspect.iscoroutinefunction() |
| asyncio | Event loop policy system: AbstractEventLoopPolicy, DefaultEventLoopPolicy, the two Windows policies, get_event_loop_policy(), set_event_loop_policy() | 3.14 | asyncio.run() or asyncio.Runner with loop_factory |
| functools | Keyword arguments to the Python reduce() | 3.14 | Positional arguments only |
| logging | Custom handlers using the strm argument | 3.14 | stream argument |
| mimetypes | Undotted extensions in MimeTypes.add_type() | 3.14 | Extensions starting with a dot (or empty) |
| shutil | ExecError | 3.14 | RuntimeError |
| symtable | Class.get_methods() | 3.14 | None listed |
| sys | _enablelegacywindowsfsencoding() | 3.13 | PYTHONLEGACYWINDOWSFSENCODING |
| sysconfig | expand_makefile_vars() | 3.14 | vars argument of get_paths() |
| tarfile | TarFile.tarfile attribute | 3.13 | None needed |
| xml.etree.ElementTree | write_c14n() and the c14n write method | Never worked | canonicalize() |
The asyncio policy removal is the one most likely to hit applications. Code and test fixtures that swap in another event loop through set_event_loop_policy() must move to the loop_factory pattern the documentation gives:
import asyncio
async def main():
...
# Replaces asyncio.set_event_loop_policy(...) followed by asyncio.run(main())
asyncio.run(main(), loop_factory=asyncio.SelectorEventLoop)
Two items from the 3.15 pending list are not recorded as removed in the 3.16 draft, and neither appears on the 3.16 deprecations page: the import system ceasing to use __loader__ when __spec__.loader is not set, and the removal of bitwise inversion on booleans (~True). Treat both as still possible before beta 1 and fix them now: set __spec__.loader in custom importers, and use not x instead of ~x on booleans.
There is also a build change. CPython no longer bundles libmpdec, so decimal always uses the system library, and the --with-system-libmpdec flag is gone. Binary releases are unaffected, but custom container images and other from-source builds need the system library installed.
Behaviour changes in the porting notes
tkinter: thenameargument ofwait_variable(),setvar()andgetvar(), andvalueforsetvar(), are now required.
uuid.UUIDnow raisesTypeErrorwhenbytesorbytes_leis not abytesobject.
xml.dom.minidomraises DOM errors for invalid names, foreign nodes and invalid hierarchies that previously produced broken documents, and stops raisingNotFoundErrwhen removing a missing attribute.
toprettyxml()andElementTree.indent()no longer add whitespace inside elements that contain text or setxml:spaceto preserve. Snapshot tests of generated XML will change.
- On Windows, seeking a pipe now raises
OSError, and opening a pipe as'r+b'or'w+b'raisesio.UnsupportedOperationunless buffering is disabled.
All five come from the porting section of the draft notes.
New deprecations that start warning in 3.16
These do not break code in October 2027, but they emit DeprecationWarning, which fails any suite that treats warnings as errors. The list is from the draft What's New.
| Deprecated in 3.16 | Removal or change | Use instead |
|---|---|---|
| abc.abstractclassmethod, abstractstaticmethod, abstractproperty | Removed in 3.21 | abc.abstractmethod with classmethod, staticmethod, property |
| ast.slice, Index, ExtSlice, Suite, Param, AugLoad, AugStore; ast.Tuple.dims | Removed in 3.21 | ast.Tuple.elts for dims |
| Dunder attributes for author, credits, copyright and date in 13 stdlib modules | Removed in 3.20 | Do not read them |
| Return annotation as dispatch type in functools.singledispatch | TypeError in 3.21 | Annotate the parameter or pass the type |
| struct type codes 'F' and 'D' | Removed in 3.21 | 'Zf' and 'Zd' |
| tempfile._TemporaryFileWrapper | Removed in 3.21 | tempfile.TemporaryFileWrapper |
| tkinter.filedialog.askopenfiles() | Removed in 3.19 | askopenfilenames() and open each file |
| typing.AnyStr | Removed in 3.21 | PEP 695 type parameter syntax |
| False non-string values (such as 0 or []) in urllib.parse functions | TypeError in 3.18 | Pass strings, bytes or None |
typing.AnyStr is likely to be the most visible item for library authors, because many typed libraries still use it in public signatures.
C API changes for extension maintainers
Compiled extensions decide most upgrade calendars, because every cp316 wheel has to exist before an application can move. The draft notes list these removals:
- 21 global configuration variables deprecated in 3.12, including
Py_DebugFlag,Py_VerboseFlag,Py_OptimizeFlag,Py_NoSiteFlag,Py_IgnoreEnvironmentFlag,Py_DontWriteBytecodeFlag,Py_IsolatedFlagandPy_UTF8Mode. Use the PyInitConfig API to set them andPyConfig_Get()to read them, with names such asverbose,optimization_levelorsite_import; several have the opposite value.
PyEval_InitThreads(), which has done nothing since 3.7.
Py_SetProgramName()andPy_SetPythonHome(), replaced by theprogram_nameandhomePyInitConfig options.
PySys_SetArgv()andPySys_SetArgvEx(); setsys.argv(andsys.pathif needed) directly.
- The
PyUnstable_ExecutableKindsarray and its related macros.
Embedders, meaning applications that start an interpreter from C or C++, are the group most affected by the configuration removals. Ordinary extension modules mostly feel the deprecations:
PyGen_New(),PyGen_NewWithQualName(),PyCoro_New()andPyAsyncGen_New()are deprecated, with removal scheduled for 3.18.
PyInitConfig_GetExitCode(),PyStatus_Exit(),PyStatus_IsExit()andPyStatus.exitcodeare deprecated; command-line exits now happen inPy_RunMain().
- Functions that modify Unicode strings in place, such as
PyUnicode_New(),PyUnicode_Fill()andPyUnicode_WRITE(), are soft-deprecated in favour of the PyUnicodeWriter API, which 3.16 adds to the limited C API along withPy_HashBuffer(),Py_SETREFandPy_XSETREF.
PyType_ClearCache()is now a no-op, and the marshal functions that read from aFILE*raiseOSErrorinstead ofEOFErroron I/O errors.
Free-threaded builds: where things stand
The free-threaded build became officially supported, though not the default, in Python 3.14 under PEP 779. Python 3.15 then made two changes that shape a 3.16 test plan:
- A stable ABI for free-threaded builds. PEP 803 introduced
abi3t, which covers free-threaded interpreters without a per-version wheel and is also compatible with GIL-enabled builds. Getting there usually means real source changes: adopting the PEP 697 APIs from Python 3.12 instead of embeddingPyObjectin the instance struct, and moving fromPyInit_to the newPyModExport_*hook (PEP 793) with thePySlotstructure (PEP 820). The 3.15 notes name meson-python, scikit-build-core and Maturin as build tools withabi3tsupport.
- Wider availability. The official macOS binaries for 3.15 install free-threading support by default.
The 3.16 draft so far adds one free-threading item: iOS builds can now be configured as free-threaded. That may change before beta 1.
For a test plan, the practical steps are:
- Add a
3.16tjob. If you ship compiled code, check that importing your extension does not quietly turn the GIL back on; CPython enables it, with a warning, when an extension is not marked as free-threading safe.
- Run thread-heavy tests with
PYTHON_GIL=0, which forces the GIL off on a free-threaded build, to expose races.
- Decide between
abi3tand per-versioncp316twheels before the release candidates, because changing the module initialisation hook is not a last-week job.
# On a free-threaded interpreter
python -VV
python -c "import sys; print('GIL enabled:', sys._is_gil_enabled())"
PYTHON_GIL=0 python -m pytest -x tests/test_concurrency.py
CI matrix for the 3.16 cycle
GitHub's actions/setup-python (v7 on 11 November 2026) accepts a t suffix for free-threaded versions and an x.y-dev form that installs the latest alpha, beta or release candidate, including 3.14t-dev style names. Its version manifest already carried 3.16.0 alpha 1 builds, free-threaded ones included, on 11 November 2026. A matrix that keeps current releases required and 3.16 as allow-failure until beta 1:
# .github/workflows/test.yml (excerpt)
jobs:
test:
runs-on: ubuntu-24.04
continue-on-error: ${{ matrix.experimental }}
strategy:
fail-fast: false
matrix:
python-version: ["3.13", "3.14", "3.15"]
experimental: [false]
include:
- python-version: "3.15t"
experimental: false
- python-version: "3.16-dev"
experimental: true # set to false at 3.16.0b1 (4 May 2027)
- python-version: "3.16t-dev"
experimental: true
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7
with:
python-version: ${{ matrix.python-version }}
- run: python -m pip install -e ".[test]"
- run: python -X dev -m pytest
Pair it with warning filters in pyproject.toml, so your own deprecations fail the build and third-party ones are tracked without blocking it:
[tool.pytest.ini_options]
filterwarnings = [
"error::DeprecationWarning",
"error::FutureWarning",
"ignore::DeprecationWarning:some_dependency.*",
]
Three habits keep the matrix honest. Pin the runner image rather than using ubuntu-latest. Keep the free-threaded job for the newest stable release required, because users who chose free-threading expect it to keep working. And once 3.16.0 ships, raise your floor in line with your support policy: projects following Scientific Python's SPEC 0 drop a Python version three years after its release, which puts 3.13 on the way out in the fourth quarter of 2027, around the 3.16 launch.
Worked example: how many wheels a compiled library ships
Take a library with a C extension that supports CPython 3.12 to 3.16 on four platforms: Linux x86_64, Linux aarch64, macOS arm64 and Windows x86_64. It supports free-threaded builds from 3.14, the release where they became officially supported. The build combinations follow the abi3t migration guide.
| Strategy | Builds per platform | Arithmetic | Wheels per release |
|---|---|---|---|
| Version-specific wheels only | 5 GIL versions (3.12 to 3.16) + 3 free-threaded (3.14t to 3.16t) | 4 x (5 + 3) | 32 |
| abi3 for GIL builds, version-specific free-threaded | 1 abi3 + 3 free-threaded | 4 x (1 + 3) | 16 |
| abi3 + cp314t + abi3t (3.15 onwards) | 1 + 1 + 1 | 4 x 3 | 12 |
The third row assumes the extension can meet both stable ABIs, which is not true for code that needs non-limited APIs. The trend is the point: without a stable ABI, each new Python release adds two wheels per platform, so 3.16 alone adds 8 builds in this example (4 x 2). With abi3 and abi3t, 3.16 adds none, but you still test on it.
The calendar arithmetic matters as much. From beta 1 (4 May) to the first release candidate (27 July) is 84 days; from beta 1 to the final release (5 October) is 154 days. A library that waits for the final release before building wheels will not have them on release day, and every downstream application waits with it.
The test plan, step by step
- Now (alpha 2 onwards). Add allow-failure
3.16and3.16tjobs. Install locally with the python.org installers; on Windows the release page says to use the Python install manager withpy install 3.16. Separately, move current interpreters to 3.15.2, 3.14.10 or the matching security release and pip to 26.2.2 or later.
- December to April. Fix every removal in the table above. Search for
set_event_loop_policy,asyncio.iscoroutinefunction,array('u',strm=,ExecError,write_c14nand~applied to booleans.
- Before 4 May 2027. Clear your own
DeprecationWarningandFutureWarningoutput on 3.15, including regex set-operation warnings andtyping.AnyStr.
- At beta 1. Make the 3.16 job required for pure-Python code. Read the updated What's New again, because late features land just before the freeze.
- During the betas. For extensions, fix C API removals, decide on
abi3t, and publish pre-release wheels. Report CPython regressions while they can still be fixed.
- At rc1 (27 July 2027). Publish final
cp316and free-threaded wheels. Applications run staging on rc1.
- At 3.16.0 (5 October 2027). Libraries add the 3.16 classifier and release. Applications upgrade production only when every dependency and the managed runtime support 3.16. If you are weighing outside help for this, our note on what to check in a Python development company lists the questions to ask.
How eCorpIT can help
eCorpIT is a Gurugram-based technology organisation, founded in 2021, assessed at CMMI Level 5 and MSME certified, with senior-led engineering teams working across AWS, Microsoft and Google platforms. Our application development services team runs Python version programmes end to end, from CI matrices across GIL and free-threaded builds to C extension porting, wheel pipelines and staged rollouts on managed runtimes. Talk to us at /contact-us/.
Last updated: 11 November 2026.