The Not So Elegant Way to Debug Multiple Python Versions on Your Terminal

Adding a preexec function on your shell resource file to show what and where the command is is not so elegant; but it might just help you catch yourself before you start chaining commands and wonder why the Python dependency you installed is not being recognized.

By Ace Z. Alba

Published on August 9, 2024, 2:21 pm

In my system, I have three Python3 sources that might be called every time I run something.

  1. System Python (/usr/bin/python3)
  2. Pyenv-managed Python (handled by ~/.pyenv/shims/python3)
  3. Project-based Python (isolated via .venv, defined via Poetry or a combination of Pyenv and Poetry)

For most tasks my Python3 version should be called from the settings in Pyenv shims. To fully enforce this I even had the python3 aliased to the specific python3 blob in Pyenv rather than from pyenv's shims. Skipping pyenv for the sake of consistency was not ideal; but this didn't became the problem. Nonetheless, when I move from Pyenv's python3 to my project-based python3, what I wanted to happen was, I want Poetry to use Pyenv's active python (either through local or through shell) to build my project's venv. But, I also want Poetry to call the python3 from its own virtual environment configuration the moment the venv was built and the venv shell was made active.

But what turned my short affair into trying out AOC code problems into a half-a-day debugging was a baffling dependency problem when Poetry recognizes my pytest dependency, but my shell cannot locate it.

Well, for some reason, Pyenv was skipping both my shell alias and the active virtual environment for my project. So all python and python modules are invoked from pyenv shims instead. So I can't even call pytest, my sole dependency, even when pip and poetry add commands declare that it exists.

I commented out my python3 and my node aliases and rethinked this. I involved my node aliases as well, because like python3, my nodejs is handled by node virtual manager, so if my aliases were to blame for python3, it will likely be to blame if my nodejs commands bugs out as well.

Alas, it has been a result of a misconfiguration on my part. This was a new system after all, and my old settings did not carry through to this machine. So poetry was generating shells on its own cache folder. So I undid pyenv shell, deleted pyenv local, deleted the cached venv for my project, toggled poetry virtualenv to use active python shell and spawn venv inside project directory. I managed to fix it a bit, but by only calling pytest as a module. And I sourced my resource file.

But there is another part to this.

Because you see, there is also the idea of PipX, which runs a python3 app from another, and by that I mean its own, virtual environment. So you can just imagine how easy it is to stumble with Python3 applications by simply forgetting which Python3 from which environment installed which dependency, and which Python3 is your current shell commands actively using. But these myriad madness of Python3 sources are necessary.

There is probably a cleaner way to deal with this scenario, but before I do so, I have to remind myself, always if necessary, that the command I invoked from the command line is the command I wanted. So in my resource file (~/.zhsrc), I made the following function:

preexec() {
    local full_command="$1"
    local cmd
    cmd=$(echo "$full_command" | awk '{print $1}')

    echo " "
    echo "Running command using:"
    echo "$(which $cmd)"
    echo " "

}

This make sure that you can see where the command you just ran, and it identifies the command if it is an alias as well. I'll probably add a logic that will allow me to toggle this on or off from the command line (or excluding shell built-in commands), but right now, it is fine the way it is; I can just comment it out if not needed.