[go: up one dir, main page]

Python 3.14's best new features

Share
Copied to clipboard.
Trey Hunner smiling in a t-shirt against a yellow wall
Trey Hunner
6 min. read Watch as video Python 3.10—3.14

Python 3.14 is now officially released, and I'd like to share some of my favorite new features.

Very important but not my favorites

I'm not going to talk about the experimental free-threading mode, the just-in-time compiler, or other performance improvements. I'm going to focus on features that you can use right after you upgrade.

Python 3.14: now in color!

One of the most immediately visible features in Python 3.14 is all the color!

Syntax highlighting in the REPL

The Python REPL now includes syntax highlighting:

Python 3.14 REPL with syntax highlighting

When you type code into the Python REPL, it will be colorized as you type it.

Syntax highlighting in PDB

The Python debugger also gains syntax highlighting in 3.14:

PDB with syntax highlighting

So when you're debugging code, whether with a breakpoint call or some other way of entering PDB, your source code will now be colorized.

Color in unittest output

Also, if you've ever wished that Python's unittest module included nicely colored output like pytest, it does now. Now with unittest, failing tests are red and passing tests are green:

Colorized unittest output

And that's not all the color!

Color in argparse help text

The argparse module now also supports colorized help text:

Colorized argparse help text

Even more color

The calendar module now highlights the current date when you print a calendar.

calendar module with current date highlighted

Also, the json module's command line interface now displays JSON output in color by default:

Colorized json module output

My tiny contribution

Speaking of the json module's command-line interface, you can now run it with python-m json instead of python-m json.tool:

$ echo '[{"id": 100, "name": "Trey", "is_admin": true}]' | python -m json
[
    {
        "id": 100,
        "name": "Trey",
        "is_admin": true
    }
]

I actually made this tiny change, and it's the first code contribution I've made to Python. It is a very small one, but kind of a neat one.

Beginner-friendly error messages

Python 3.14 includes even more improvements to error messages, particularly syntax errors.

In previous versions of Python, you'd often get a generic SyntaxError if you misspelled a Python keyword like import:

>>> improt math
  File "<stdin>", line 1
    improt math
           ^^^^
SyntaxError: invalid syntax

But in Python 3.14, the error message now suggests the correct keyword instead:

>>> improt math
  File "<python-input-6>", line 1
    improt math
    ^^^^^^
SyntaxError: invalid syntax. Did you mean 'import'?

Python's error messages have improved a lot in the past few versions, and this is exactly the kind of change that makes Python even more beginner-friendly.

Tab completion for import statements

Another REPL improvement that I stumbled upon recently while teaching is tab completion of Python imports.

When you're in the REPL and you type the import statement followed by the start of a module name, and then you hit tab, it auto-completes the module name:

>>> import col<TAB>
collections    colorsys

This is especially handy for long module names like collections or datetime.

Standard library improvements

Python 3.14 also includes a bunch of little improvements to various standard library modules.

argparse suggests typo corrections

For example, the argparse module now includes a suggest_on_error argument that will suggest corrections when you make a typo in the choices options for a command-line argument:

import argparse

objects = ["rock", "paper", "scissors"]
parser = argparse.ArgumentParser(suggest_on_error=True)
parser.add_argument("player1", choices=objects)
parser.add_argument("player2", choices=objects)
args = parser.parse_args()

if args.player1 == args.player2:
    print("It's a tie.")
else:
    index = objects.index(args.player1)
    if args.player2 == objects[index-1]:
        print("Player 1 wins")
    else:
        print("Player 2 wins")

Here we're misspelling scissors, and Python is showing us the option that it thinks we meant to type, spelled correctly:

$ python rock.py rock scisors
usage: rock.py [-h] {rock,paper,scissors} {rock,paper,scissors}
rock.py: error: argument player2: invalid choice: 'scisors', maybe you
meant 'scissors'? (choose from rock, paper, scissors)

Now pathlib has copy and move methods!

One of my favorite features in Python 3.14 is that pathlib.Path objects now have copy and move methods.

Before 3.14, if we wanted to copy or move a file, we had to use Python's shutil module:

>>> from pathlib import Path
>>> import shutil
>>> source = Path("readme.txt")
>>> destination = Path("readme.md")
>>> shutil.move(source, destination)
PosixPath('readme.md')

But in 3.14, you can just use the move method directly on the Path object:

>>> from pathlib import Path
>>> source = Path("readme.txt")
>>> destination = Path("readme.md")
>>> source.move(destination)
PosixPath('readme.md')

There's also a move_into method for moving a file into a directory, as well as a copy method and a copy_into method.

A new info attribute for pathlib.Path

The Path class also gained a new info attribute that caches information about whether a path represents an existing file or directory:

>>> source.info.is_file()
True
>>> source.info.is_dir()
False

Easier date and time parsing with datetime

In 3.14, you can also now parse a date string or a time string directly into a datetime.date object or a datetime.time object.

In previous versions of Python, you either needed to use datetime.datetime.strptime, and then extract the date or parse the string manually:

>>> from datetime import datetime
>>> date_string = "2026-03-14"
>>> datetime.strptime(date_string, "%Y-%m-%d").date()
datetime.date(2026, 3, 14)

But in 3.14, you can just use date.strptime directly. The date and time classes now have their own strptime method that works the same way as the datetime class's strptime method:

>>> from datetime import date
>>> date_string = "2026-03-14"
>>> date.strptime(date_string, "%Y-%m-%d")
datetime.date(2026, 3, 14)

Support for uuid7: UUIDs sortable by creation time

Python's uuid module now includes uuid6, uuid7, and uuid8 functions.

If you're starting a new project and you need to generate UUIDs, I'd recommend taking a look at uuid7:

>>> from uuid import uuid7
>>> uuid7()
UUID('0199b0ac-022f-72fb-9350-af229cb89256')
>>> uuid7()
UUID('0199b0ac-10b0-7628-8c45-96ab90bcc870')
>>> uuid7()
UUID('0199b0ac-1847-75d5-b17a-298cc953ea99')

It has all the pseudo-randomness benefits of uuid4, but the generated UUIDs are also ordered by their creation time.

The sortability of these UUIDs can be really useful for things like database primary keys.

Cleaner multi-exception catching

Have you ever written code that catches multiple exception types?

In previous Python versions, you needed to wrap those exception types in parentheses:

>>> value = "3"
>>> try:
...     int(value)
... except (ValueError, TypeError):
...     print("Invalid number")
...
3

In 3.14, you can omit the parentheses and the code still works:

>>> value = "3"
>>> try:
...     int(value)
... except ValueError, TypeError:
...     print("Invalid number")
...
3

This earlier version was a holdover back from the Python 2 days.

Concurrency improvements

3.14 also includes some interesting improvements for concurrent and parallel programming.

Multiple interpreters

concurrent.futures now includes an InterpreterPoolExecutor, which spawns a new Python interpreter in a separate thread, but in the same process:

>>> from concurrent.futures import InterpreterPoolExecutor

Each interpreter has its own global interpreter lock, meaning they can each use separate CPUs if needed.

So you kind of get the best of both worlds between multi-threading and multi-processing.

asyncio introspection

Also, if you've ever needed to inspect running asyncio tasks, well, you can now do so from the command-line.

$ python -m asyncio pstree PID

External debugger interface

3.14 now also has an external debugger interface that allows you to safely attach debuggers to running Python processes, so you can start a breakpoint from a process that's already running.

$ python -m pdb --pid 49611

T-strings (template strings)

The last thing I'd like to talk about in 3.14 is a new syntax, t-strings:

>>> name = "World"
>>> template = t"Hello {name}"

The "T" in t-string stands for template.

Unlike f-strings, they don't actually make strings. They make Template objects:

>>> template
Template(strings=('Hello ', ''), interpolations=(Interpolation('World', 'name', None, ''),))

These Template objects allow a utility to pre-process the interpolations within the template, in whatever way they'd like, before actually making a string.

Keep in mind that f-strings are still the go-to string formatting tool.

T-strings are really useful for library authors, in particular. So you might find yourself soon using a library in Python where a t-string is required and not an f-string, because some pre-processing needs to be done in some of your data.

You can find my video about t-strings here.

Try out Python 3.14 yourself

If you're excited about syntax highlighting, the new pathlib methods, or anything else that I mentioned, go install Python 3.14 and try it out.

And if you want to see everything that's new, go to the "What's New in Python 3.14" page in the documentation.

A Python Tip Every Week

Need to fill-in gaps in your Python skills? I send weekly emails designed to do just that.