Skip to content

How to Inject Custom Code on Python Interpreter Startup

Tags: coding, python, software • Categories: Software

Table of Contents

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).

The Magic .pth File

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:

  1. Create a .pth file in your site-packages venv folder. Import a module.
  2. 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.

Keep in Touch

Subscribe to my email list to keep in touch. I’ll send you new blog posts and other thoughts.