In this series looking at features introduced by every version of Python 3, we take a look at some of the new features added in Python 3.13. In this article we look at the improvements to the interactive interpreter and improved error reporting.
This is the 33rd of the 34 articles that currently make up the “Python 3 Releases” series.
It’s been awhile since I’ve been keep an eye on Python releases, so I thought it was high time I took a look at what’s new. The latest release, 3.13, was made final on the 7th October 2024, so it should be more than ready for me to dig into.
I often make the mistake of trying to cram too much into these posts, so this time around I’m going to try and spread things out a bit more—hopefully this means they don’t take so long to write, and also they’re bit easier to consume. This article, then, is going to focus on the first headline update, which is an improved interactive interpreter, and also some improvements to error reporting.
To give you a taste of what’s coming up in later articles, these comprise the rest of what I plan to cover:
In addition there are the usual swathe of standard library improvements, including a whole new module (dbm.sqlite3
) and some notable updates to asyncio
, os
, and pathlib
. There are smaller updates to around 40 more modules—although I may not choose to cover quite all of them—and quite a rew removals and deprecations as well.
Right now, however, let’s kick off this new series on Python 3.13 by looking at the updates to the interactive interpreter.
One of the aspects to Python which has always made it really convenient is the fact that you can fire up the interactive interpreter and test things out really easily. However, for all but the simplest one-liners, you quickly hit annoying limitations which make this more frustrating than it should be.
This situation has now been massively improved using code from the PyPy project, which was introduced over a large number of commits as you can see from the tracking issue. I’ll briefly go over the main enhancements.
As an aside, if you decide you don’t like the new interpreter, you can disable it by setting PYTHON_BASIC_REPL=1
. Why you’d want to do that I’ve no idea, I guess some people just really hate convenience.
One of the first things you’ll notice is that the interpreter now looks a little more colourful—at least assuming your terminal supports colours, and you haven’t turned them off by setting the NO_COLOR
environment variable. As with some other applications, FORCE_COLOR
enables colours even if the terminal doesn’t claim to support them, and if you just want to override this specifically for Python you can set PYTHON_COLORS
to either 0
or 1
instead, which takes precedence over the more generally supported variables.
The code you type isn’t syntax-highlighted, probably unsurprisingly, but the prompt and error messages are coloured for readability. Here’s an example of a traceback using the new colours:
Unfortunately I don’t think it’s possible to customise the colours, and having taken a quick look in the source code I can see colour names hard-coded in quite a few places so this doesn’t look like something that’s likely to change1.
Not much else to say about this one, really, but it certainly helps readability somewhat.
Now this one is probably the most impactful improvement for my use of the interactive interpreter. You can how edit multiline statements and function definitions in-place, just as you would with a basic text editor. This even works for history, so the next time you realise you’ve made a mistake in one or two lines of a function, you no longer need to re-enter the whole thing line by line, making corrections as you go. You can see an example of this below, where I’ve entered a function with some (fairly glaring) flaws, and then used up-arrow to recall the whole lot and correct it:
I can’t overstate how helpful this will be when doing ad hoc little tasks in the interactive interpreter, such as little data analysis tasks or trying new modules out to see how they work. This single feature alone is worth immediately upgrading to using 3.13 as your default interpreter in my opinion.
You can now invoke interactive interpreter commands like help
and exit
without needing to type brackets as if they were functions. This is a fairly minor improvement, and probably more helpful to people coming to Python for the first time.
One point I did notice, however, is that if you want to actually pass any arguments to help
then you still want the brackets. For example, help "pathlib"
still doesn’t work but help("pathlib")
does.
One other enhancement is that you can now press the F1
key to enter help mode, the same as if you just run help
. This interactive help is the same as running help()
in previous versions.
As well as being able to access your history by cycling through it with the cursor keys, you can now also see your entire history by pressing F2
. This pops up a concatenation of your entire history which you can page through using the less
utility.
This is pretty handy, but it does seem to be a simple read-only view—there doesn’t seem to be a way to select an item from this history to paste in, except using your operating system’s cut and paste features, of course. I also noticed that it can be a little hard to tell the difference between a series of single-line commands and a multiline command, although in practice it’s probably mostly obvious from context, so I don’t imagine this will be a major pain.
Finally, there’s a new paste mode that can be activated by pressing F3
. This disables the auto-indent and similar features which make it painful to paste large blocks of code. So if you need to paste a function definition or something else in, you just press F3
to enter the mode, do your pasting, and then press F3
to leave paste mode again.
The other changes I’d like to highlight in this article are a couple of some improvements to error reporting. This continues a trend in previous releases, where 3.10, 3.11 and 3.12 all had some enhancements in this area.
We’ve already seen that errors and tracebacks are now coloured in an earlier screenshot, and this already helps readability. There are also a couple of extra cases where errors are more helpfully reported.
The first is where you’ve accidentally named your script the same as a standard or third-party library that you’re trying to use. This can be confusing, as the current directory is usually the first item on sys.path
, so your own script is typically the highest-priority import, overriding the real module you’re presumably trying to use. In this circumstance, Python now produces a more helpful hint as to what the issue might be:
$ python graphlib.py
Traceback (most recent call last):
File "/Users/andy/graphlib.py", line 1, in <module>
import graphlib
File "/Users/andy/graphlib.py", line 4, in <module>
ts = graphlib.TopologicalSorter(graph)
^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: module 'graphlib' has no attribute 'TopologicalSorter'
(consider renaming '/Users/andy/graphlib.py' since it has the same name
as the standard library module named 'graphlib' and the import system
gives it precedence)
The second improvement is for cases where you’ve used the wrong keyword parameter for a function. You’ll still get a TypeError
, but now the interpreter will now make a sensible suggestion as to what you may have meant:
>>> round(1.234, digits=2)
Traceback (most recent call last):
File "<python-input-4>", line 1, in <module>
round(1.234, digits=2)
~~~~~^^^^^^^^^^^^^^^^^
TypeError: round() got an unexpected keyword argument 'digits'. Did you mean 'ndigits'?
There’s not a great deal else to say beyond what I’ve already outlined, to be honest. The interactive interpreter enhancements are very welcome, especially the multiline editing feature. I actually end up using the interactive interpreter for quite a lot more than you might expect, as I’m often having to do little ad-hoc data processing tasks and the like. Where some people might always reach for Excel, I tend to always reach for Python as my first port of call. Whilst I always think to myself I should probably use things like Jupyter Notebook, the fact that the Python interpreter is right there in the terminal with no setup and no fuss is really hard to beat for convenience.
The improved error-handling seems useful, and it’s nice to see this trend continue since they started it a few releases ago. These are the kind of improvements that never seem like a particularly big deal when you first see them, but make each exception just that little bit easier to track down and cut the friction of debugging by that little bit that, cumulatively, still makes your life noticeably better.
So that’s it for this article—I told you I was making them shorter! Probably now it’ll be annoyingly short, but them’s the breaks. Think of this as a light starter for the most likely more meaty dish coming up next time, where I’m going to drill into more technical aspects: the removal of the GIL, and the optional JIT compilation.
Well, OK, if you really don’t mind mucking around, you could go find _colorize.py
in your install and change the definitions of things like BOLD_MAGENTA
and GREEN
to be whatever ANSI codes you liked. But such fiddling around under the hood could easily come back to bite you later, so don’t tell anyone I suggested it. ↩