Skip to content

Latest commit

 

History

History
2102 lines (1520 loc) · 77.6 KB

File metadata and controls

2102 lines (1520 loc) · 77.6 KB

What's new in Python 3.15

Editor:Hugo van Kemenade

This article explains the new features in Python 3.15, compared to 3.14.

For full details, see the :ref:`changelog <changelog>`.

Note

Prerelease users should be aware that this document is currently in draft form. It will be updated substantially as Python 3.15 moves towards release, so it's worth checking back even after reading earlier versions.

Summary -- Release highlights

New features

PEP 810: Explicit lazy imports

Large Python applications often suffer from slow startup times. A significant contributor to this problem is the import system: when a module is imported, Python must locate the file, read it from disk, compile it to bytecode, and execute all top-level code. For applications with deep dependency trees, this process can take seconds, even when most of the imported code is never actually used during a particular run.

Developers have worked around this by moving imports inside functions, using :mod:`importlib` to load modules on demand, or restructuring code to avoid unnecessary dependencies. These approaches work but make code harder to read and maintain, scatter import statements throughout the codebase, and require discipline to apply consistently.

Python now provides a cleaner solution through explicit :keyword:`lazy` imports using the new lazy soft keyword. When you mark an import as lazy, Python defers the actual module loading until the imported name is first used. This gives you the organizational benefits of declaring all imports at the top of the file while only paying the loading cost for modules you actually use.

The lazy keyword works with both import and from ... import statements. When you write lazy import heavy_module, Python does not immediately load the module. Instead, it creates a lightweight proxy object. The actual module loading happens transparently when you first access the name:

lazy import json
lazy from pathlib import Path

print("Starting up...")  # json and pathlib not loaded yet

data = json.loads('{"key": "value"}')  # json loads here
p = Path(".")  # pathlib loads here

This mechanism is particularly useful for applications that import many modules at the top level but may only use a subset of them in any given run. The deferred loading reduces startup latency without requiring code restructuring or conditional imports scattered throughout the codebase.

In the case where loading a lazily imported module fails (for example, if the module does not exist), Python raises the exception at the point of first use rather than at import time. The associated traceback includes both the location where the name was accessed and the original import statement, making it straightforward to diagnose & debug the failure.

For cases where you want to enable lazy loading globally without modifying source code, Python provides the :option:`-X lazy_imports <-X>` command-line option and the :envvar:`PYTHON_LAZY_IMPORTS` environment variable. Both accept three values: all makes all imports lazy by default, none disables lazy imports entirely (even explicit lazy statements become eager), and normal (the default) respects the lazy keyword in source code. The :func:`sys.set_lazy_imports` and :func:`sys.get_lazy_imports` functions allow changing and querying this mode at runtime.

For more selective control, :func:`sys.set_lazy_imports_filter` accepts a callable that determines whether a specific module should be loaded lazily. The filter receives three arguments: the importing module's name (or None), the imported module's name, and the fromlist (or None for regular imports). It should return True to allow the import to be lazy, or False to force eager loading. This allows patterns like making only your own application's modules lazy while keeping third-party dependencies eager:

import sys

def myapp_filter(importing, imported, fromlist):
    return imported.startswith("myapp.")
sys.set_lazy_imports_filter(myapp_filter)
sys.set_lazy_imports("all")

import myapp.slow_module  # lazy (matches filter)
import json               # eager (does not match filter)

The proxy type itself is available as :data:`types.LazyImportType` for code that needs to detect lazy imports programmatically.

There are some restrictions on where the lazy keyword can be used. Lazy imports are only permitted at module scope; using lazy inside a function, class body, or try/except/finally block raises a :exc:`SyntaxError`. Neither star imports nor future imports can be lazy (lazy from module import * and lazy from __future__ import ... both raise :exc:`SyntaxError`).

.. seealso:: :pep:`810` for the full specification and rationale.

(Contributed by Pablo Galindo Salgado and Dino Viehland in :gh:`142349`.)

PEP 814: Add frozendict built-in type

