How to Inject Custom Code on Python Interpreter Startup
Ruby and Javascript both allow you to execute arbitrary code before your main application runs without modifying your application code. My most common use case is executing monkey patches to make it easier to debug an application.
However, this is not possible with Python. This makes using development tools like pretty-traceback challenging. In my case, I work across a bunch of reports, and injecting six lines of Python into each repo is annoying (especially since many open-source repos don’t have a concept of a PYTHON_ENV
, so there’s not an easy way to disable it in prod).
.pth
File
The Magic In any engineering discipline, the more you understand the entire stack you are working with, the faster you’ll be able to fix issues. Deeper understanding of a tech stack always pays dividends.
While poking around in my venv
‘s site-packages
folder, I noticed .pth
files. These files are path configuration files that are executed automatically during Python startup. There’s some documentation on this but they are not commonly used.
Here’s how to use them to automatically run code before running your module, script, etc file:
- Create a
.pth
file in yoursite-packages
venv folder. Import a module. - Within the imported module execute your code.
Here’s a shell script I used to create these files in the current venv:
# create a file to automatically import pretty-traceback on startup
python-inject-startup() {
local site_packages=$(python -c "import site; print(site.getsitepackages()[0])")
local pth_file=$site_packages/mbianco_injection.pth
local py_file=$site_packages/_mbianco_injection.py
if [[ -f "$pth_file" ]]; then
echo "python startup already exists: $target_file"
return 1
fi
cat << 'EOF' > "$py_file"
def run_startup_script():
try:
import pretty_traceback
pretty_traceback.install()
except ImportError:
pass
run_startup_script()
EOF
# the pth file must have a single line, so it's easier to import another file
echo "import _mbianco_injection" > "$pth_file"
echo "Python startup injection created: $pth_file"
}
Aliases for Venv Inspection
Want to quickly inspect the packages installed in your venv? Here are some nice aliases to jump into the site-packages
directory:
alias py-venv-path="python -c 'import site; print(site.getsitepackages()[0])'"
alias py-venv="cd $(py-venv-path)"
Here’s one I like to use if I need to make a quick-and-dirty hack to a package:
# find the location of a pip package
pip-show() {
# convert - => _
package_name=${1//-/_}
package_path=$(pip show $package_name | grep "Location:" | awk '{print $2}' | tr -d "\n")/$package_name
echo $package_path
cd $package_path
code $package_path
}
For instance, if you run pip-show openai
vs code will open with the source of the local package install of the openai
python package. You can test debug/test things out directly in the package’s source code without having to modify your pyproject
or monkeypatch the library.