# What’s new in Python 3.15

Editor:

  TBD

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

For full details, see the changelog.

Nota

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

### High frequency statistical sampling profiler

A new statistical sampling profiler has been added to the new `profiling` module as `profiling.sampling`. This profiler enables low-overhead performance analysis of running Python processes without requiring code modification or process restart.

Unlike deterministic profilers (`cProfile` and `profile`) 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.

Key features include:

- Zero-overhead profiling: Attach to any running Python process without affecting its performance

- No code modification required: Profile existing applications without restart

- Real-time statistics: Monitor sampling quality during data collection

- Multiple output formats: Generate both detailed statistics and flamegraph data

- Thread-aware profiling: Option to profile all threads or just the main thread

Profile process 1234 for 10 seconds with default settings:

    python -m profiling.sampling 1234

Profile with custom interval and duration, save to file:

    python -m profiling.sampling -i 50 -d 30 -o profile.stats 1234

Generate collapsed stacks for flamegraph:

    python -m profiling.sampling --collapsed 1234

Profile all threads and sort by total time:

    python -m profiling.sampling -a --sort-tottime 1234

The profiler generates statistical estimates of where time is spent:

    Real-time sampling stats: Mean: 100261.5Hz (9.97µs) Min: 86333.4Hz (11.58µs) Max: 118807.2Hz (8.42µs) Samples: 400001
    Captured 498841 samples in 5.00 seconds
    Sample rate: 99768.04 samples/sec
    Error rate: 0.72%
    Profile Stats:
          nsamples   sample%   tottime (s)    cumul%   cumtime (s)  filename:lineno(function)
          43/418858       0.0         0.000      87.9         4.189  case.py:667(TestCase.run)
        3293/418812       0.7         0.033      87.9         4.188  case.py:613(TestCase._callTestMethod)
      158562/158562      33.3         1.586      33.3         1.586  test_compile.py:725(TestSpecifics.test_compiler_recursion_limit.<locals>.check_limit)
      129553/129553      27.2         1.296      27.2         1.296  ast.py:46(parse)
          0/128129       0.0         0.000      26.9         1.281  test_ast.py:884(AST_Tests.test_ast_recursion_limit.<locals>.check_limit)
            7/67446       0.0         0.000      14.2         0.674  test_compile.py:729(TestSpecifics.test_compiler_recursion_limit)
            6/60380       0.0         0.000      12.7         0.604  test_ast.py:888(AST_Tests.test_ast_recursion_limit)
            3/50020       0.0         0.000      10.5         0.500  test_compile.py:727(TestSpecifics.test_compiler_recursion_limit)
            1/38011       0.0         0.000       8.0         0.380  test_ast.py:886(AST_Tests.test_ast_recursion_limit)
            1/25076       0.0         0.000       5.3         0.251  test_compile.py:728(TestSpecifics.test_compiler_recursion_limit)
        22361/22362       4.7         0.224       4.7         0.224  test_compile.py:1368(TestSpecifics.test_big_dict_literal)
            4/18008       0.0         0.000       3.8         0.180  test_ast.py:889(AST_Tests.test_ast_recursion_limit)
          11/17696       0.0         0.000       3.7         0.177  subprocess.py:1038(Popen.__init__)
        16968/16968       3.6         0.170       3.6         0.170  subprocess.py:1900(Popen._execute_child)
            2/16941       0.0         0.000       3.6         0.169  test_compile.py:730(TestSpecifics.test_compiler_recursion_limit)

    Legend:
      nsamples: Direct/Cumulative samples (direct executing / on call stack)
      sample%: Percentage of total samples this function was directly executing
      tottime: Estimated total time spent directly in this function
      cumul%: Percentage of total samples when this function was on the call stack
      cumtime: Estimated cumulative time (including time in called functions)
      filename:lineno(function): Function location and name

    Summary of Interesting Functions:

    Functions with Highest Direct/Cumulative Ratio (Hot Spots):
      1.000 direct/cumulative ratio, 33.3% direct samples: test_compile.py:(TestSpecifics.test_compiler_recursion_limit.<locals>.check_limit)
      1.000 direct/cumulative ratio, 27.2% direct samples: ast.py:(parse)
      1.000 direct/cumulative ratio, 3.6% direct samples: subprocess.py:(Popen._execute_child)

    Functions with Highest Call Frequency (Indirect Calls):
      418815 indirect calls, 87.9% total stack presence: case.py:(TestCase.run)
      415519 indirect calls, 87.9% total stack presence: case.py:(TestCase._callTestMethod)
      159470 indirect calls, 33.5% total stack presence: test_compile.py:(TestSpecifics.test_compiler_recursion_limit)

    Functions with Highest Call Magnification (Cumulative/Direct):
      12267.9x call magnification, 159470 indirect calls from 13 direct: test_compile.py:(TestSpecifics.test_compiler_recursion_limit)
      10581.7x call magnification, 116388 indirect calls from 11 direct: test_ast.py:(AST_Tests.test_ast_recursion_limit)
      9740.9x call magnification, 418815 indirect calls from 43 direct: case.py:(TestCase.run)

