Yet another Python 3.12 release highlight summary

I'm doing a fair bit of Python lately, and thus need to stay up-to-date with the language releases. Some days ago Python 3.12 was released. The release note overview, and the detailed release notes in particular, are worth a read. Other articles I can recommend are What didn't make the headlines and Static typing improvements.

  • PEP-701 formalisation of f-strings will lift some limitations, like nested f-strings, use of the quotation delimiter of the f-strings inside an expression, comments in multi-line f-strings. Examples are listed in the PEP.
>>> m = {'a': 'b'}
# This wasn't possible in prior versions.
>>> f'm={m['a']}'
'm=b'

Since Python3.8 there's also a debug format in f-strings, that is used when post-fixing a variable with =:

>>> f'{m=}'
"m={'a': 'b'}"
# or as nested f-string
>>> f'Debug output {f'{m=}'}'
"Debug output m={'a': 'b'}"
  • PEP 688 exposes the Buffer protocol (interface) to Python code. This was limited to CPython before. I'm not sure what this is useful for.
  • PEP 669 introduces a new, low-overhead debugging and profiling API.
  • PEP 684 adds support for isolated sub-interpreters with separate GILs, and is only available through the C-API. So, not of any particular use for me.
  • Improved error messages.
  • Large and small performance improvements, enhanced Linux perf support.
  • PEP 695 adds a new type annotation syntax.

More compact way to create generic functions and classes:

def max[T](args: Iterable[T]) -> T:
	...

Type aliases can be generic:

type Point[T] = tuple[T, T]
  • PEP 698 adds a @override decorator to indicate methods that should be overridden in a sub-class.
  • The sqlite3 module received a command-line interface. This might be useful as a simple SQLite interface on systems that only have Python available.
# python -m sqlite3 [-h] [-v] [filename] [sql]
$ python -m sqlite3
sqlite3 shell, running on SQLite version 3.39.5
Connected to a transient in-memory database

Each command will be run using execute() on the cursor.
Type ".help" for more information; type ".quit" or CTRL-D to quit.
sqlite>
  • The uuid package receive a CLI as well. Now there's an easy cross-platform way to create UUIDs!
# python -m uuid [-h] [-u {uuid1,uuid3,uuid4,uuid5}] [-n NAMESPACE] [-N NAME]
$ python -m uuid -u uuid4
6ebeb0e2-6257-4eba-a638-97a20a222bed
  • itertools.batched will be very useful for splitting workloads into evenly sized chunks.
>>> from itertools import batched
>>> files = [f'{i}.txt' for i in range(1,11)]
>>> list(batched(files, 3))
[('1.txt', '2.txt', '3.txt'), ('4.txt', '5.txt', '6.txt'), ('7.txt', '8.txt', '9.txt'), ('10.txt',)]
  • There's now pathlib.Path directory traversal function pathlib.Path.walk.
  • os.path.splitroot splits a path into a three tuple of (drive, root, tail) where drive is always empty on POSIX systems.
# POSIX
>>> os.path.splitroot(os.getcwd())
('', '/', 'Users/someuser')
# Windows
('C:', '\\', 'Users\\someuser\\AppData\\Local\\Programs\\Python\\Python312')
# module.py
def g():
    return 21*2

def f():
    breakpoint()
    return g()

f()

Here's how to show the return value:

$ python -m module
> /private/var/folders/mc/3ssj7_c51dv76d6ll79s5l3w0000gn/T/tmp.xHdeQkAESD/module.py(6)f()
-> return g()
(Pdb) n
--Return--
> /private/var/folders/mc/3ssj7_c51dv76d6ll79s5l3w0000gn/T/tmp.xHdeQkAESD/module.py(6)f()->42
-> return g()
(Pdb) $_retval
42
(Pdb)