Warning

Most of the documentation was written prior to version 0.5 and needs to be updated. This work has now started for version 0.7 and we aim to have it completed before version 0.8 is available.

Some thoughts on the design of friendly

The following are thoughts on the design of this project. More information about various design choices are scattered throughout this documentation. If you can think of better design choices, please feel free to file an issue.

Original purpose

friendly’s primary purpose is to make it easier for beginners and/or for people that have limited knowledge of English to understand what caused a program to generate a traceback.

A secondary goal is to help them learn how to decipher a normal Python traceback and use the information provided by Python to understand what went wrong and how to fix it.

Revised purpose

As friendly was developed, we found that going beyond providing an explanation for the traceback printed by Python was potentially very useful. This is something that is currently done by Thonny which, in some cases, even attempts to identify more than one possible cause giving rise to an exception, as well as ordering them in order of likelihood, based on its own analysis of the code.

As a concrete example, in the image below, Thonny shows the normal Python traceback [1], and offers some additional explanations [2], parts of which can be hidden or revealed by clicking on a button.

_images/thonny.png

For now, friendly usually tries to identify the most likely cause of the exception, but makes some additional suggestions in a few relatively rare cases.

Gradual reveal

Initially, it was thought that the information provided by friendly should be shown all at once. As we accumulated more and more cases, we realised that this could yield a huge amount of material which could be rather daunting for beginners. Eventually, this lead to the approach of using a REPL whenever possible so that the user could get some small amount of information at a time by entering what(), why() or where().

As part of the gradual reveal, the traceback shown to the user shows an added “hint” which attempts to summarize in a single sentence a possible cause or remedy to the exception that was raised. This has been inspired in parts by the DidYouMean-Python (aka BetterErrorMessages) project.

Summary of design choices

Scattered throughout this documentation, we added notes about choices that were made in designing friendly and friendly-traceback. These are are listed below.

Design Choice: automatic installation for Jupyter

Anyone using friendly in a Jupyter environment does so because they want the traceback information to be processed by friendly. For this reason, friendly is automatically installed when friendly.jupyter is imported, instead of requiring users to call install() after the import statement.

(The original entry is located in C:\Users\Andre\github\friendly-docs\source\jupyter.rst, line 64.)

UI Design Choice: buttons instead of function calls for Jupyter

friendly aims to be as easy to use as possible for beginners. Having them clicking on buttons to reveal some additional information when needed is more user-friendly than requiring them to type in and execute some function calls.

(The original entry is located in C:\Users\Andre\github\friendly-docs\source\jupyter.rst, line 74.)

Design Choice: only message shown by default for Jupyter

Rather than showing the friendly traceback by default, only the exception message is shown in addition to the More ... button.

(The original entry is located in C:\Users\Andre\github\friendly-docs\source\jupyter.rst, line 85.)

Design Choice: Jupyter font family with Rich

Instead of using the Jupyter default, Rich specifies a set of possible fonts for its output. As a result, the apparent size of the fonts, at least on Windows, appears to be larger when using Rich than without. To avoid this, I override the default from Rich to give a more consistent look and feel.

(The original entry is located in C:\Users\Andre\github\friendly-docs\source\jupyter.rst, line 217.)

UI Design Choice: Red means exception or error

For both themes, I have chosen to use the colour red only for exception names, such as SyntaxError, for traceback headings, and for headings showing where an exception occurred.

(The original entry is located in C:\Users\Andre\github\friendly-docs\source\themes.rst, line 26.)

Note

The sections shown below will likely be moved elsewhere

About Warnings

In addition to generating exceptions, Python can provide some Warnings to users. For now, these are simply silenced but we would like to consider including them in the information provided by friendly.

Location of the exception

While a Python traceback includes the information from all the frames that were involved, friendly focus on the first and last frame, as these are more likely to contain the relevant information to the user.

Variable information

friendly include the value of all known variables found on the lines of code shown; earlier versions, such as that shown in the example below, included only variable information from a single line of code. In the example below (IndexError), this information [1] together with the reminder [2] and the code from the offending line [3] give enough information to properly diagnose the error.

IndexError traceback

In some cases, the value of some variables could, in principle, yield an enormous amount of text. To avoid this situation, we truncate any value that exceeds a predetermined length. However, when we do so, if the variable has a __len__ attribute, we show its value as it can sometimes be helpful in identifying the problem.

IndexError traceback

SyntaxError: invalid syntax

For SyntaxError, Python often offers very little useful information beyond where it finally identified that a SyntaxError occurred. Sometimes, the offending code actually occurred well before: for example, an open bracket might have been inserted many lines prior to where the absence of the corresponding closing bracket was noted to cause an error.

For SyntaxError, friendly does a fairly simple analysis of the code and tries to identify a single cause which produced the error.

SyntaxError traceback

Localization

It is possible to translate almost all the text provided by friendly.

When using Python, it is customary to determine which language should be used to provide translations by a call to locale.getdefaultlocale(). In an earlier version, we did this but have decided to use English as the default and let the user (which could be another program that imports friendly) decide what language should be used.

The information provided by locale.getdefaultlocale() includes not only a language code, but information about a specific region as well. For example, on my computer, this is fr_CA. As far as I can tell, gettext does not have a graceful fallback from the specific (fr_CA) to the generic (fr); it does have the option of having a fallback to the version hard-coded in a program.

What we have done is including the possibility of loading a specific translation with no fallback. If an exception is raised, we then reduce the length of the language code to the first two characters, and attempt to load the translation while using gettext’s option of falling back to the hard-coded version if needed.

Important

By default, we should perhaps ask translators to provide generic 2-letter code versions for translations, so that a better fallback than the default English version could be found. See the related open question above, as to whether or not this should be provided in addition to any region specific version.

Other similar projects

Many other projects do some enhanced traceback formatting, however none that we know of aim at

  1. making tracebacks easier to understand by beginners

  2. translating traceback information.

Still, there is much to learn by looking at what others are doing. The following is an incomplete list of projects or modules to look at:

Todo

Add explanation about:

  • variable shown

  • how it works (especially for analysis of SyntaxError cases)

  • use Mu to show numbered prompt