The profiler automatically identifies performance bottlenecks through statistical analysis, highlighting functions with high CPU usage and call frequency patterns.

This capability is particularly valuable for debugging performance issues in production systems where traditional profiling approaches would be too intrusive.

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

### Improved error messages

- The interpreter now provides more helpful suggestions in `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'?

## 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, e.g. `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.

  Ver também

  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 `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.)

- Unraisable exceptions are now highlighted with color by default. This can be controlled by environment variables. (Contributed by Peter Bierma in gh-134170.)

- The `__repr__()` of `ImportError` and `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__` 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.)

## New modules

- None yet.

## Improved modules

### dbm

- Added new `reorganize()` methods to `dbm.dumb` and `dbm.sqlite3` which allow to recover unused free space previously occupied by deleted entries. (Contributed by Andrea Oliveri in gh-134004.)

- Add the `'m'` flag for `dbm.gnu.open()` which allows to disable the use of mmap(2). This may harm performance, but improve crash tolerance. (Contributed by Serhiy Storchaka in gh-66234.)

### difflib

- Introduced the optional color parameter to `difflib.unified_diff()`, enabling color output similar to git diff. This can be controlled by environment variables. (Contributed by Douglas Thor in gh-133725.)

- Improved the styling of HTML diff pages generated by the `difflib.HtmlDiff` class, and migrated the output to the HTML5 standard. (Contributed by Jiahao Li in gh-134580.)

### hashlib

- Ensure that hash functions guaranteed to be always available exist as attributes of `hashlib` even if they will not work at runtime due to missing backend implementations. For instance, `hashlib.md5` will no longer raise `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

- A new max_response_headers keyword-only parameter has been added to `HTTPConnection` and `HTTPSConnection` constructors. This parameter overrides the default maximum number of allowed response headers. (Contributed by Alexander Enrique Urieles Nieto in gh-131724.)

### http.cookies

- Allow ‘`"`’ double quotes in cookie values. (Contributed by Nick Burns and Senthil Kumaran in gh-92936.)

### locale

- `setlocale()` now supports language codes with `@`-modifiers. `@`-modifier are no longer silently removed in `getlocale()`, but included in the language code. (Contributed by Serhiy Storchaka in gh-137729.)

### math

- Add `math.isnormal()` and `math.issubnormal()` functions. (Contributed by Sergey B Kirpichev in gh-132908.)

- Add `math.fmax()`, `math.fmin()` and `math.signbit()` functions. (Contributed by Bénédikt Tran in gh-135853.)

### mmap

- `mmap.mmap` now has a trackfd parameter on Windows; if it is `False`, the file handle corresponding to fileno will not be duplicated. (Contributed by Serhiy Storchaka in gh-78502.)

### os.path

- Add support of the all-but-last mode in `realpath()`. (Contributed by Serhiy Storchaka in gh-71189.)

- The strict parameter to `os.path.realpath()` accepts a new value, `os.path.ALLOW_MISSING`. If used, errors other than `FileNotFoundError` will be re-raised; the resulting path can be missing but it will be free of symlinks. (Contributed by Petr Viktorin for CVE 2025-4517.)

### resource

- Add new constants: `RLIMIT_NTHR`, `RLIMIT_UMTXP`, `RLIMIT_THREADS`, `RLIM_SAVED_CUR`, and `RLIM_SAVED_MAX`. (Contributed by Serhiy Storchaka in gh-137512.)

### shelve

- Added new `reorganize()` method to `shelve` used to recover unused free space previously occupied by deleted entries. (Contributed by Andrea Oliveri in gh-134004.)

### sqlite3

- The command-line interface has several new features:

  > - SQL keyword completion on <tab>. (Contributed by Long Tan in gh-133393.)

  > - Prompts, error messages, and help text are now colored. This is enabled by default, see Controlando cores for details. (Contributed by Stan Ulbrych and Łukasz Langa in gh-133461)

### ssl

- Indicate through `ssl.HAS_PSK_TLS13` whether the `ssl` module supports “External PSKs” in TLSv1.3, as described in RFC 9258. (Contributed by Will Childs-Klein in gh-133624.)

