Python π (3.14) beta 1 is out, which means the features are locked in. The big feature this time around are template strings, lots more color (including syntax highlighting in the REPL!), remote debugging, deferred evaluation of annotations, and the usual error message and performance improvements.
Template strings
Possibly the most noticeable addition to Python π is template strings. These
are basically identical to f-strings, except they don’t render into a string
automatically. So while f"hello {world}"
becomes a string, t"hello {world}"
is a new type, string.templatelib.Template
. You can write functions that take
this new type and process it. Here’s an simple example that implements
f-strings (but skips handling conversion and format specifiers for clarity):
from string.templatelib import Template, Interpolation
def to_string(template: Template) -> str:
return "".join(
item.value if isinstance(item, Interpolation) else item for item in template
)
world = "world"
assert f"hello {world}" == to_string(t"hello {world}")
Since this is a new type, you can check for in it APIs. It is not lazy, though you can
manually build in laziness by evaluating callables, for example. You have
access to the original value
, the expression
string that gave that value,
along with the conversion
and format_spec
options. There are some proposed
additions to standard library based on it, but those are deferred to 3.15.
Forcing a Template
can be useful. If your API takes only template strings,
you can make sure substitutions are sanitized. Unfortunately, that’s a backward
incompatible change on an existing API. I expect initial usage will simply allow
both, with template strings getting automatic sanitation.
REPL
Color all the things!
The new REPL added color in 3.13, and now we are getting it in many more
places. Syntax highlighting is now supported in the REPL. The unittest
,
(new) json
, and calendar
command lines now sport colors. And argparse
supports color too, with a new color
parameter, enabled on the stdlib modules
too! Argparse also gets a new suggest_on_error
parameter.
Remote debugging
You can now connect pdb from one process to another one, using the process ID
with -p PID
. This is enabled by a new sys.remote_exec()
function, which
lets you execute code on an running interpreter in a different process. Other
debuggers and profilers can take advantage of this too. Various ways to disable
this have also been added; you can even build Python with this disabled.
There’s also a new addition to the python -m asyncio
module; new commands ps PID
and ptree PID
, which allow you to inspect the asyncio state of a running
Python process.
More autocompletion
Modules now autocomplete with <tab>
(though not attributes inside modules).
Error messages
Many improvements have been made to error messages. These are:
- “Did you mean” for Python keyword typos
- Argument unpacking length hints
elif
followingelse
dedicated message- Highlighting for statements in the wrong place
- Better incorrectly closed string message
- Incompatible string prefix explanation
- Better messages for incompatible
as
targets - Better JSON serialization errors with added notes
Faster CPython
There are some speedups from the Faster CPython team:
- A new opt-in tail call interpreter (when built with LLVM 19+), 3-5% faster, up to 30% faster
- Official CPython builds now have JIT enabled as a runtime opt-in
There’s now a new InterpreterPoolExecutor
, which is an easy way to use subinterpreters.
Start up time for a handful of modules has been improved, like subprocess
,
tomllib
, and asyncio
.
Using pdb from the CLI or with breakpoint()
now uses the much faster
sys.monitoring
backend. The popular coverage
library can now do branch
measurements with sys.monitoring
; applying this to the packaging repo cut the
test time from 55 seconds to 35 seconds.
Static Typing
The big update here is PEP 649/[PEP 749][], deferred evaluation of type
annotations. This brings most of the features we used from __future__ import annotations
with less runtime impact, including (most of the) speed, new
constructs, and forward references.
This comes with a new library if you need to use the annotations at runtime,
annotationlib
. This lib has methods to get annotations as strings, values, or
forward references. This change should be mostly transparent, and tools like
typing.get_origin
and typing.get_args
continue to work correctly.
The only other change is the unification of types.UnionType
and
types.Union
, which means that the old-style Union[A, B]
and the new-style
A | B
unions are identical. Union
is now valid in isinstance
.
Smaller features:
memoryview
is now generic type
Typing development is still active, there are several in-progress PEPs related to typing, but they were deferred to Python 3.15.
Compression
A new namespace, compression
, was added, and all the existing compression
libraries are now available inside it. So import tarfile
can now be written
from compression import tarfile
. Similarly with lzma
, bz2
, gzip
, and
zlib
. Don’t worry, the old names are still around.
There’s now a new compression library, as well: zstd
, with a similar API to
lzma
and bz2
.
Language
You no longer have to use parentheses around exceptions in the except
statement. This was a holdover from Python 2, where leaving them off did
something completely different.
try:
f()
except Err1, Err2:
pass
Other features
Several new methods made it to pathlib.Path
, for copying and moving files and
directory trees. There’s also a new .info
attribute with path information,
filled when using iterdir()
.
Other features include:
map()
now supportsstrict=True
, likezip()
super()
is now pickleable and copyable- A few small updates to regex, such as
\z
added and\B
matches an empty string python -c
now dedents code passed to itast.compare()
compares two ASTsast
nodes now have more descriptive reprs (finally!)- You can now terminate/kill workers in ProcessPoolExecutor
- You can specify a max buffer size for
Executor.map
fnmatch.filterfalse()
for excluding matchesfunctools.Placeholder
to hold a place for positional argumentsinspect.ispackage()
addedio.Reader
andio.Writer
protocols addedpython -m json
added (with color!)os.reload_environ()
reloads the environment- More assert methods for unittest.
- New arguments to better handle
file:
URls inurllib
.
Removals and deprecations
The ability to call control flow altering statements inside a finally block is
now a SyntaxWarning
. This was considered confusing for some reason (though I
believe this was clear with a good a mental model of how exceptions and finally
work). Regardless, it’s now a warning. Pluggy (used by pytest) currently uses
this, so you’ll see warning in your tests until it’s updated.
Some other deprecations:
- The
asyncio
policy system PurePath.as_uri
(usePath.as_uri()
instead)os.popen
andos.spawn*
are soft deprecated (no removal timeline)
Some things were removed, but most of them should have been producing warnings
for a while. Remember to run your tests with warnings as errors turned on!
These removals are things like ast
classes that were replaced by
ast.Constant
in 3.8, some asyncio stuff, importlib.abc stuff, and some
pkgutil stuff.
The remove of __package__
(replaced by __spec__.parent
) was delayed to 3.15.
Other Developer changes
Other features include:
- iOS testing and output improvements
- Three argument
pow()
now considers__rpow__
- Complex arithmetic update to match C99
-X importtime=2
added, tracks cached modules too- Slots are not wrapped if not overridden (small perf improvement)
- Default multiprocessing method on Linux is now
forkserver
instead offork
- Some ctypes structure updates
- Instances are reused in pdb, keeping instance data across breakpoints
- Pickle default protocol is now 5.
- Windows now include ABIFLAGS in
sysconfig.get_config_Vars()
.
And CAPI had a lot of additions to clean it up and make it easier to use, like
PyUnicodeWriter_*
, iteration and conversion additions, integer API, checking
for immortality, and more. There’s also a new PyConfig_*
and PyInitConfig_*
C API for getting and setting runtime configuration.
Final Words
If you are using GitHub Actions, the new and best way to add 3.14 is to use this:
- uses: actions/setup-python@v5
with:
python-version: "3.14"
allow-prereleases: true
This works in a matrix, etc. too. If you want to try out free-threaded Python,
that’s either "3.14t"
or enable the free-threaded option.
So far, it’s been pretty easy to adopt. There’s a deprecation warning coming from pluggy that shows up in pytest in some cases, nox works out of the box. Tox has some issues that should be worked out soon. pybind11 has four failures we still need to solve. If you don’t depend on a binary package (like numpy), try it out!