Behavior Quirks

Backscape Key Doesn’t work in Python REPL

If you attempt to run python and the backspace key doesn’t erase characters or the arrow keys don’t work as expected, this is because the executable can’t find the terminfo database.

When you type a special key like the backspace key, this is registered as a key press. There is special software (typically readline or libedit) that most interactive programs use that intercepts these special key presses and converts them into special behavior, such as moving the cursor back instead of forward. But because computer environments are different, there needs to be some definition of how these special behaviors are performed. This is the terminfo database.

When readline and libedit are compiled, there is typically a hard-coded set of search locations for the terminfo database baked into the built library. And when you build a program (like Python) locally, you link against readline or libedit and get these default locations for free.

Because python-build-standalone Python distributions compile and use their own version of readline/libedit and because the build environment is different from your machine, the default search locations for the terminfo database built into binaries distributed with this project may point to a path that doesn’t exist. The terminfo database cannot be located and readline/libedit do not know how to convert special key presses to special behavior.

The solution to this is to set an environment variable with the location of the terminfo database.

If running a Debian based Linux distribution (including Ubuntu):

$ TERMINFO_DIRS=/etc/terminfo:/lib/terminfo:/usr/share/terminfo

If running a RedHat based Linux distribution:

$ TERMINFO_DIRS=/etc/terminfo:/usr/share/terminfo

If running macOS:

$ TERMINFO_DIRS=/usr/share/terminfo

e.g.:

$ TERMINFO_DIRS=/etc/terminfo:/lib/terminfo:/usr/share/terminfo install/bin/python3.9

The macOS distributions built with this project should automatically use the terminfo database in /usr/share/terminfo. Please file a bug report if the macOS distributions do not behave as expected.

Tcl/tk Support Files

Python functionality using tcl/tk (such as the tkinter or turtle modules) requires loading .tcl support files from the filesystem. If these support files cannot be found, you’ll get an error like _tkinter.TclError: Can't find a usable init.tcl in the following directories:.

Distributions produced from this project contain tcl/tk support files. The paths to these files in the extracted distribution are advertised in the PYTHON.json file.

When tcl is initialized by Python, Python and tcl attempt to locate the .tcl support files. If the tcl<X.Y>/init.tcl file cannot be found, an error occurs.

But the mechanism for finding the .tcl files varies by platform.

On all platforms, if the TCL_LIBRARY environment variable is set, it will be used to locate the .tcl support files. This environment variable is processed by tcl itself and is documented at https://wiki.tcl-lang.org/page/TCL_LIBRARY.

On Windows, CPython will attempt to locate the .tcl support files in well-defined directories. The C code performs the equivalent of the following:

import os
import sys

def get_tcl_path():
    # e.g. sys.prefix/tcl/tcl8.6
    p = os.path.join(sys.prefix, "tcl", "tcl<X.Y>")
    if os.path.exists(p):
        return p

    return None

If Python’s code can find the support files in the well-defined location, it calls into the tcl C API and defines the tcl_library variable to the found path.

The most robust way to ensure Python/tcl can find the .tcl support files is to define TCL_LIBRARY to the path to the .tcl files present in the extracted Python distribution. It is possible to define this environment variable from within Python. But it must be done before running any Python code in the tkinter module. The following example should work on Linux and macOS distributions:

import os
import sys

os.environ["TCL_LIBRARY"] = os.path.join(os.path.dirname(sys.executable), "..", "lib", "tcl8.6")

import turtle

If you don’t set TCL_LIBRARY on Linux and macOS, the default search mechanics implemented by Tcl are used. These may pick up .tcl files from a location outside the Python distribution. This may just work. This may fail fast. Or it could result in undefined behavior. For best results, forcefully point Tcl at the .tcl files from the Python distribution produced by this project.

On Windows, explicitly setting TCL_LIBRARY is not required as the default install layout of this project’s Python distributions allows CPython’s filesystem probing code to find the .tcl files. As long as the files from python/install/tcl are present (in a tcl directory under the directory where the python.exe is), things should just work.

For reference, PyOxidizer’s approach to this problem is to copy all the .tcl files from the Python distribution into an install location. At run time, the TCL_LIBRARY environment variable is set from within the process before the Python interpreter is initialized. This ensures the .tcl files from the Python distribution are used.

Linking Static Library on macOS

Python 3.9+ makes use of the __builtin_available() compiler feature. This functionality requires a symbol from libclang_rt, which may not be linked by default. Failure to link against libclang_rt could result in a linker error due to an undefined symbol ___isOSVersionAtLeast.

To work around this linker failure, link against the static library libclang_rt.<platform>.a present in the Clang installation. e.g. libclang_rt.osx.a. You can find this library by invoking clang --print-search-dirs and looking in the lib/darwin directory under the printed libraries directory. An example path is /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/12.0.0/lib/darwin/libclang_rt.osx.a.

A copy of the libclang_rt.<platform>.a from the Clang used to build the distribution is included in the archive. However, it isn’t annotated in PYTHON.json because we’re unsure if using the file with another build/version of Clang is supported. Use at your own risk.

See https://jonnyzzz.com/blog/2018/06/05/link-error-2/ and https://jonnyzzz.com/blog/2018/06/13/link-error-3/ for more on this topic