Stable Release in branch 3.12
3.12.15
Released 01 Oct 2026
(14 days ago)
SoftwarePython
Version3.12
Initial release3.12.0
02 Oct 2023
(3 years ago)
Latest release3.12.15
01 Oct 2026
(14 days ago)
End of life31 Oct 2028
(Ends in 2 years)
Source codehttps://github.com/python/cpython/tree/v3.12.15
Documentationhttps://docs.python.org/release/3.12.15/
Downloadhttps://www.python.org/downloads/release/python-31215/
Python 3.12 ReleasesView full list

What Is New in Python 3.12

Python 3.12 continues the performance work from 3.11, adding a new type parameter syntax for generics, f-string parser rewrite that unlocks previously invalid syntax, Linux perf profiler integration, and the largest stdlib cleanup in Python's history via PEP 594 removals. Sub-interpreters gain their own GIL, moving toward genuine interpreter-level isolation.

Category Change PEP / Reference
New Syntax Type parameter syntax: def fn[T](x: T) -> T PEP 695
New Syntax type statement for type aliases: type Point = tuple[float, float] PEP 695
New Syntax F-strings rewritten -- nested f-strings, quotes, multiline expressions allowed PEP 701
Performance ~5% average speedup over 3.11; per-interpreter GIL for sub-interpreters PEP 684
Tooling Linux perf profiler support via python -X perf --
Standard Library pathlib.Path.walk(); os.path.isreserved(); itertools.batched() --
Removed distutils, asynchat, asyncore, smtpd, lib2to3, imp --
Deprecated pkgutil.find_loader(); typing.AnyStr; non-path objects in pathlib --

What Is the New Type Parameter Syntax in Python 3.12?

PEP 695 introduces a compact syntax for defining generics directly in def, class, and the new type statement. Before 3.12, defining a generic function required importing TypeVar and declaring it separately -- now it is inline.

# Before 3.12
from typing import TypeVar
T = TypeVar("T")
def first(items: list[T]) -> T:
    return items[0]

# Python 3.12+
def first[T](items: list[T]) -> T:
    return items[0]

# Generic class
class Stack[T]:
    def __init__(self) -> None:
        self._items: list[T] = []
    def push(self, item: T) -> None:
        self._items.append(item)
    def pop(self) -> T:
        return self._items.pop()

# Type alias
type Vector = list[float]
type Matrix[T] = list[list[T]]

The type statement creates a TypeAliasType object, which type checkers understand as a true alias rather than a variable assignment. This resolves an old ambiguity where MyList = list[str] looked like a regular assignment.

What Changed About F-Strings in Python 3.12?

The f-string parser was completely rewritten (PEP 701). The old parser was a hack bolted onto the tokenizer and had arbitrary restrictions. The new parser allows:

  • Reuse of any quote type inside an expression: f"{'nested string'}"
  • Nested f-strings: f"{f"{f"{x}"}"}"
  • Multiline expressions inside {} with comments
  • Backslashes inside expression blocks (previously a SyntaxError)
# All these now work:
names = ["Alice", "Bob"]
print(f"{", ".join(names)}")          # nested same-quote string
print(f"{"\n".join(names)}")          # backslash in expression
total = f"{
    sum(range(100))   # multiline with comment
}")
print(f"nested: {f"inner {1 + 1}"}")

How Does Linux perf Profiler Support Work in 3.12?

Running python -X perf script.py generates profiling data readable by the Linux perf tool. CPython emits DWARF frame information so that perf report shows Python function names alongside C stack frames -- giving a unified view of where time is spent across both layers.

# Profile with Linux perf
python -X perf -m my_app

# Then analyze
perf report

This is aimed at production profiling scenarios where you need to see the interaction between Python code and C extensions or system calls -- situations where cProfile alone is insufficient.

What Was Removed in Python 3.12?

Python 3.12 completed the removal of modules deprecated in 3.11 and earlier:

  • distutils -- use setuptools (PyPI) or the build package instead.
  • asynchat, asyncore -- use asyncio.
  • smtpd -- use aiosmtpd or a dedicated SMTP library.
  • lib2to3 and the 2to3 tool -- Python 2 migration tools are obsolete.
  • imp module -- use importlib.
  • pkgutil.find_loader() -- use importlib.util.find_spec().