- Added new methods for managing groups used for SSL key agreement

  > - `ssl.SSLContext.set_groups()` sets the groups allowed for doing key agreement, extending the previous `ssl.SSLContext.set_ecdh_curve()` method. This new API provides the ability to list multiple groups and supports fixed-field and post-quantum groups in addition to ECDH curves. This method can also be used to control what key shares are sent in the TLS handshake.

  > - `ssl.SSLSocket.group()` returns the group selected for doing key agreement on the current connection after the TLS handshake completes. This call requires OpenSSL 3.2 or later.

  > - `ssl.SSLContext.get_groups()` returns a list of all available key agreement groups compatible with the minimum and maximum TLS versions currently set in the context. This call requires OpenSSL 3.5 or later.

  > (Contributed by Ron Frederick in gh-136306)

- Added a new method `ssl.SSLContext.set_ciphersuites()` for setting TLS 1.3 ciphers. For TLS 1.2 or earlier, `ssl.SSLContext.set_ciphers()` should continue to be used. Both calls can be made on the same context and the selected cipher suite will depend on the TLS version negotiated when a connection is made. (Contributed by Ron Frederick in gh-137197.)

### tarfile

- `data_filter()` now normalizes symbolic link targets in order to avoid path traversal attacks. (Contributed by Petr Viktorin in gh-127987 and CVE 2025-4138.)

- `extractall()` now skips fixing up directory attributes when a directory was removed or replaced by another kind of file. (Contributed by Petr Viktorin in gh-127987 and CVE 2024-12718.)

- `extract()` and `extractall()` now (re-)apply the extraction filter when substituting a link (hard or symbolic) with a copy of another archive member, and when fixing up directory attributes. The former raises a new exception, `LinkFallbackError`. (Contributed by Petr Viktorin for CVE 2025-4330 and CVE 2024-12718.)

- `extract()` and `extractall()` no longer extract rejected members when `errorlevel()` is zero. (Contributed by Matt Prodani and Petr Viktorin in gh-112887 and CVE 2025-4435.)

### types

- Expose the write-through `locals()` proxy type as `types.FrameLocalsProxyType`. This represents the type of the `frame.f_locals` attribute, as described in PEP 667.

### unittest

- `unittest.TestCase.assertLogs()` will now accept a formatter to control how messages are formatted. (Contributed by Garry Cairns in gh-134567.)

### zlib

- Allow combining two Adler-32 checksums via `adler32_combine()`. (Contributed by Callum Attryde and Bénédikt Tran in gh-134635.)

- Allow combining two CRC-32 checksums via `crc32_combine()`. (Contributed by Bénédikt Tran in gh-134635.)

## Optimizations

### module_name

- TODO

## Deprecated

### hashlib

- In hash function constructors such as `new()` or the direct hash-named constructors such as `md5()` and `sha256()`, their optional initial data parameter could also be passed a keyword argument named `data=` or `string=` in various `hashlib` implementations.

  Support for the `string` keyword argument name is now deprecated and is slated for removal in Python 3.19. Prefer passing the initial data as a positional argument for maximum backwards compatibility.

  (Contributed by Bénédikt Tran in gh-134978.)

## Removed

### ctypes

- Removed the undocumented function `ctypes.SetPointerType()`, which has been deprecated since Python 3.13. (Contributed by Bénédikt Tran in gh-133866.)

### glob

- Removed the undocumented `glob.glob0()` and `glob.glob1()` functions, which have been deprecated since Python 3.13. Use `glob.glob()` and pass a directory to its root_dir argument instead. (Contributed by Barney Gale in gh-137466.)

### http.server

- Removed the `CGIHTTPRequestHandler` class and the `--cgi` flag from the python -m http.server command-line interface. They were deprecated in Python 3.13. (Contributed by Bénédikt Tran in gh-133810.)

### pathlib

- Removed deprecated `pathlib.PurePath.is_reserved()`. Use `os.path.isreserved()` to detect reserved paths on Windows. (Contributed by Nikita Sobolev in gh-133875.)

### platform

- Removed the `platform.java_ver()` function, which was deprecated since Python 3.13. (Contributed by Alexey Makridenko in gh-133604.)

### sre_*

- Removed `sre_compile`, `sre_constants` and `sre_parse` modules. (Contributed by Stan Ulbrych in gh-135994.)

### sysconfig

- Removed the check_home parameter of `sysconfig.is_python_build()`. (Contributed by Filipe Laíns in gh-92897.)

### threading

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

### typing

- The undocumented keyword argument syntax for creating `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 `TypedDict` type with zero field is no longer supported. Use `class TD(TypedDict): pass` or `TD = TypedDict("TD", {})` instead. (Contributed by Bénédikt Tran in gh-133823.)

- Code like `class ExtraTypeVars(P1[S], Protocol[T, T2]): ...` now raises a `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 infered in runtime before. (Contributed by Nikita Sobolev in gh-137191.)