A new :term:`immutable` type, :class:`frozendict`, is added to the :mod:`builtins` module. It does not allow modification after creation. A :class:`!frozendict` is not a subclass of dict; it inherits directly from object. A :class:`!frozendict` is :term:`hashable` as long as all of its keys and values are hashable. A :class:`!frozendict` preserves insertion order, but comparison does not take order into account.

For example:

>>> a = frozendict(x=1, y=2)
>>> a
frozendict({'x': 1, 'y': 2})
>>> a['z'] = 3
Traceback (most recent call last):
  File "<python-input-2>", line 1, in <module>
    a['z'] = 3
    ~^^^^^
TypeError: 'frozendict' object does not support item assignment
>>> b = frozendict(y=2, x=1)
>>> hash(a) == hash(b)
True
>>> a == b
True

The following standard library modules have been updated to accept :class:`!frozendict`: :mod:`copy`, :mod:`decimal`, :mod:`json`, :mod:`marshal`, :mod:`plistlib` (only for serialization), :mod:`pickle`, :mod:`pprint` and :mod:`xml.etree.ElementTree`.

:func:`eval` and :func:`exec` accept :class:`!frozendict` for globals, and :func:`type` and :meth:`str.maketrans` accept :class:`!frozendict` for dict.

Code checking for :class:`dict` type using isinstance(arg, dict) can be updated to isinstance(arg, (dict, frozendict)) to accept also the :class:`!frozendict` type, or to isinstance(arg, collections.abc.Mapping) to accept also other mapping types such as :class:`~types.MappingProxyType`.

.. seealso:: :pep:`814` for the full specification and rationale.

(Contributed by Victor Stinner and Donghee Na in :gh:`141510`.)

PEP 799: A dedicated profiling package

A new :mod:`profiling` module has been added to organize Python's built-in profiling tools under a single, coherent namespace. This module contains:

The cProfile module remains as an alias for backwards compatibility. The :mod:`profile` module is deprecated and will be removed in Python 3.17.

.. seealso:: :pep:`799` for further details.

(Contributed by Pablo Galindo and László Kiss Kollár in :gh:`138122`.)

Tachyon: High frequency statistical sampling profiler

Tachyon profiler logo

A new statistical sampling profiler (Tachyon) has been added as :mod:`profiling.sampling`. This profiler enables low-overhead performance analysis of running Python processes without requiring code modification or process restart.

Unlike deterministic profilers (such as :mod:`profiling.tracing`) that instrument every function call, the sampling profiler periodically captures stack traces from running processes. This approach provides virtually zero overhead while achieving sampling rates of up to 1,000,000 Hz, making it the fastest sampling profiler available for Python (at the time of its contribution) and ideal for debugging performance issues in production environments. This capability is particularly valuable for debugging performance issues in production systems where traditional profiling approaches would be too intrusive.

