Skip to content

Instantly share code, notes, and snippets.

@ruben-arts
Last active July 31, 2026 07:23
Show Gist options
  • Select an option

  • Save ruben-arts/596390c215bdd7d2578e593b5309d68e to your computer and use it in GitHub Desktop.

Select an option

Save ruben-arts/596390c215bdd7d2578e593b5309d68e to your computer and use it in GitHub Desktop.
Preview of Python Python 3.16.0a0 (heads/main-dirty:ac8ba0ca5a)
# Run this with Pixi:
# pixi run --script https://gist.github.com/ruben-arts/596390c215bdd7d2578e593b5309d68e/raw/c95c30398594ce53abc4761206eb5cead67995b9/python316.py
# /// script
# [tool.pixi.workspace]
# channels = ["conda-forge"]
# preview = true
#
# [tool.pixi.dependencies]
# python.git = "https://github.com/python/cpython"
# python.subdirectory = "Tools/pixi-packages"
# python.rev = "ac8ba0ca5a04cd7b27b44822d31df640e817fd78"
# ///
"""A tour of what's new in Python 3.16.
Every section runs real code from
https://docs.python.org/3.16/whatsnew/3.16.html and prints the result, so the
output is the demo. Sections that need a platform-specific library (lzma BCJ
filters, iconv codecs, Linux-only syscalls) print a skip line instead of
failing.
"""
import ast
import csv
import ctypes
import gzip
import io
import ipaddress
import math
import os
import re
import shlex
import symtable
import sys
import tempfile
import weakref
import zipfile
from ctypes.util import wrap_dll_function
from pathlib import Path
def section(title: str) -> None:
print(f"\n\033[1m{title}\033[0m")
def show(label: str, value: object) -> None:
print(f" {label:<44} {value!r}")
def skip(reason: str) -> None:
print(f" (skipped: {reason})")
def raises(label: str, thunk) -> None:
"""Print the error from something that 3.16 removed or now rejects."""
try:
thunk()
except Exception as exc:
print(f" {label:<44} {type(exc).__name__}: {exc}")
else:
print(f" {label:<44} unexpectedly still works")
# --------------------------------------------------------------------------
# re: nested character sets and the set operators -- && ||
# --------------------------------------------------------------------------
def demo_re_set_operations() -> None:
section("re — nested sets with set operators (--, &&, ||)")
show("[a-z--[aeiou]] on 'hello world'", re.findall(r"[a-z--[aeiou]]", "hello world"))
show(r"[\w&&[a-z]] on 'Py3_16'", re.findall(r"[\w&&[a-z]]", "Py3_16"))
show("[a-z||A-Z] on 'Py 3.16'", re.findall(r"[a-z||A-Z]", "Py 3.16"))
# A leading ^ complements the whole result, not just the first operand.
show("[^a-z--[aeiou]] on 'hello!'", re.findall(r"[^a-z--[aeiou]]", "hello!"))
# Operators apply left to right, with no precedence.
show("[a-z--[aeiou]&&[a-m]] on 'hello world'",
re.findall(r"[a-z--[aeiou]&&[a-m]]", "hello world"))
# ~~ is reserved for a future symmetric difference and warns today.
import warnings
with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("always")
re.compile(r"[a-z~~[aeiou]]")
show("[a-z~~[aeiou]] compiles with",
caught[0].category.__name__ if caught else None)
# --------------------------------------------------------------------------
# re: Unicode property escapes \p{...} and \P{...}
# --------------------------------------------------------------------------
def demo_re_unicode_properties() -> None:
section(r"re — Unicode property escapes \p{...} / \P{...}")
text = "Hello Wörld ÜBER 42 ٤٢"
show(r"\p{Lu} (uppercase letters)", re.findall(r"\p{Lu}", text))
show(r"\p{Nd} (decimal digits, any script)", re.findall(r"\p{Nd}", text))
show(r"\P{ASCII} (non-ASCII)", re.findall(r"\P{ASCII}", text))
# Property names match loosely: case, spaces, '-' and '_' are all ignored.
show(r"\p{ General-Category = lu }", re.findall(r"\p{ General-Category = lu }", text))
# The real payoff: properties as operands of the new set operators.
show(r"[\p{L}--\p{ASCII}] (non-ASCII letters)",
re.findall(r"[\p{L}--\p{ASCII}]", text))
# A Unicode-correct identifier check, expressed entirely in the pattern.
ident = re.compile(r"\p{XID_Start}\p{XID_Continue}*")
show(r"\p{XID_Start}\p{XID_Continue}* 'café_1'", bool(ident.fullmatch("café_1")))
show("...same pattern vs '1café'", bool(ident.fullmatch("1café")))
# --------------------------------------------------------------------------
# math: half-turn trigonometry (C23 / IEEE 754-2019)
# --------------------------------------------------------------------------
def demo_math_halfturns() -> None:
section("math — half-turn trigonometry, exact where radians can't be")
show("sin(pi) (old, off by an eps)", math.sin(math.pi))
show("sinpi(1.0) (new, exactly zero)", math.sinpi(1.0))
show("cos(pi/2) (old)", math.cos(math.pi / 2))
show("cospi(0.5) (new)", math.cospi(0.5))
show("tanpi(0.25)", math.tanpi(0.25))
show("asinpi(1.0) (half-turns)", math.asinpi(1.0))
show("acospi(-1.0) (half-turns)", math.acospi(-1.0))
show("atanpi(1.0) (half-turns)", math.atanpi(1.0))
show("atan2pi(1.0, -1.0) (quadrant-aware)", math.atan2pi(1.0, -1.0))
# Every quarter turn stays exact, all the way around the circle.
show("[cospi(n/2) for n in 0..4]", [math.cospi(n / 2) for n in range(5)])
# --------------------------------------------------------------------------
# io.BytesIO.peek()
# --------------------------------------------------------------------------
def demo_bytesio_peek() -> None:
section("io — BytesIO.peek() looks ahead without consuming")
buf = io.BytesIO(b"GIF89a\x01\x02\x03")
show("peek(6)", buf.peek(6))
show("tell() after peek", buf.tell())
show("read(6)", buf.read(6))
show("tell() after read", buf.tell())
# --------------------------------------------------------------------------
# zipfile.ZipFile.remove() / .repack()
# --------------------------------------------------------------------------
def demo_zipfile_remove_repack() -> None:
section("zipfile — remove() a member, repack() to reclaim the bytes")
with tempfile.TemporaryDirectory() as tmp:
path = Path(tmp, "demo.zip")
with zipfile.ZipFile(path, "w", zipfile.ZIP_STORED) as zf:
zf.writestr("keep.txt", "keep me\n" * 100)
zf.writestr("secret.txt", "leak me\n" * 5000)
show("size with both members", path.stat().st_size)
with zipfile.ZipFile(path, "a") as zf:
removed = [zf.remove("secret.txt")]
show("remove() returned", removed[0].filename)
show("size after remove() (data still there)", path.stat().st_size)
# Passing the removed ZipInfo lets repack skip scanning the archive.
zf.repack(removed)
show("size after repack()", path.stat().st_size)
with zipfile.ZipFile(path) as zf:
show("remaining members", zf.namelist())
show("b'leak me' still on disk?", b"leak me" in path.read_bytes())
# --------------------------------------------------------------------------
# ipaddress: IPv4Network.next_network() / IPv6Network.next_network()
# --------------------------------------------------------------------------
def demo_ipaddress_next_network() -> None:
section("ipaddress — next_network() walks an allocation forward")
net = ipaddress.ip_network("192.0.2.0/26")
show("192.0.2.0/26 .next_network()", net.next_network())
# A wider next_prefix snaps forward to the next correctly-aligned block.
show("...next block as a /25", net.next_network(next_prefix=25))
show("...next block as a /28", net.next_network(next_prefix=28))
show("2001:db8::/48 .next_network()", ipaddress.ip_network("2001:db8::/48").next_network())
# Handing out consecutive subnets without any arithmetic by hand.
cursor = ipaddress.ip_network("10.0.0.0/24")
handed_out = [str(cursor := cursor.next_network()) for _ in range(3)]
show("three /24s after 10.0.0.0/24", handed_out)
# --------------------------------------------------------------------------
# shlex.quote(force=True)
# --------------------------------------------------------------------------
def demo_shlex_force_quote() -> None:
section("shlex — quote(force=True) for uniform-looking command lines")
argv = ["cp", "notes.txt", "my backup/"]
show("default quoting", " ".join(shlex.quote(a) for a in argv))
show("force=True", " ".join(shlex.quote(a, force=True) for a in argv))
# --------------------------------------------------------------------------
# csv.Sniffer.sniff() rebuilt on trial parsing
# --------------------------------------------------------------------------
def demo_csv_sniffer() -> None:
section("csv — Sniffer.sniff() now trial-parses (and finds escapechar)")
escaped = 'a;b;c\r\nx\\;y;z;"w;w"\r\np;q;r\r\n'
dialect = csv.Sniffer().sniff(escaped)
show("delimiter", dialect.delimiter)
show("escapechar", dialect.escapechar)
show("quotechar", dialect.quotechar)
show("parsed rows", list(csv.reader(escaped.splitlines(), dialect)))
# A non-ASCII delimiter is allowed in the candidate list now, and a
# delimiter that only shows up inside quoted fields no longer wins the vote.
ideographic = "名前、年齢\r\n\"佐藤、A\"、42\r\n\"鈴木、B\"、37\r\n"
show("non-ASCII delimiter candidate",
csv.Sniffer().sniff(ideographic, delimiters="、,;").delimiter)
# --------------------------------------------------------------------------
# symtable.symtable() accepts an AST
# --------------------------------------------------------------------------
def demo_symtable_from_ast() -> None:
section("symtable — symtable() takes an AST, so no reparsing")
source = (
"def outer(a):\n"
" b = a + 1\n"
" def inner():\n"
" return b\n"
" return inner\n"
)
tree = ast.parse(source)
top = symtable.symtable(tree, "<demo>", "exec")
outer = top.lookup("outer").get_namespace()
show("top-level names", sorted(s.get_name() for s in top.get_symbols()))
show("outer's names", sorted(s.get_name() for s in outer.get_symbols()))
show("'b' is a cell variable in outer", outer.lookup("b").is_cell())
# --------------------------------------------------------------------------
# Frame objects support weak references
# --------------------------------------------------------------------------
def demo_frame_weakrefs() -> None:
section("frames — frame objects now support weak references")
captured: list[weakref.ref] = []
def annotate_me() -> bool:
# A debugger can hang data off a live frame without pinning it forever.
ref = weakref.ref(sys._getframe())
captured.append(ref)
return ref() is not None
show("resolvable during the call", annotate_me())
show("dead after it returns", captured[0]() is None)
# --------------------------------------------------------------------------
# encodings: modified UTF-7 for IMAP mailbox names
# --------------------------------------------------------------------------
def demo_utf7_imap() -> None:
section("encodings — utf-7-imap codec (RFC 3501 mailbox names)")
for mailbox in ["Übersicht", "受信箱", "Sent & Archived"]:
wire = mailbox.encode("utf-7-imap")
show(f"{mailbox!r} on the wire", wire)
assert wire.decode("utf-7-imap") == mailbox
# --------------------------------------------------------------------------
# codecs: every encoding iconv knows, plus an iconv: prefix
# --------------------------------------------------------------------------
def demo_iconv_codecs() -> None:
section("codecs — iconv-backed encodings (cp1133, iconv: prefix)")
try:
show("'ກ' (Lao ko) via cp1133", "ກ".encode("cp1133"))
except (LookupError, UnicodeError) as exc:
skip(f"cp1133 unavailable ({exc})")
try:
show("'Grüße' via iconv:latin1", "Grüße".encode("iconv:latin1"))
except (LookupError, UnicodeError) as exc:
skip(f"iconv: prefix unavailable ({exc})")
# --------------------------------------------------------------------------
# gzip.open(mtime=...) for reproducible archives
# --------------------------------------------------------------------------
def demo_gzip_mtime() -> None:
section("gzip — open(mtime=0) makes the output reproducible")
payload = b"reproducible builds\n" * 20
def write(directory: str, **kwargs: object) -> bytes:
path = Path(directory, "dist.tar.gz")
with gzip.open(path, "wb", **kwargs) as fh:
fh.write(payload)
return path.read_bytes()
with tempfile.TemporaryDirectory() as one, tempfile.TemporaryDirectory() as two:
pinned = [write(one, mtime=0), write(two, mtime=0)]
show("mtime=0 header (timestamp zeroed)", pinned[0][:8])
show("two mtime=0 writes are identical", pinned[0] == pinned[1])
show("default header (timestamp baked in)", write(one)[:8])
# --------------------------------------------------------------------------
# ctypes.util.wrap_dll_function()
# --------------------------------------------------------------------------
def demo_ctypes_wrap_dll_function() -> None:
section("ctypes — wrap_dll_function() reads argtypes/restype off annotations")
@wrap_dll_function(ctypes.pythonapi)
def PyObject_GetAttrString(
op: ctypes.py_object, attr: ctypes.c_char_p
) -> ctypes.py_object:
"""No body needed; the decorator supplies the call."""
show("PyObject_GetAttrString(3+4j, b'real')", PyObject_GetAttrString(3 + 4j, b"real"))
show("argtypes inferred", PyObject_GetAttrString.argtypes)
show("restype inferred", PyObject_GetAttrString.restype)
# --------------------------------------------------------------------------
# lzma: new BCJ filters for ARM64 and RISC-V
# --------------------------------------------------------------------------
def demo_lzma_bcj_filters() -> None:
section("lzma — FILTER_ARM64 and FILTER_RISCV branch/call/jump filters")
try:
import lzma
except ImportError as exc:
skip(f"this interpreter was built without liblzma ({exc})")
return
data = b"\x00\x01\x02\x03" * 1024
for name in ("FILTER_ARM64", "FILTER_RISCV"):
filter_id = getattr(lzma, name, None)
if filter_id is None:
skip(f"{name} not present in this build")
continue
chain = [{"id": filter_id}, {"id": lzma.FILTER_LZMA2, "preset": 6}]
try:
blob = lzma.compress(data, format=lzma.FORMAT_XZ, filters=chain)
except lzma.LZMAError as exc:
skip(f"{name} needs a newer liblzma ({exc})")
continue
show(f"{name} round-trips", lzma.decompress(blob) == data)
# --------------------------------------------------------------------------
# os.pidfd_getfd() (Linux 5.6+)
# --------------------------------------------------------------------------
def demo_pidfd_getfd() -> None:
section("os — pidfd_getfd() borrows a descriptor from another process")
if not hasattr(os, "pidfd_getfd"):
skip(f"Linux-only, and this is {sys.platform}")
return
pidfd = os.pidfd_open(os.getpid())
try:
duped = os.pidfd_getfd(pidfd, sys.stdout.fileno())
show("duplicated stdout as fd", duped)
os.close(duped)
finally:
os.close(pidfd)
# --------------------------------------------------------------------------
# Removals that finally landed
# --------------------------------------------------------------------------
def demo_removals() -> None:
section("removals — long-deprecated APIs are gone in 3.16")
import array
import asyncio
import functools
import shutil
import sysconfig
import tarfile
raises("array.array('u') -> use 'w'", lambda: array.array("u"))
raises("asyncio.iscoroutinefunction", lambda: asyncio.iscoroutinefunction(print))
raises("asyncio.get_event_loop_policy", lambda: asyncio.get_event_loop_policy())
raises("reduce(function=..., sequence=...)",
lambda: functools.reduce(function=lambda a, b: a + b, sequence=[1, 2, 3]))
raises("shutil.ExecError", lambda: shutil.ExecError)
raises("sysconfig.expand_makefile_vars",
lambda: sysconfig.expand_makefile_vars("$(CC)", {}))
raises("TarFile.tarfile attribute", lambda: tarfile.TarFile.tarfile)
raises("symtable.Class.get_methods", lambda: symtable.Class.get_methods)
def main() -> None:
print(f"\033[1mPython {sys.version}\033[0m")
if sys.version_info < (3, 16):
sys.exit(f"This tour needs Python 3.16+; got {sys.version.split()[0]}.")
demo_re_set_operations()
demo_re_unicode_properties()
demo_math_halfturns()
demo_bytesio_peek()
demo_zipfile_remove_repack()
demo_ipaddress_next_network()
demo_shlex_force_quote()
demo_csv_sniffer()
demo_symtable_from_ast()
demo_frame_weakrefs()
demo_utf7_imap()
demo_iconv_codecs()
demo_gzip_mtime()
demo_ctypes_wrap_dll_function()
demo_lzma_bcj_filters()
demo_pidfd_getfd()
demo_removals()
print()
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment