Python 3.13: What's New

Last updated October 21st, 2024

One more year has passed, and Python 3.13 is out! It was released on October 7th, 2024. I'm sure you're as excited as we are to see what it brings. This article will take a look into the novelties that will make your Python development even better:

  1. Improved interactive interpreter - e.g., multiline editing with history preservation
  2. No-GIL, free-threaded Python - utilize all available processing power by running threads in parallel on all available CPUs
  3. Improved error messages - more helpful error messages for common mistakes
  4. Improved typing - typing.ReadOnly to mark read-only attributes inside typed dicts
  5. Improved warnings - new warnings.deprecated() decorator to emit warnings at runtime and type checking if a deprecated object/function/overload is used
  6. Improved argparse - a new deprecated argument was added to support deprecation of command-line options, positional arguments, and subcommands

Contents

Installing Python 3.13

If you have Docker, you can quickly spin up a Python 3.13 shell to play around with the examples in this article like so:

$ docker run -it --rm python:3.13

Not using Docker? We recommend installing Python 3.13 with pyenv:

$ pyenv install 3.13.0

You can learn more about managing Python with pyenv from the Modern Python Environments - dependency and workspace management article.

Improved Interactive Interpreter

Have you ever tried to exit the REPL with exit, but it didn't work? Well, now you can! The new REPL now supports calling REPL-specific commands just by name -- no need to call them as functions anymore.

Python < 3.13:

>>> exit()

Python >= 3.13:

>>> exit

Obviously, the old way of calling them as functions still works.

Another nugget of goodness is multiline editing with history preservation. You can now edit multiline statements in the REPL without losing the history of the previous lines. This is a great improvement for debugging and testing code snippets.

Using history with Python < 3.13

Defining function:

>>> def foo():
...     print('Hello')

Going through history with up arrow:

>>>     print('Hello')
>>> def foo():

Using history with Python >= 3.13

Defining function:

>>> def foo():
...     print('Hello')

Going through history with up arrow:

>>> def foo():
...     print('Hello')

Another thing worth mentioning is that prompts and tracebacks are now colored by default. That makes everything much more readable.

More info:

  1. Official docs
  2. PyREPL -- New default REPL written in Python

No-GIL, Free-threaded Python

As already described in Python 3.12: What's New, the Global Interpreter Lock (GIL) is a mutex (or lock) that protects access to Python objects, preventing multiple threads from executing Python bytecodes at once. This lock is necessary mainly because CPython's memory management is not thread-safe. The GIL is controversial because it prevents multithreaded CPython programs from taking full advantage of multiprocessor systems in certain situations.

Well not anymore! CPython finally comes with an option for execution with the GIL disabled. For now, this is only experimental. To make use of it, you'll need to install a special version of Python. Anyhow, with that, you can utilize all available processing power by running threads in parallel on all available CPUs.

More info:

  1. PEP 703
  2. Official docs

Improved Error Messages

As with the last three Python releases, Python 3.13 comes with improved error messages. This time, they've improved error messages for some of the common mistakes. Also, the interpreter now uses color when showing errors inside the terminal. This will make debugging easier and more efficient.

From now on, you'll get very useful error message if your module name will collide with a standard library module:

# math.py

import math

print(math.sqrt(25))

Python < 3.13:

$ python math.py

AttributeError: partially initialized module 'math' has no attribute 'sqrt'
(most likely due to a circular import)

Python >= 3.13:

$ python math.py

AttributeError: module 'math' has no attribute 'sqrt'
(consider renaming 'math.py' since it has the same name as the standard library
module named 'math' and the import system gives it precedence)

Another example is when you provide an incorrect keyword argument to a function -- the interpreter will now try to suggest the correct one:

#  hello.py

def say_hello(*, full_name):
    return f"Hello, {full_name}!"

print(say_hello(fullname="Python"))

Python < 3.13:

$ python hello.py

TypeError: say_hello() got an unexpected keyword argument 'fullname'

Python >= 3.13:

$ python hello.py

TypeError: say_hello() got an unexpected keyword argument 'fullname'.
Did you mean 'full_name'?

Review the official docs for more info.

Also, check out the following resources if you're curious about the error message improvements to the previous three versions of Python:

  1. Python 3.10: Clearer Error Messages
  2. Python 3.11: Better Error Messages
  3. Python 3.12: Better Error Messages

Improved Typing

Yet again, some typing improvements have been made in Python 3.13. Let's look at the most interesting one: The new typing.ReadOnly type. It can be used to mark read-only attributes inside typed dicts. This is a great way to ensure that some key is not modified by mistake.

# song.py

from typing import ReadOnly, TypedDict

class Song(TypedDict):
   name: ReadOnly[str]
   band: ReadOnly[str]
   number_of_plays: int

def count_plays(s: Song) -> None:
   s["number_of_plays"] += 1  # OK
   s["name"] = "New Name"  # Error
$ python -m mypy song.py

song.py:11: error: ReadOnly TypedDict key "name"
TypedDict is mutated  [typeddict-readonly-mutated]

More info:

  1. Official docs
  2. PEP 705

Improved Warnings

Python 3.13 comes with the new warnings.deprecated() decorator. It can be used to emit warnings at runtime if a deprecated object/function/overload is used. This is a great way to inform users that some part of the code is deprecated and should be replaced with a new one.

# integration.py

from warnings import deprecated

@deprecated("Use NewIntegration instead")
class LegacyIntegration:
    pass

class NewIntegration:
    pass

LegacyIntegration()
$ python integration.py

integration.py:12: DeprecationWarning: Use NewIntegration instead
  LegacyIntegration()

Type checkers will also complain when an object is decorated with the warnings.deprecated() decorator.

More info:

  1. Official docs
  2. PEP 702

Improved argparse

The last improvement we'll cover in this article is the new deprecated argument in argparse. It can be used to support deprecation of command-line options, positional arguments, and subcommands. This is a great way to inform users that some part of the CLI is deprecated and should be replaced with a new one.

# cli.py

import argparse

parser = argparse.ArgumentParser()
parser.add_argument("--old", help="Old option", deprecated=True, required=False)
parser.add_argument("--new", help="New option", required=False)

args = parser.parse_args()
$ python cli.py --old foo

cli.py:  warning: option '--old' is deprecated

Review the official docs for more info.

Conclusion

Like with Python 3.12, 3.13 doesn't seem like a huge release; however, many of the improvements are very welcome. For beginners, the improved error messages will make debugging easier. More advanced users should be sure to make use of the new warnings.deprecated() decorator. Python 3.14 is coming next year. Until then, happy coding!

Jan Giacomelli

Jan Giacomelli

Jan is a software engineer who lives in Ljubljana, Slovenia, Europe. He is a Staff Software Engineer at ren.co where he is leading backend engineering efforts. He loves Python, FastAPI, and Test-Driven Development. When he's not writing code, deploying to AWS, or speaking at a conference, he's probably skiing, windsurfing, or playing guitar. Currently, he's working on his new course Complete Python Testing Guide.

Share this tutorial

Featured Course

Serverless Apps with FastAPI, DynamoDB, and Vue

Build serverless applications with FastAPI and Vue that run on AWS.

Featured Course

Serverless Apps with FastAPI, DynamoDB, and Vue

Build serverless applications with FastAPI and Vue that run on AWS.