Key features include:

  • Zero-overhead profiling: Attach to any running Python process without affecting its performance. Ideal for production debugging where you can't afford to restart or slow down your application.
  • No code modification required: Profile existing applications without restart. Simply point the profiler at a running process by PID and start collecting data.
  • Flexible target modes:
    • Profile running processes by PID (attach) - attach to already-running applications
    • Run and profile scripts directly (run) - profile from the very start of execution
    • Execute and profile modules (run -m) - profile packages run as python -m module
  • Multiple profiling modes: Choose what to measure based on your performance investigation:
    • Wall-clock time (--mode wall, default): Measures real elapsed time including I/O, network waits, and blocking operations. Use this to understand where your program spends calendar time, including when waiting for external resources.
    • CPU time (--mode cpu): Measures only active CPU execution time, excluding I/O waits and blocking. Use this to identify CPU-bound bottlenecks and optimize computational work.
    • GIL-holding time (--mode gil): Measures time spent holding Python's Global Interpreter Lock. Use this to identify which threads dominate GIL usage in multi-threaded applications.
    • Exception handling time (--mode exception): Captures samples only from threads with an active exception. Use this to analyze exception handling overhead.
  • Thread-aware profiling: Option to profile all threads (-a) or just the main thread, essential for understanding multi-threaded application behavior.
  • Multiple output formats: Choose the visualization that best fits your workflow:
    • --pstats: Detailed tabular statistics compatible with :mod:`pstats`. Shows function-level timing with direct and cumulative samples. Best for detailed analysis and integration with existing Python profiling tools.
    • --collapsed: Generates collapsed stack traces (one line per stack). This format is specifically designed for creating flame graphs with external tools like Brendan Gregg's FlameGraph scripts or speedscope.
    • --flamegraph: Generates a self-contained interactive HTML flame graph using D3.js. Opens directly in your browser for immediate visual analysis. Flame graphs show the call hierarchy where width represents time spent, making it easy to spot bottlenecks at a glance.
    • --gecko: Generates Gecko Profiler format compatible with Firefox Profiler. Upload the output to Firefox Profiler for advanced timeline-based analysis with features like stack charts, markers, and network activity.
    • --heatmap: Generates an interactive HTML heatmap visualization with line-level sample counts. Creates a directory with per-file heatmaps showing exactly where time is spent at the source code level.
  • Live interactive mode: Real-time TUI profiler with a top-like interface (--live). Monitor performance as your application runs with interactive sorting and filtering.
  • Async-aware profiling: Profile async/await code with task-based stack reconstruction (--async-aware). See which coroutines are consuming time, with options to show only running tasks or all tasks including those waiting.
  • Opcode-level profiling: Gather bytecode opcode information for instruction-level profiling (--opcodes). Shows which bytecode instructions are executing, including specializations from the adaptive interpreter.

See :mod:`profiling.sampling` for the complete documentation, including all available output formats, profiling modes, and configuration options.

(Contributed by Pablo Galindo and László Kiss Kollár in :gh:`135953` and :gh:`138122`.)

PEP 798: Unpacking in Comprehensions

List, set, and dictionary comprehensions, as well as generator expressions, now support unpacking with * and **. This extends the unpacking syntax from PEP 448 to comprehensions, providing a new syntax for combining an arbitrary number of iterables or dictionaries into a single flat structure. This new syntax is a direct alternative to nested comprehensions, :func:`itertools.chain`, and :meth:`itertools.chain.from_iterable`. For example:

>>> lists = [[1, 2], [3, 4], [5]]
>>> [*L for L in lists]  # equivalent to [x for L in lists for x in L]
[1, 2, 3, 4, 5]

>>> sets = [{1, 2}, {2, 3}, {3, 4}]
>>> {*s for s in sets}  # equivalent to {x for s in sets for x in s}
{1, 2, 3, 4}

>>> dicts = [{'a': 1}, {'b': 2}, {'a': 3}]
>>> {**d for d in dicts}  # equivalent to {k: v for d in dicts for k,v in d.items()}
{'a': 3, 'b': 2}

Generator expressions can similarly use unpacking to yield values from multiple iterables:

>>> gen = (*L for L in lists)  # equivalent to (x for L in lists for x in L)
>>> list(gen)
[1, 2, 3, 4, 5]

This change also extends to asynchronous generator expressions, such that, for example, (*a async for a in agen()) is equivalent to (x async for a in agen() for x in a).

.. seealso:: :pep:`798` for further details.

(Contributed by Adam Hartz in :gh:`143055`.)

Improved error messages

  • The interpreter now provides more helpful suggestions in :exc:`AttributeError` exceptions when accessing an attribute on an object that does not exist, but a similar attribute is available through one of its members.

    For example, if the object has an attribute that itself exposes the requested name, the error message will suggest accessing it via that inner attribute:

    @dataclass
    class Circle:
       radius: float
    
       @property
       def area(self) -> float:
          return pi * self.radius**2
    
    class Container:
       def __init__(self, inner: Circle) -> None:
          self.inner = inner
    
    circle = Circle(radius=4.0)
    container = Container(circle)
    print(container.area)

    Running this code now produces a clearer suggestion:

    Traceback (most recent call last):
    File "/home/pablogsal/github/python/main/lel.py", line 42, in <module>
       print(container.area)
             ^^^^^^^^^^^^^^
    AttributeError: 'Container' object has no attribute 'area'. Did you mean '.inner.area' instead of '.area'?

