3.12.13

Latest release in branch 3.12
Released 1 month ago (March 03, 2026)

Software Python
Branch 3.12
Status
Supported
End of life October 02, 2028
First official release version 3.12.0
First official release date 2 years ago (October 02, 2023)
Source code https://github.com/python/cpython/tree/v3.12.13
Documentation https://docs.python.org/release/3.12.13/
Download https://www.python.org/downloads/release/python-31213/
Python 3.12 Releases View 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

Version Release date
3.12.13 1 month ago
(March 03, 2026)
3.12.12 6 months ago
(October 09, 2025)
3.12.11 10 months ago
(June 03, 2025)
3.12.10 1 year ago
(April 08, 2025)
3.12.9 1 year ago
(February 04, 2025)
3.12.8 1 year ago
(December 03, 2024)
3.12.7 1 year ago
(October 01, 2024)
3.12.6 1 year ago
(September 06, 2024)
3.12.5 1 year ago
(August 06, 2024)
3.12.4 1 year ago
(June 06, 2024)
3.12.3 2 years ago
(April 09, 2024)
3.12.2 2 years ago
(February 06, 2024)
3.12.1 2 years ago
(December 07, 2023)
3.12.0 2 years ago
(October 02, 2023)
3.12.0rc3 2 years ago
(September 18, 2023)
3.12.0rc2 2 years ago
(September 05, 2023)
3.12.0rc1 2 years ago
(August 05, 2023)
3.12.0b4 2 years ago
(July 11, 2023)
3.12.0b3 2 years ago
(June 19, 2023)
3.12.0b2 2 years ago
(June 06, 2023)
3.12.0b1 2 years ago
(May 22, 2023)
3.12.0a7 3 years ago
(April 04, 2023)
3.12.0a6 3 years ago
(March 07, 2023)
3.12.0a5 3 years ago
(February 07, 2023)
3.12.0a4 3 years ago
(January 10, 2023)
3.12.0a3 3 years ago
(December 06, 2022)
3.12.0a2 3 years ago
(November 14, 2022)
3.12.0a1 3 years ago
(October 24, 2022)