### wave

- Removed the `getmark()`, `setmark()` and `getmarkers()` methods of the `Wave_read` and `Wave_write` classes, which were deprecated since Python 3.13. (Contributed by Bénédikt Tran in gh-133873.)

### zipimport

- Remove deprecated `zipimport.zipimporter.load_module()`. Use `zipimport.zipimporter.exec_module()` instead. (Contributed by Jiahao Li in gh-133656.)

## Porting to Python 3.15

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

## Build changes

- Removed implicit fallback to the bundled copy of the `libmpdec` library. Now this should be explicitly enabled with `--with-system-libmpdec` set to `no` or with `--without-system-libmpdec`. (Contributed by Sergey B Kirpichev in gh-115119.)

## C API changes

### New features

- Add `PySys_GetAttr()`, `PySys_GetAttrString()`, `PySys_GetOptionalAttr()`, and `PySys_GetOptionalAttrString()` functions as replacements for `PySys_GetObject()`. (Contributed by Serhiy Storchaka in gh-108512.)

- Add `PyUnstable_Unicode_GET_CACHED_HASH` to get the cached hash of a string. See the documentation for caveats. (Contributed by Petr Viktorin in gh-131510)

### Porting to Python 3.15

- `sqlite3.Connection` APIs has been cleaned up.

  - All parameters of `sqlite3.connect()` except database are now keyword-only.

  - The first three parameters of methods `create_function()` and `create_aggregate()` are now positional-only.

  - The first parameter of methods `set_authorizer()`, `set_progress_handler()` and `set_trace_callback()` is now positional-only.

  (Contributed by Serhiy Storchaka in gh-133595.)

- 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.

- `resource.RLIM_INFINITY` is now always positive. Passing a negative integer value that corresponded to its old value (such as `-1` or `-3`, depending on platform) to `resource.setrlimit()` and `resource.prlimit()` is now deprecated. (Contributed by Serhiy Storchaka in gh-137044.)

### Deprecated C APIs

- For unsigned integer formats in `PyArg_ParseTuple()`, accepting Python integers with value that is larger than the maximal value for the C type or less than the minimal value for the corresponding signed integer type of the same size is now deprecated. (Contributed by Serhiy Storchaka in gh-132629.)

- Deprecate `cval` field of the the `PyComplexObject` type. Use `PyComplex_AsCComplex()` and `PyComplex_FromCComplex()` to convert a Python complex number to/from the C `Py_complex` representation. (Contributed by Sergey B Kirpichev in gh-128813.)

- Functions `_Py_c_sum()`, `_Py_c_diff()`, `_Py_c_neg()`, `_Py_c_prod()`, `_Py_c_quot()`, `_Py_c_pow()` and `_Py_c_abs()` are soft deprecated. (Contributed by Sergey B Kirpichev in gh-128813.)

### Removed C APIs

- Remove deprecated `PyUnicode` functions:

  - `PyUnicode_AsDecodedObject()`: Use `PyCodec_Decode()` instead.

  - `PyUnicode_AsDecodedUnicode()`: Use `PyCodec_Decode()` instead; Note that some codecs (for example, “base64”) may return a type other than `str`, such as `bytes`.

  - `PyUnicode_AsEncodedObject()`: Use `PyCodec_Encode()` instead.

  - `PyUnicode_AsEncodedUnicode()`: Use `PyCodec_Encode()` instead; Note that some codecs (for example, “base64”) may return a type other than `bytes`, such as `str`.

  (Contributed by Stan Ulbrych in gh-133612)

- `PyImport_ImportModuleNoBlock()`: deprecated alias of `PyImport_ImportModule()`. (Contributed by Bénédikt Tran in gh-133644.)

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

- Python initialization functions:

  - `Py_GetExecPrefix()`: use `PyConfig_Get("base_exec_prefix")` (`sys.base_exec_prefix`) instead. Use `PyConfig_Get("exec_prefix")` (`sys.exec_prefix`) if virtual environments need to be handled.

  - `Py_GetPath()`: use `PyConfig_Get("module_search_paths")` (`sys.path`) instead.

  - `Py_GetPrefix()`: use `PyConfig_Get("base_prefix")` (`sys.base_prefix`) instead. Use `PyConfig_Get("prefix")` (`sys.prefix`) if virtual environments need to be handled.

  - `Py_GetProgramFullPath()`: use `PyConfig_Get("executable")` (`sys.executable`) instead.

  - `Py_GetProgramName()`: use `PyConfig_Get("executable")` (`sys.executable`) instead.

  - `Py_GetPythonHome()`: use `PyConfig_Get("home")` or the `PYTHONHOME` environment variable instead.

  (Contributed by Bénédikt Tran in gh-133644.)