Other language changes

  • Python now uses UTF-8 as the default encoding, independent of the system's environment. This means that I/O operations without an explicit encoding, for example, open('flying-circus.txt'), will use UTF-8. UTF-8 is a widely-supported Unicode character encoding that has become a de facto standard for representing text, including nearly every webpage on the internet, many common file formats, programming languages, and more.

    This only applies when no encoding argument is given. For best compatibility between versions of Python, ensure that an explicit encoding argument is always provided. The :ref:`opt-in encoding warning <io-encoding-warning>` can be used to identify code that may be affected by this change. The special encoding='locale' argument uses the current locale encoding, and has been supported since Python 3.10.

    To retain the previous behaviour, Python's UTF-8 mode may be disabled with the :envvar:`PYTHONUTF8=0 <PYTHONUTF8>` environment variable or the :option:`-X utf8=0 <-X>` command-line option.

    .. seealso:: :pep:`686` for further details.
    
    

    (Contributed by Adam Turner in :gh:`133711`; PEP 686 written by Inada Naoki.)

  • Several error messages incorrectly using the term "argument" have been corrected. (Contributed by Stan Ulbrych in :gh:`133382`.)

  • The interpreter now tries to provide a suggestion when :func:`delattr` fails due to a missing attribute. When an attribute name that closely resembles an existing attribute is used, the interpreter will suggest the correct attribute name in the error message. For example:

    >>> class A:
    ...     pass
    >>> a = A()
    >>> a.abcde = 1
    >>> del a.abcdf  # doctest: +ELLIPSIS
    Traceback (most recent call last):
    ...
    AttributeError: 'A' object has no attribute 'abcdf'. Did you mean: 'abcde'?

    (Contributed by Nikita Sobolev and Pranjal Prajapati in :gh:`136588`.)

  • Unraisable exceptions are now highlighted with color by default. This can be controlled by :ref:`environment variables <using-on-controlling-color>`. (Contributed by Peter Bierma in :gh:`134170`.)

  • The :meth:`~object.__repr__` of :class:`ImportError` and :class:`ModuleNotFoundError` now shows "name" and "path" as name=<name> and path=<path> if they were given as keyword arguments at construction time. (Contributed by Serhiy Storchaka, Oleg Iarygin, and Yoav Nir in :gh:`74185`.)

  • The :attr:`~object.__dict__` and :attr:`!__weakref__` descriptors now use a single descriptor instance per interpreter, shared across all types that need them. This speeds up class creation, and helps avoid reference cycles. (Contributed by Petr Viktorin in :gh:`135228`.)

  • The :option:`-W` option and the :envvar:`PYTHONWARNINGS` environment variable can now specify regular expressions instead of literal strings to match the warning message and the module name, if the corresponding field starts and ends with a forward slash (/). (Contributed by Serhiy Storchaka in :gh:`134716`.)

  • Functions that take timestamp or timeout arguments now accept any real numbers (such as :class:`~decimal.Decimal` and :class:`~fractions.Fraction`), not only integers or floats, although this does not improve precision. (Contributed by Serhiy Storchaka in :gh:`67795`.)

New modules

math.integer

This module provides access to the mathematical functions for integer arguments (PEP 791). (Contributed by Serhiy Storchaka in :gh:`81313`.)

Improved modules

argparse

  • The :class:`~argparse.BooleanOptionalAction` action supports now single-dash long options and alternate prefix characters. (Contributed by Serhiy Storchaka in :gh:`138525`.)
  • Changed the suggest_on_error parameter of :class:`argparse.ArgumentParser` to default to True. This enables suggestions for mistyped arguments by default. (Contributed by Jakob Schluse in :gh:`140450`.)
  • Added backtick markup support in description and epilog text to highlight inline code when color output is enabled. (Contributed by Savannah Ostrowski in :gh:`142390`.)

array

base64

binascii

calendar

collections

concurrent.futures

contextlib

dataclasses

  • Annotations for generated __init__ methods no longer include internal type names.

dbm

difflib

functools