If your code imports any of these, it will raise ModuleNotFoundError on Python 3.12. Migration is straightforward in all cases.

What New Standard Library Functions Shipped in 3.12?

  • pathlib.Path.walk() -- like os.walk() but returns Path objects; supports top_down and on_error.
  • itertools.batched(iterable, n) -- yields tuples of up to n items; clean chunking without manual slicing.
  • os.process_cpu_count() -- counts CPUs available to the process (respects cgroups), unlike os.cpu_count() which counts all physical CPUs.
  • sys.monitoring -- low-overhead event monitoring for debuggers and profilers to hook into without patching bytecode.
  • dataclasses.KW_ONLY -- marker to make all subsequent fields keyword-only in a dataclass definition.

FAQ

Does the new def fn[T]() syntax work in Python 3.11 and earlier?
No -- it is a 3.12+ syntax and causes a SyntaxError on older versions. If you need 3.11 compatibility, keep using TypeVar and the old import-based style. The __future__ module does not back-port this syntax.

Does the f-string rewrite change any existing f-string behavior?
No -- all existing valid f-strings produce identical results. The change only unlocks previously invalid constructs. This is purely additive: old code runs unchanged, and new code can take advantage of the relaxed grammar.

Does per-interpreter GIL (PEP 684) mean CPython finally has true thread parallelism in 3.12?
Not for normal threads. PEP 684 gives each sub-interpreter its own GIL, but sub-interpreters in 3.12 still cannot share Python objects between them safely. The work toward no-GIL free-threading started in 3.13 as an opt-in experimental build.

What should I use instead of distutils now that it is removed?
For packaging: setuptools (still on PyPI, has the same API) or the modern build backend ecosystem (build, hatch, flit). For C extension compilation: most projects use setuptools's Extension class. The pyproject.toml build system is the right long-term direction.

Is itertools.batched() equivalent to a manual islice loop?
Yes, semantically -- but cleaner. batched("ABCDEFG", 3) yields ('A','B','C'), ('D','E','F'), ('G',). The last batch is shorter if the input does not divide evenly. In 3.13, a strict=True option was added to raise if the final batch is short.

Releases In Branch 3.12

VersionRelease date
3.12.1501 Oct 2026
(14 days ago)
3.12.1412 Aug 2026
(2 months ago)
3.12.1303 Mar 2026
(7 months ago)
3.12.1209 Oct 2025
(1 year ago)
3.12.1103 Jun 2025
(1 year ago)
3.12.1008 Apr 2025
(1 year ago)
3.12.904 Feb 2025
(1 year ago)
3.12.803 Dec 2024
(1 year ago)
3.12.701 Oct 2024
(2 years ago)
3.12.606 Sep 2024
(2 years ago)
3.12.506 Aug 2024
(2 years ago)
3.12.406 Jun 2024
(2 years ago)
3.12.309 Apr 2024
(2 years ago)
3.12.206 Feb 2024
(2 years ago)
3.12.107 Dec 2023
(2 years ago)
3.12.002 Oct 2023
(3 years ago)
3.12.0rc318 Sep 2023
(3 years ago)
3.12.0rc205 Sep 2023
(3 years ago)
3.12.0rc105 Aug 2023
(3 years ago)
3.12.0b411 Jul 2023
(3 years ago)
3.12.0b319 Jun 2023
(3 years ago)
3.12.0b206 Jun 2023
(3 years ago)
3.12.0b122 May 2023
(3 years ago)
3.12.0a704 Apr 2023
(3 years ago)
3.12.0a607 Mar 2023
(3 years ago)
3.12.0a507 Feb 2023
(3 years ago)
3.12.0a410 Jan 2023
(3 years ago)
3.12.0a306 Dec 2022
(3 years ago)
3.12.0a214 Nov 2022
(3 years ago)
3.12.0a124 Oct 2022
(3 years ago)