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 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[¶](https://docs.python.org/3.15/whatsnew/3.15.html#summary-release-highlights "Link to this heading")
- **PEP 810**: Explicit lazy imports for faster startup times
- **PEP 814**: Add frozendict built-in type
- **PEP 661**: Add sentinel built-in type
- **PEP 799**: A dedicated profiling package for organizing Python profiling tools
- **PEP 799**: Tachyon: High frequency statistical sampling profiler
- **PEP 831**: Frame pointers are enabled by default for improved system-level observability
- **PEP 798**: Unpacking in comprehensions
- **PEP 686**: Python now uses UTF-8 as the default encoding
- **PEP 829**: Package startup configuration files
- **PEP 728**: TypedDict with typed extra items
- **PEP 747**: Annotating type forms with TypeForm
- **PEP 800**: Disjoint bases in the type system
- **PEP 782**: A new PyBytesWriter C API to create a Python bytes object
- **PEP 803**, **820**, **793**: Stable ABI for free-threaded builds and related C API
- **PEP 788**: Protection against finalization in the C API
- The JIT compiler has been significantly upgraded
- The official Windows 64-bit binaries now use the tail-calling interpreter
New features[¶](https://docs.python.org/3.15/whatsnew/3.15.html#new-features "Link to this heading")
**PEP 810**: Explicit lazy imports[¶](https://docs.python.org/3.15/whatsnew/3.15.html#pep-810-explicit-lazy-imports "Link to this heading")
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 [`importlib`](https://docs.python.org/3.15/library/importlib.html#module-importlib "importlib: The implementation of the import machinery.") 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 `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 and debug the failure.
For cases where you want to enable lazy loading globally without modifying source code, Python provides the `-X lazy_imports` command-line option and the `PYTHON_LAZY_IMPORTS` environment variable. Both accept two values: `all` makes all imports lazy by default, and `normal` (the default) respects the `lazy` keyword in source code. The [`sys.set_lazy_imports()`](https://docs.python.org/3.15/library/sys.html#sys.set_lazy_imports "sys.set_lazy_imports") and [`sys.get_lazy_imports()`](https://docs.python.org/3.15/library/sys.html#sys.get_lazy_imports "sys.get_lazy_imports") functions allow changing and querying this mode at runtime.
For more selective control, [`sys.set_lazy_imports_filter()`](https://docs.python.org/3.15/library/sys.html#sys.set_lazy_imports_filter "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 [`types.LazyImportType`](https://docs.python.org/3.15/library/types.html#types.LazyImportType "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 [`SyntaxError`](https://docs.python.org/3.15/library/exceptions.html#SyntaxError "SyntaxError"). Neither star imports nor future imports can be lazy (`lazy from module import *` and `lazy from __future__ import ...` both raise `SyntaxError`).
For code that cannot use the `lazy` keyword directly (for example, when supporting Python versions older than 3.15 while still using lazy imports on 3.15+), a module can define [`__lazy_modules__`](https://docs.python.org/3.15/reference/datamodel.html#module.__lazy_modules__ "module.__lazy_modules__") as a container of fully qualified module name strings. Regular `import` statements for those modules are then treated as lazy, with the same semantics as the `lazy` keyword:
__lazy_modules__ = ["json", "pathlib"]
import json # lazy import os # still eager
See also
**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[¶](https://docs.python.org/3.15/whatsnew/3.15.html#pep-814-add-frozendict-built-in-type "Link to this heading")
A new immutable type, [`frozendict`](https://docs.python.org/3.15/library/stdtypes.html#frozendict "frozendict"), is added to the [`builtins`](https://docs.python.org/3.15/library/builtins.html#module-builtins "builtins: The module that provides the built-in namespace.") module. It does not allow modification after creation. A `frozendict` is not a subclass of `dict`; it inherits directly from `object`. A `frozendict` is hashable as long as all of its keys and values are hashable. A `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 `frozendict`: [`copy`](https://docs.python.org/3.15/library/copy.html#module-copy "copy: Shallow and deep copy operations."), [`decimal`](https://docs.python.org/3.15/library/decimal.html#module-decimal "decimal: Implementation of the General Decimal Arithmetic Specification."), [`json`](https://docs.python.org/3.15/library/json.html#module-json "json: Encode and decode the JSON format."), [`marshal`](https://docs.python.org/3.15/library/marshal.html#module-marshal "marshal: Convert Python objects to streams of bytes and back (with different constraints)."), [`plistlib`](https://docs.python.org/3.15/library/plistlib.html#module-plistlib "plistlib: Generate and parse Apple plist files.") (only for serialization), [`pickle`](https://docs.python.org/3.15/library/pickle.html#module-pickle "pickle: Convert Python objects to streams of bytes and back."), [`pprint`](https://docs.python.org/3.15/library/pprint.html#module-pprint "pprint: Data pretty printer.") and [`xml.etree.ElementTree`](https://docs.python.org/3.15/library/xml.etree.elementtree.html#module-xml.etree.ElementTree "xml.etree.ElementTree: Implementation of the ElementTree API.").
[`eval()`](https://docs.python.org/3.15/library/functions.html#eval "eval") and [`exec()`](https://docs.python.org/3.15/library/functions.html#exec "exec") accept `frozendict` for _globals_, and [`type()`](https://docs.python.org/3.15/library/functions.html#type "type") and [`str.maketrans()`](https://docs.python.org/3.15/library/stdtypes.html#str.maketrans "str.maketrans") accept `frozendict` for _dict_.
Code checking for [`dict`](https://docs.python.org/3.15/library/stdtypes.html#dict "dict") type using `isinstance(arg, dict)` can be updated to `isinstance(arg, (dict, frozendict))` to accept also the `frozendict` type, or to `isinstance(arg, collections.abc.Mapping)` to accept also other mapping types such as [`MappingProxyType`](https://docs.python.org/3.15/library/types.html#types.MappingProxyType "types.MappingProxyType").
See also
**PEP 814** for the full specification and rationale.
(Contributed by Victor Stinner and Donghee Na in gh-141510.)
**PEP 661**: Add sentinel built-in type[¶](https://docs.python.org/3.15/whatsnew/3.15.html#pep-661-add-sentinel-built-in-type "Link to this heading")
A new [`sentinel`](https://docs.python.org/3.15/library/functions.html#sentinel "sentinel") type is added to the [`builtins`](https://docs.python.org/3.15/library/builtins.html#module-builtins "builtins: The module that provides the built-in namespace.") module for creating unique sentinel values with a concise representation. Sentinel objects preserve identity when copied, support use in type expressions with the `|` operator, and can be pickled when they are importable by module and name.
(PEP by Tal Einat; contributed by Jelle Zijlstra in gh-148829.)
See also
**PEP 661** for further details.
**PEP 799**: A dedicated profiling package[¶](https://docs.python.org/3.15/whatsnew/3.15.html#pep-799-a-dedicated-profiling-package "Link to this heading")
A new [`profiling`](https://docs.python.org/3.15/library/profiling.html#module-profiling "profiling: Python profiling tools for performance analysis.") module has been added to organize Python’s built-in profiling tools under a single, coherent namespace. This module contains:
- [`profiling.tracing`](https://docs.python.org/3.15/library/profiling.tracing.html#module-profiling.tracing "profiling.tracing: Deterministic tracing profiler for Python programs."): deterministic function-call tracing (relocated from `cProfile`).
- [`profiling.sampling`](https://docs.python.org/3.15/library/profiling.sampling.html#module-profiling.sampling "profiling.sampling: Statistical sampling profiler for Python processes."): a new statistical sampling profiler (named Tachyon).
The `cProfile` module remains as an alias for backwards compatibility. The [`profile`](https://docs.python.org/3.15/library/profile.html#module-profile "profile: Pure Python profiler (deprecated). (deprecated)") module is deprecated and will be removed in Python 3.17.
See also
**PEP 799** for further details.
(Contributed by Pablo Galindo and László Kiss Kollár in gh-138122.)
Tachyon: High frequency statistical sampling profiler[¶](https://docs.python.org/3.15/whatsnew/3.15.html#tachyon-high-frequency-statistical-sampling-profiler "Link to this heading")

A new statistical sampling profiler (Tachyon) has been added as [`profiling.sampling`](https://docs.python.org/3.15/library/profiling.sampling.html#module-profiling.sampling "profiling.sampling: Statistical sampling profiler for Python processes."). This profiler enables low-overhead performance analysis of running Python processes without requiring code modification or process restart.
Unlike deterministic profilers (such as [`profiling.tracing`](https://docs.python.org/3.15/library/profiling.tracing.html#module-profiling.tracing "profiling.tracing: Deterministic tracing profiler for Python programs.")) 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`
- Capture a one-shot snapshot of a running process (`dump`) - print a traceback-style stack of every thread (or all asyncio tasks with `--async-aware`). Useful for investigating hung processes.
- **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 [`pstats`](https://docs.python.org/3.15/library/pstats.html#module-pstats "pstats: Statistics object for analyzing profiler output."). 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 [`profiling.sampling`](https://docs.python.org/3.15/library/profiling.sampling.html#module-profiling.sampling "profiling.sampling: Statistical sampling profiler for Python processes.") 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 831**: Frame pointers enabled by default[¶](https://docs.python.org/3.15/whatsnew/3.15.html#pep-831-frame-pointers-enabled-by-default "Link to this heading")
CPython is now built with frame pointers by default on platforms that support them. This uses the compiler flags `-fno-omit-frame-pointer` and `-mno-omit-leaf-frame-pointer`, making native stack unwinding faster and more reliable for system profilers, debuggers, crash analysis tools, and eBPF-based observability tools.
The flags are exposed through [`sysconfig`](https://docs.python.org/3.15/library/sysconfig.html#module-sysconfig "sysconfig: Python's configuration information"), so extension modules built by tools that consume Python’s build configuration inherit frame pointers by default. This propagation is intentional: mixed Python/native profiling needs an unbroken frame-pointer chain through the interpreter, extension modules, embedding applications, and native libraries.
Important
Third-party build backends and native build systems should preserve these flags when they consume Python’s [`sysconfig`](https://docs.python.org/3.15/library/sysconfig.html#module-sysconfig "sysconfig: Python's configuration information") values. Build systems that compile C, C++, Rust, or other native code without inheriting Python’s compiler flags should enable equivalent frame-pointer flags themselves. A single native component built without frame pointers can break stack unwinding for the whole Python process.
(Contributed by Pablo Galindo Salgado and Savannah Ostrowski in gh-149201; PEP 831 written by Pablo Galindo Salgado, Ken Jin, Savannah Ostrowski, and Diego Russo.)
See also
**PEP 831** for further details.
**PEP 798**: Unpacking in comprehensions[¶](https://docs.python.org/3.15/whatsnew/3.15.html#pep-798-unpacking-in-comprehensions "Link to this heading")
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, [`itertools.chain()`](https://docs.python.org/3.15/library/itertools.html#itertools.chain "itertools.chain"), and [`itertools.chain.from_iterable()`](https://docs.python.org/3.15/library/itertools.html#itertools.chain.from_iterable "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) ``` .
See also
**PEP 798** for further details.
(Contributed by Adam Hartz in gh-143055.)
**PEP 829**: Package startup configuration files[¶](https://docs.python.org/3.15/whatsnew/3.15.html#pep-829-package-startup-configuration-files "Link to this heading")
Loaded by the [`site`](https://docs.python.org/3.15/library/site.html#module-site "site: Module responsible for site-specific configuration.") module when `-S` is not given, .pth files can contain lines that both extend [`sys.path`](https://docs.python.org/3.15/library/sys.html#sys.path "sys.path") and execute arbitrary code when the line starts with `import` (followed by a space or tab). The latter functionality can be problematic, since it is difficult to know exactly what gets executed when Python starts up.
As a step towards improving the ability to audit pre-start executable code, Python 3.15 introduces .start files which contain entry point specifications of the form `pkg.mod:callable` where `pkg.mod` is the import path to the given callable. When Python starts up, the callable is located and called with no arguments.
`import` lines in `.pth` files are silently deprecated. When a matching `.start` file is found, `import` lines in `.pth` files are ignored. There is no change to [`sys.path`](https://docs.python.org/3.15/library/sys.html#sys.path "sys.path") extension lines in `.pth` files.
The [`site`](https://docs.python.org/3.15/library/site.html#module-site "site: Module responsible for site-specific configuration.") module also provides [`site.StartupState`](https://docs.python.org/3.15/library/site.html#site.StartupState "site.StartupState") to batch startup processing for multiple site directories, ensuring all static path extensions are applied before any startup code is executed. [`site.main()`](https://docs.python.org/3.15/library/site.html#site.main "site.main") uses an instance of this class implicitly to batch process all startup configuration files during normal interpreter startup. Callers needing the same batching behavior can build a `StartupState` directly and drive it with [`addsitedir()`](https://docs.python.org/3.15/library/site.html#site.StartupState.addsitedir "site.StartupState.addsitedir"), [`addusersitepackages()`](https://docs.python.org/3.15/library/site.html#site.StartupState.addusersitepackages "site.StartupState.addusersitepackages"), and [`addsitepackages()`](https://docs.python.org/3.15/library/site.html#site.StartupState.addsitepackages "site.StartupState.addsitepackages"), then call [`process()`](https://docs.python.org/3.15/library/site.html#site.StartupState.process "site.StartupState.process") once at the end of the batch.
(Contributed by Barry Warsaw in gh-148641 and gh-150228.)
**PEP 803**: Stable ABI for free-threaded builds[¶](https://docs.python.org/3.15/whatsnew/3.15.html#pep-803-stable-abi-for-free-threaded-builds "Link to this heading")
C extensions that target the Stable ABI can now be compiled for the new _Stable ABI for Free-Threaded Builds_ (also known as `abi3t`), which makes them compatible with free-threaded builds of CPython. This usually requires some non-trivial changes to the source code; specifically:
- Switching to API introduced in **PEP 697** (Python 3.12), such as negative [`basicsize`](https://docs.python.org/3.15/c-api/type.html#c.PyType_Spec.basicsize "PyType_Spec.basicsize") and [`PyObject_GetTypeData()`](https://docs.python.org/3.15/c-api/object.html#c.PyObject_GetTypeData "PyObject_GetTypeData"), rather than making [`PyObject`](https://docs.python.org/3.15/c-api/structures.html#c.PyObject "PyObject") part of the instance struct; and
- Switching from a `PyInit_` function to a new export hook, [`PyModExport_*`](https://docs.python.org/3.15/c-api/extension-modules.html#c.PyModExport_modulename "PyModExport_modulename"), introduced for this purpose in **PEP 793**, with a new [`PySlot`](https://docs.python.org/3.15/c-api/slots.html#c.PySlot "PySlot") structure introduced in **PEP 820**.
Note that Stable ABI does not offer all the functionality that CPython has to offer. Extensions that cannot switch to `abi3t` should continue to build for the existing Stable ABI (`abi3`) and the version-specific ABI for free-threading (`cp315t`) separately.
Stable ABI for Free-Threaded Builds should typically be selected in a build tool (such as, for example, Setuptools, meson-python, scikit-build-core, or Maturin). At the time of writing, these tools do **not** support `abi3t`. If this is the case for your tool, compile for `cp315t` separately. If not using a build tool – or when writing such a tool – you can select `abi3t` by setting the macro `Py_TARGET_ABI3T` as discussed in Compiling for Stable ABI.
A practical migration guide for switching to `abi3t` is available.
See also
**PEP 803** for further details.
**PEP 788**: Protecting the C API from interpreter finalization[¶](https://docs.python.org/3.15/whatsnew/3.15.html#pep-788-protecting-the-c-api-from-interpreter-finalization "Link to this heading")
In the C API, interpreter finalization can be problematic for many extensions, because attaching a thread state will permanently hang the thread, resulting in deadlocks and other spurious issues. Additionally, it has historically been impossible to safely check whether an interpreter is alive before using it, leading to crashes when a thread concurrently deletes an interpreter while another thread is trying to attach to it.
There are now several new suites of APIs to circumvent these problems:
- Interpreter guards, which prevent an interpreter from finalizing.
- Interpreter views, which allow thread-safe access to an interpreter that may be concurrently finalizing or deleted.
- New APIs to automatically attach and detach thread states that come with built-in protection against finalization.
In addition, APIs in the `PyGILState` family (most notably [`PyGILState_Ensure()`](https://docs.python.org/3.15/c-api/threads.html#c.PyGILState_Ensure "PyGILState_Ensure") and [`PyGILState_Release()`](https://docs.python.org/3.15/c-api/threads.html#c.PyGILState_Release "PyGILState_Release")) have been soft deprecated. There is **no** plan to remove them, and existing code will continue to work, but there will be no new `PyGILState` APIs in future versions of Python.
See also
**PEP 788** for further details.
(Contributed by Peter Bierma in gh-149101.)
Improved error messages[¶](https://docs.python.org/3.15/whatsnew/3.15.html#improved-error-messages "Link to this heading")
- The interpreter now provides more helpful suggestions in [`AttributeError`](https://docs.python.org/3.15/library/exceptions.html#AttributeError "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'?
- When an [`AttributeError`](https://docs.python.org/3.15/library/exceptions.html#AttributeError "AttributeError") on a builtin type has no close match via Levenshtein distance, the error message now checks a static table of common method names from other languages (JavaScript, Java, Ruby, C#) and suggests the Python equivalent:
>>> [1, 2, 3].push(4) Traceback (most recent call last): ... AttributeError: 'list' object has no attribute 'push'. Did you mean '.append'?
>>> 'hello'.toUpperCase() Traceback (most recent call last): ... AttributeError: 'str' object has no attribute 'toUpperCase'. Did you mean '.upper'? When the Python equivalent is a language construct rather than a method, the hint describes the construct directly:
>>> {}.put("a", 1) Traceback (most recent call last): ... AttributeError: 'dict' object has no attribute 'put'. Use d[k] = v. When a mutable method is called on an immutable type, the hint suggests the mutable counterpart:
>>> (1, 2, 3).append(4) Traceback (most recent call last): ... AttributeError: 'tuple' object has no attribute 'append'. Did you mean to use a 'list' object? These hints also work for subclasses of builtin types.
(Contributed by Matt Van Horn in gh-146406.)
- The interpreter now tries to provide a suggestion when [`delattr()`](https://docs.python.org/3.15/library/functions.html#delattr "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 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.)
- Several error messages incorrectly using the term “argument” have been corrected. (Contributed by Stan Ulbrych in gh-133382.)
Other language changes[¶](https://docs.python.org/3.15/whatsnew/3.15.html#other-language-changes "Link to this heading")
- 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 opt-in 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 `PYTHONUTF8=0` environment variable or the `-X utf8=0` command-line option.
See also
**PEP 686** for further details. (Contributed by Adam Turner in gh-133711; PEP 686 written by Inada Naoki.)
- The interpreter help (such as `python --help`) is now in color. This can be controlled by environment variables. (Contributed by Hugo van Kemenade in gh-148766.)
- Unraisable exceptions are now highlighted with color by default. This can be controlled by environment variables. (Contributed by Peter Bierma in gh-134170.)
- More color in argparse, ast, calendar, difflib, http.server, pickletools, PyREPL tab completion, python –help, sqlite3, timeit, tokenize, unraisable exceptions and stdlib (ast, compileall, doctest, gzip, inspect, json.tool, pdb, profiling.sampling, random, regrtest, sqlite3, timeit, tokenize, trace, unittest, uuid, zipapp, zipfile) CLI help.
- The [`__repr__()`](https://docs.python.org/3.15/reference/datamodel.html#object.__repr__ "object.__repr__") of [`ImportError`](https://docs.python.org/3.15/library/exceptions.html#ImportError "ImportError") and [`ModuleNotFoundError`](https://docs.python.org/3.15/library/exceptions.html#ModuleNotFoundError "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 [`__dict__`](https://docs.python.org/3.15/reference/datamodel.html#object.__dict__ "object.__dict__") and `__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 `-W` option and the `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 [`Decimal`](https://docs.python.org/3.15/library/decimal.html#decimal.Decimal "decimal.Decimal") and [`Fraction`](https://docs.python.org/3.15/library/fractions.html#fractions.Fraction "fractions.Fraction")), not only integers or floats, although this does not improve precision. (Contributed by Serhiy Storchaka in gh-67795.)
- Added [`bytearray.take_bytes(n=None, /)`](https://docs.python.org/3.15/library/stdtypes.html#bytearray.take_bytes "bytearray.take_bytes") to take bytes out of a [`bytearray`](https://docs.python.org/3.15/library/stdtypes.html#bytearray "bytearray") without copying. This enables optimizing code which must return [`bytes`](https://docs.python.org/3.15/library/stdtypes.html#bytes "bytes") after working with a mutable buffer of bytes such as data buffering, network protocol parsing, encoding, decoding, and compression. Common code patterns which can be optimized with [`take_bytes()`](https://docs.python.org/3.15/library/stdtypes.html#bytearray.take_bytes "bytearray.take_bytes") are listed below.
Suggested optimizing refactors[¶](https://docs.python.org/3.15/whatsnew/3.15.html#id12 "Link to this table") | Description | Old | New | | --- | --- | --- |
| Return [`bytes`](https://docs.python.org/3.15/library/stdtypes.html#bytes "bytes") after working with [`bytearray`](https://docs.python.org/3.15/library/stdtypes.html#bytearray "bytearray") | def read() -> bytes: buffer = bytearray(1024) ... return bytes(buffer) | def read() -> bytes: buffer = bytearray(1024) ... return buffer.take_bytes() | | Empty a buffer getting the bytes | buffer = bytearray(1024) ... data = bytes(buffer) buffer.clear() | buffer = bytearray(1024) ... data = buffer.take_bytes() |
| Split a buffer at a specific separator | buffer = bytearray(b'abc\ndef') n = buffer.find(b'\n') data = bytes(buffer[:n + 1]) del buffer[:n + 1] assert data == b'abc\n' assert buffer == bytearray(b'def') | buffer = bytearray(b'abc\ndef') n = buffer.find(b'\n') data = buffer.take_bytes(n + 1) |
| Split a buffer at a specific separator; discard after the separator | buffer = bytearray(b'abc\ndef') n = buffer.find(b'\n') data = bytes(buffer[:n]) buffer.clear() assert data == b'abc' assert len(buffer) == 0 | buffer = bytearray(b'abc\ndef') n = buffer.find(b'\n') buffer.resize(n) data = buffer.take_bytes() |
(Contributed by Cody Maloney in gh-139871.)
- Many functions related to compiling or parsing Python code, such as [`compile()`](https://docs.python.org/3.15/library/functions.html#compile "compile"), [`ast.parse()`](https://docs.python.org/3.15/library/ast.html#ast.parse "ast.parse"), [`symtable.symtable()`](https://docs.python.org/3.15/library/symtable.html#symtable.symtable "symtable.symtable"), and [`importlib.abc.InspectLoader.source_to_code()`](https://docs.python.org/3.15/library/importlib.html#importlib.abc.InspectLoader.source_to_code "importlib.abc.InspectLoader.source_to_code"), now allow the module name to be passed. It is needed to unambiguously filter syntax warnings by module name. (Contributed by Serhiy Storchaka in gh-135801.)
- Allowed defining the _\_\_dict\_\__ and _\_\_weakref\_\____slots__ for any class. (Contributed by Serhiy Storchaka in gh-41779.)
- Allowed defining any __slots__ for a class derived from [`tuple`](https://docs.python.org/3.15/library/stdtypes.html#tuple "tuple") (including classes created by [`collections.namedtuple()`](https://docs.python.org/3.15/library/collections.html#collections.namedtuple "collections.namedtuple")). (Contributed by Serhiy Storchaka in gh-41779.)
- The [`slice`](https://docs.python.org/3.15/library/functions.html#slice "slice") type now supports subscription, making it a generic type. (Contributed by James Hilton-Balfe in gh-128335.)
- The class [`memoryview`](https://docs.python.org/3.15/library/stdtypes.html#memoryview "memoryview") now supports the float complex and double complex C types: formatting characters `'Zf'` and `'Zd'` respectively. (Contributed by Victor Stinner in gh-146151 and gh-148675.)
- Allow the _count_ argument of [`bytes.replace()`](https://docs.python.org/3.15/library/stdtypes.html#bytes.replace "bytes.replace") to be a keyword. (Contributed by Stan Ulbrych in gh-147856.)
- Unary plus is now accepted in `match` literal patterns, mirroring the existing support for unary minus. (Contributed by Bartosz Sławecki in gh-145239.)
- The import system now acquires per-module locks in hierarchical order (parent packages before their submodules). This fixes a long-standing deadlock where one thread importing `pkg.sub` and another importing `pkg.sub.mod` could each block the other when `pkg/sub/__init__.py` imports `pkg.sub.mod`. (Contributed by Gregory P. Smith in gh-83065.)
- File names of Stable ABI extensions that use the `.so` suffix may now include a multiarch tuple, for example, `foo.abi3-x86-64-linux-gnu.so`. This permits stable ABI extensions for multiple architectures to be co-installed into the same directory, without clashing with each other, as regular dynamic extensions do. (Contributed by Stefano Rivera in gh-122931.)
- The `__cached__` attribute on modules, which was deprecated since version 3.13, is no longer set or taken into consideration by the import system or standard library. Use [`__spec__.cached`](https://docs.python.org/3.15/library/importlib.html#importlib.machinery.ModuleSpec.cached "importlib.machinery.ModuleSpec.cached") instead. (Contributed by Brett Cannon in gh-97879)
Note that the [`__loader__`](https://docs.python.org/3.15/reference/datamodel.html#module.__loader__ "module.__loader__") and [`__package__`](https://docs.python.org/3.15/reference/datamodel.html#module.__package__ "module.__package__") attributes are also deprecated and scheduled for removal.
Default interactive shell[¶](https://docs.python.org/3.15/whatsnew/3.15.html#default-interactive-shell "Link to this heading")
- Tab completions are now colored by object kind, based on fancycompleter. Set `PYTHON_BASIC_COMPLETER` to fall back to [`rlcompleter`](https://docs.python.org/3.15/library/rlcompleter.html#module-rlcompleter "rlcompleter: Python identifier completion, suitable for the GNU readline library."). Color can also be controlled by environment variables. (Contributed by Antonio Cuni and Pablo Galindo in gh-130472.)
New modules[¶](https://docs.python.org/3.15/whatsnew/3.15.html#new-modules "Link to this heading")
math.integer[¶](https://docs.python.org/3.15/whatsnew/3.15.html#math-integer "Link to this heading")
This module provides access to the mathematical functions for integer arguments (**PEP 791**). (Contributed by Serhiy Storchaka in gh-81313.)
Improved modules[¶](https://docs.python.org/3.15/whatsnew/3.15.html#improved-modules "Link to this heading")
argparse[¶](https://docs.python.org/3.15/whatsnew/3.15.html#argparse "Link to this heading")
- The [`BooleanOptionalAction`](https://docs.python.org/3.15/library/argparse.html#argparse.BooleanOptionalAction "argparse.BooleanOptionalAction") action now supports single-dash long options and alternate prefix characters. (Contributed by Serhiy Storchaka in gh-138525.)
- Changed the _suggest\_on\_error_ parameter of [`argparse.ArgumentParser`](https://docs.python.org/3.15/library/argparse.html#argparse.ArgumentParser "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 [`ArgumentParser`](https://docs.python.org/3.15/library/argparse.html#argparse.ArgumentParser "argparse.ArgumentParser") description and epilog text to highlight inline code when color output is enabled. (Contributed by Savannah Ostrowski in gh-142390.)
- Extended backtick markup to argument `help` text and added support for double backticks (RST inline-literal style). (Contributed by Hugo van Kemenade in gh-149375.)
array[¶](https://docs.python.org/3.15/whatsnew/3.15.html#array "Link to this heading")
- Support the float complex and double complex C types: formatting characters `'Zf'` and `'Zd'` respectively. (Contributed by Victor Stinner in gh-146151 and gh-148675.)
- Support half-floats (16-bit IEEE 754 binary interchange format): formatting character `'e'`. (Contributed by Sergey B Kirpichev in gh-146238.)
- The [`array.typecodes`](https://docs.python.org/3.15/library/array.html#array.typecodes "array.typecodes") type changed from [`str`](https://docs.python.org/3.15/library/stdtypes.html#str "str") to [`tuple`](https://docs.python.org/3.15/library/stdtypes.html#tuple "tuple") to support type codes longer than 1 character (`Zf` and `Zd`). (Contributed by Victor Stinner in gh-148675.)
ast[¶](https://docs.python.org/3.15/whatsnew/3.15.html#ast "Link to this heading")
- Add _color_ parameter to [`dump()`](https://docs.python.org/3.15/library/ast.html#ast.dump "ast.dump"). If `True`, the returned string is syntax highlighted using ANSI escape sequences. If `False` (the default), colored output is always disabled. (Contributed by Stan Ulbrych in gh-148981.)
- The command-line output is now syntax highlighted by default. This can be controlled using environment variables. (Contributed by Stan Ulbrych in gh-148981.)
asyncio[¶](https://docs.python.org/3.15/whatsnew/3.15.html#asyncio "Link to this heading")
- Added [`TaskGroup.cancel`](https://docs.python.org/3.15/library/asyncio-task.html#asyncio.TaskGroup.cancel "asyncio.TaskGroup.cancel") to allow early termination of a task group, for instance, when the goal of the tasks has been achieved or their services are no longer needed. Previously this would involve unintuitive boilerplate such as an extra task raising a custom exception which is then suppressed as it exits the task group. (Contributed by John Belmonte in gh-127214.)
base64[¶](https://docs.python.org/3.15/whatsnew/3.15.html#base64 "Link to this heading")
- Added the _pad_ parameter in [`z85encode()`](https://docs.python.org/3.15/library/base64.html#base64.z85encode "base64.z85encode"). (Contributed by Hauke Dämpfling in gh-143103.)
- Added the _padded_ parameter in [`b32encode()`](https://docs.python.org/3.15/library/base64.html#base64.b32encode "base64.b32encode"), [`b32decode()`](https://docs.python.org/3.15/library/base64.html#base64.b32decode "base64.b32decode"), [`b32hexencode()`](https://docs.python.org/3.15/library/base64.html#base64.b32hexencode "base64.b32hexencode"), [`b32hexdecode()`](https://docs.python.org/3.15/library/base64.html#base64.b32hexdecode "base64.b32hexdecode"), [`b64encode()`](https://docs.python.org/3.15/library/base64.html#base64.b64encode "base64.b64encode"), [`b64decode()`](https://docs.python.org/3.15/library/base64.html#base64.b64decode "base64.b64decode"), [`urlsafe_b64encode()`](https://docs.python.org/3.15/library/base64.html#base64.urlsafe_b64encode "base64.urlsafe_b64encode"), and [`urlsafe_b64decode()`](https://docs.python.org/3.15/library/base64.html#base64.urlsafe_b64decode "base64.urlsafe_b64decode"). (Contributed by Serhiy Storchaka in gh-73613.)
- Added the _wrapcol_ parameter in [`b16encode()`](https://docs.python.org/3.15/library/base64.html#base64.b16encode "base64.b16encode"), [`b32encode()`](https://docs.python.org/3.15/library/base64.html#base64.b32encode "base64.b32encode"), [`b32hexencode()`](https://docs.python.org/3.15/library/base64.html#base64.b32hexencode "base64.b32hexencode"), [`b64encode()`](https://docs.python.org/3.15/library/base64.h