hashlib

  • Ensure that hash functions guaranteed to be always available exist as attributes of :mod:`hashlib` even if they will not work at runtime due to missing backend implementations. For instance, hashlib.md5 will no longer raise :exc:`AttributeError` if OpenSSL is not available and Python has been built without MD5 support. (Contributed by Bénédikt Tran in :gh:`136929`.)

http.client

http.cookies

  • Allow '"' double quotes in cookie values. (Contributed by Nick Burns and Senthil Kumaran in :gh:`92936`.)

http.server

inspect

json

  • Add the array_hook parameter to :func:`~json.load` and :func:`~json.loads` functions: allow a callback for JSON literal array types to customize Python lists in the resulting decoded object. Passing combined :class:`frozendict` to object_pairs_hook param and :class:`tuple` to array_hook will yield a deeply nested immutable Python structure representing the JSON data. (Contributed by Joao S. O. Bueno in :gh:`146440`)

locale

math

mimetypes

  • Add application/dicom MIME type for .dcm extension. (Contributed by Benedikt Johannes in :gh:`144217`.)

  • Add application/efi. (Contributed by Charlie Lin in :gh:`145720`.)

  • Add application/node MIME type for .cjs extension. (Contributed by John Franey in :gh:`140937`.)

  • Add application/toml. (Contributed by Gil Forcada in :gh:`139959`.)

  • Add application/sql and application/vnd.sqlite3. (Contributed by Charlie Lin in :gh:`145698`.)

  • Add the following MIME types:

    • application/vnd.ms-cab-compressed for .cab extension
    • application/vnd.ms-htmlhelp for .chm extension
    • application/vnd.ms-officetheme for .thmx extension

    (Contributed by Charlie Lin in :gh:`145718`.)

  • Add image/jxl. (Contributed by Foolbar in :gh:`144213`.)

  • Rename application/x-texinfo to application/texinfo. (Contributed by Charlie Lin in :gh:`140165`.)

  • Changed the MIME type for .ai files to application/pdf. (Contributed by Stan Ulbrych in :gh:`141239`.)

mmap

os

  • Add :func:`os.statx` on Linux kernel versions 4.11 and later with glibc versions 2.28 and later. (Contributed by Jeffrey Bosboom and Victor Stinner in :gh:`83714`.)

os.path

pickle

  • Add support for pickling private methods and nested classes. (Contributed by Zackery Spytz and Serhiy Storchaka in :gh:`77188`.)

re

resource

shelve

socket

  • Add constants for the ISO-TP CAN protocol. (Contributed by Patrick Menschel and Stefan Tatschner in :gh:`86819`.)

sqlite3

ssl

subprocess

  • :meth:`subprocess.Popen.wait`: when timeout is not None and the platform supports it, an efficient event-driven mechanism is used to wait for process termination:

    If none of these mechanisms are available, the function falls back to the traditional busy loop (non-blocking call and short sleeps). (Contributed by Giampaolo Rodola in :gh:`83069`).

symtable

sys

tarfile

timeit

tkinter

tomllib

  • The :mod:`tomllib` module now supports TOML 1.1.0. This is a backwards compatible update, meaning that all valid TOML 1.0.0 documents are parsed the same way.

    The changes, according to the official TOML changelog, are:

    • Allow newlines and trailing commas in inline tables.

      Previously an inline table had to be on a single line and couldn't end with a trailing comma. This is now relaxed so that the following is valid:

      tbl = {
         key      = "a string",
         moar-tbl =  {
            key = 1,
         },
      }
      
    • Add \xHH notation to basic strings for codepoints under 255, and the \e escape for the escape character:

      null = "null byte: \x00; letter a: \x61"
      csi = "\e["
      
    • Seconds in datetime and time values are now optional. The following are now valid:

      dt = 2010-02-03 14:15
      t  = 14:15
      

    (Contributed by Taneli Hukkinen in :gh:`142956`.)

types

typing

  • PEP 747: Add :data:`~typing.TypeForm`, a new special form for annotating values that are themselves type expressions. TypeForm[T] means "a type form object describing T (or a type assignable to T)". At runtime, TypeForm(x) simply returns x, which allows explicit annotation of type-form values without changing behavior.

    This helps libraries that accept user-provided type expressions (for example int, str | None, :class:`~typing.TypedDict` classes, or list[int]) expose precise signatures:

    from typing import Any, TypeForm
    
    def cast[T](typ: TypeForm[T], value: Any) -> T: ...

    (Contributed by Jelle Zijlstra in :gh:`145033`.)

  • Code like class ExtraTypeVars(P1[S], Protocol[T, T2]): ... now raises a :exc:`TypeError`, because S is not listed in Protocol parameters. (Contributed by Nikita Sobolev in :gh:`137191`.)

  • Code like class B2(A[T2], Protocol[T1, T2]): ... now correctly handles type parameters order: it is (T1, T2), not (T2, T1) as it was incorrectly inferred in runtime before. (Contributed by Nikita Sobolev in :gh:`137191`.)

traceback

  • Add new show_source_lines and recent_first keyword only arguments to the :mod:`traceback` functions.

    The show_source_lines argument controls whether source code lines are displayed. It is default to True.

    The recent_first argument controls whether the most recent frames are displayed first or last in the traceback. It affects whether the exception is displayed at the top or bottom of the traceback. It is default to False. (Contributed by Inada Naoki in :gh:`135751`)

unicodedata

unittest

urllib.parse

venv

  • On POSIX platforms, platlib directories will be created if needed when creating virtual environments, instead of using lib64 -> lib symlink. This means purelib and platlib of virtual environments no longer share the same lib directory on platforms where :data:`sys.platlibdir` is not equal to lib. (Contributed by Rui Xi in :gh:`133951`.)

warnings

  • Improve filtering by module in :func:`warnings.warn_explicit` if no module argument is passed. It now tests the module regular expression in the warnings filter not only against the filename with .py stripped, but also against module names constructed starting from different parent directories of the filename (with /__init__.py, .py and, on Windows, .pyw stripped). (Contributed by Serhiy Storchaka in :gh:`135801`.)

wave

(Contributed by Lionel Koenig and Michiel W. Beijen in :gh:`60729`.)

xml.parsers.expat

zlib

Optimizations

base64 & binascii

  • CPython's underlying base64 implementation now encodes 2x faster and decodes 3x faster thanks to simple CPU pipelining optimizations. (Contributed by Gregory P. Smith and Serhiy Storchaka in :gh:`143262`.)
  • Implementation for Ascii85, Base85, and Z85 encoding has been rewritten in C. Encoding and decoding is now two orders of magnitude faster and consumes two orders of magnitude less memory. (Contributed by James Seo and Serhiy Storchaka in :gh:`101178`.)
  • Implementation for Base32 has been rewritten in C. Encoding and decoding is now two orders of magnitude faster. (Contributed by James Seo in :gh:`146192`)

csv

Upgraded JIT compiler

Results from the pyperformance benchmark suite report 6-7% geometric mean performance improvement for the JIT over the standard CPython interpreter built with all optimizations enabled on x86-64 Linux. On AArch64 macOS, the JIT has a 12-13% speedup over the :ref:`tail calling interpreter <whatsnew314-tail-call-interpreter>` with all optimizations enabled. The speedups for JIT builds versus no JIT builds range from roughly 15% slowdown to over 100% speedup (ignoring the unpack_sequence microbenchmark) on x86-64 Linux and AArch64 macOS systems.

Attention!

These results are not yet final.

The major upgrades to the JIT are:

  • LLVM 21 build-time dependency
  • New tracing frontend
  • Basic register allocation in the JIT
  • More JIT optimizations
  • Better machine code generation

LLVM 21 build-time dependency

The JIT compiler now uses LLVM 21 for build-time stencil generation. As always, LLVM is only needed when building CPython with the JIT enabled; end users running Python do not need LLVM installed. Instructions for installing LLVM can be found in the JIT compiler documentation for all supported platforms.

(Contributed by Savannah Ostrowski in :gh:`140973`.)

A new tracing frontend

The JIT compiler now supports significantly more bytecode operations and control flow than in Python 3.14, enabling speedups on a wider variety of code. For example, simple Python object creation is now understood by the 3.15 JIT compiler. Overloaded operations and generators are also partially supported. This was made possible by an overhauled JIT tracing frontend that records actual execution paths through code, rather than estimating them as the previous implementation did.

(Contributed by Ken Jin in :gh:`139109`. Support for Windows added by Mark Shannon in :gh:`141703`.)

Basic register allocation in the JIT

A basic form of register allocation has been added to the JIT compiler's optimizer. This allows the JIT compiler to avoid certain stack operations altogether and instead operate on registers. This allows the JIT to produce more efficient traces by avoiding reads and writes to memory.

(Contributed by Mark Shannon in :gh:`135379`.)

More JIT optimizations

More constant-propagation is now performed. This means when the JIT compiler detects that certain user code results in constants, the code can be simplified by the JIT.

(Contributed by Ken Jin and Savannah Ostrowski in :gh:`132732`.)

The JIT avoids :term:`reference count`s where possible. This generally reduces the cost of most operations in Python.

(Contributed by Ken Jin, Donghee Na, Zheao Li, Hai Zhu, Savannah Ostrowski, Reiden Ong, Noam Cohen, Tomas Roun, PuQing, Cajetan Rodrigues, and Sacul in :gh:`134584`.)

Better machine code generation

The JIT compiler's machine code generator now produces better machine code for x86-64 and AArch64 macOS and Linux targets. In general, users should experience lower memory usage for generated machine code and more efficient machine code versus the old JIT.

(Contributed by Brandt Bucher in :gh:`136528` and :gh:`136528`. Implementation for AArch64 contributed by Mark Shannon in :gh:`139855`. Additional optimizations for AArch64 contributed by Mark Shannon and Diego Russo in :gh:`140683` and :gh:`142305`.)

Removed

collections.abc

ctypes

glob

http.server

importlib.resources

pathlib

platform

pprint

sre_*

sysconfig

threading

  • Remove support for arbitrary positional or keyword arguments in the C implementation of :class:`~threading.RLock` objects. This was deprecated in Python 3.14. (Contributed by Bénédikt Tran in :gh:`134087`.)

typing

  • :class:`typing.ByteString` has been removed from typing.__all__. :class:`!typing.ByteString` has been deprecated since Python 3.9, and is scheduled for removal in Python 3.17.
  • The undocumented keyword argument syntax for creating :class:`~typing.NamedTuple` classes (for example, Point = NamedTuple("Point", x=int, y=int)) is no longer supported. Use the class-based syntax or the functional syntax instead. (Contributed by Bénédikt Tran in :gh:`133817`.)
  • Using TD = TypedDict("TD") or TD = TypedDict("TD", None) to construct a :class:`~typing.TypedDict` type with zero fields is no longer supported. Use class TD(TypedDict): pass or TD = TypedDict("TD", {}) instead. (Contributed by Bénédikt Tran in :gh:`133823`.)

wave

zipimport

Deprecated

New deprecations

C API changes

New features

Changed C APIs

Porting to Python 3.15

  • Private functions promoted to public C APIs:

    The pythoncapi-compat project can be used to get most of these new functions on Python 3.14 and older.

Removed C APIs

The following functions are removed in favor of :c:func:`PyConfig_Get`. The pythoncapi-compat project can be used to get :c:func:`!PyConfig_Get` on Python 3.13 and older.

Deprecated C APIs

Build changes

  • 64-bit builds using Visual Studio 2026 (MSVC 18) may now use the new :ref:`tail-calling interpreter <whatsnew314-tail-call-interpreter>`. Results on Visual Studio 18.1.1 report between 15-20% speedup on the geometric mean of pyperformance on Windows x86-64 over the switch-case interpreter on an AMD Ryzen 7 5800X. We have observed speedups ranging from 14% for large pure-Python libraries to 40% for long-running small pure-Python scripts on Windows. This was made possible by a new feature introduced in MSVC 18, which the official Windows 64-bit binaries on python.org now use. (Contributed by Chris Eibl, Ken Jin, and Brandt Bucher in :gh:`143068`. Special thanks to Steve Dower, and the MSVC team including Hulon Jenkins.)

Porting to Python 3.15

This section lists previously described changes and other bugfixes that may require changes to your code.