pipx: Once system python and pipx upgraded, cannot install new app if some previous old shared_venv existed
Describe the bug
On Manjaro/Arch, I was with python 3.7.4. To ensure stability of some apps (and as some apps are not fully working on 3.8 that I new was coming) I also had python 3.7.5 installed through pyenv. I use this python to install my apps with
pipx install --python ~/.pyenv/versions/3.7.5/bin/python flexget
pipx inject flexget transmissionrpc
Then my system got upgraded from 3.7.4 to 3.8 (Note that my 3.7.5 pyenv stayed) I reinstalled pipx on 3.8 on system. Then I had error thrown when installing new pipx no matter what versions I tried:
$ pipx install pycowsay --verbose
pipx > (run_pipx_command:134): Virtual Environment location is /home/hcooh/.local/pipx/venvs/pycowsay
pipx > (run:97): running /usr/bin/python -m venv --without-pip /home/hcooh/.local/pipx/venvs/pycowsay
pipx > (run:97): running /home/hcooh/.local/pipx/venvs/pycowsay/bin/python -m pip install pycowsay
/home/hcooh/.local/pipx/venvs/pycowsay/bin/python: No module named pip
pipx > (rmdir:16): removing directory /home/hcooh/.local/pipx/venvs/pycowsay
'/home/hcooh/.local/pipx/venvs/pycowsay/bin/python -m pip install pycowsay' failed
or even trying to use the pyenv:
pipx install --python ~/.pyenv/versions/3.7.5/bin/python pycowsay --verbose
pipx > (run_pipx_command:134): Virtual Environment location is /home/hcooh/.local/pipx/venvs/pycowsay
pipx > (run:97): running /home/hcooh/.pyenv/versions/3.7.5/bin/python -m venv --without-pip /home/hcooh/.local/pipx/venvs/pycowsay
pipx > (run:97): running /home/hcooh/.local/pipx/venvs/pycowsay/bin/python -m pip install pycowsay
/home/hcooh/.local/pipx/venvs/pycowsay/bin/python: No module named pip
pipx > (rmdir:16): removing directory /home/hcooh/.local/pipx/venvs/pycowsay
'/home/hcooh/.local/pipx/venvs/pycowsay/bin/python -m pip install pycowsay' failed
Running the already installed app (using the 3.7.5) would work fine and even pipx runpip xxx list
I realised then that .local/pipx/shared/pyvenv.cfg mentioned python version 3.7.4.
So I renamed the .local/pipx/shared folder differently. It recreated the shared lib for python3.8.
I had to then symlink the pyenv folder of python3.7 with:
ln -s ../../../../.pyenv/versions/3.7.5/lib/python3.7/ ~/.local/pipx/shared/lib/python3.7
And then install run fine and the already installed app run fine too
How to reproduce
See above
Expected behavior
pipx Install install should work after a pyhton upgrade even if hte previous shared folder still exists. i think
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 6
- Comments: 25 (11 by maintainers)
Have you tried
pipx reinstall-all
? This command is supposed to help for situations when the system python changes, and has been known to work for that situation on the Mac.If that doesn’t work, what does
pipx list
return? It should show a lit of packages with warnings that they are missing a valid python interpreter.Did you uninstall the old
jq
before trying to install the newjq
? That may be important.The other thing I can think of that might possibly be affecting you is that you are installing pipx to the system as opposed to using
python3 -m pip install --user pipx
as is our recommendation.Just had this issue again upgrading to fedora 35:
I fixed it by running:
and then running a pipx operation to make it recreate the shared env with the new python version.
Summary
Judging from https://github.com/pypa/pipx/issues/294#issuecomment-992699160 and https://github.com/pypa/pipx/issues/294#issuecomment-597792241, there’s at least 3 versions in a pipx setup:
Each of these should match the system Python interpreter, but might desync (the system Python interpreter’s path is still present, but belongs to a different Python version) when updating Python in-place on Linux.
Ideas
Not a pipx contributor so far, but I could try changing
reinstall-all
to always recreate the shared environment, and change more pipx operations (reinstall
,upgrade[-all]
,install
, etc.) to automatically recreate the shared environment if the Python interpreter version has changed. (Perhaps it should also reinstall all packages? It’s necessary to fix broken packages, but unexpected by the user. Maybe do nothing in shell scripts and prompt the user if a TTY is detected?) You’ll either have to save the install-time Python version in a config file, or detect the install-time Python version from the shared/app venv (I’m not sure how, and looking for a.../lib/python3.x
folder can mistakenly indicate a venv was installed on newer Python than it actually was; on current pipx I managed to end up with a shared env with an emptypython3.10
folder, and an app’s venv with apython3.10
folder containing Python 3.10__pycache__
but 3.9 C extensions).My other idea is to add a config file/database containing a list of venvs and each venv’s install options and Python versions. This complicates the architecture since the config file can desync with the actual folders present (eg. if a user adds/removes venv folders by hand), but is useful since if a failed
reinstall
deletes a venv, it’s still present in the config/database and a subsequentreinstall-all
knows to install them. This config file should also store the shared environment’s Python version and/or interpreter path, so pipx can compute when to regenerate the shared env and all app venvs (or you can skip this and use the version stored in~/.local/pipx/shared/pyvenv.cfg
).In theory you could also store the Python version/path of each app’s venv (though this is already tracked in
~/.local/pipx/venvs/*/pyvenv.cfg
. Using this to support different venvs with different Python versions/interpreters is too complex IMO. It might be useful to use this to track which venvs need to be reinstalled. I kinda prefer not supporting partially upgrading venvs, but instead block all pipx operations until the shared env and all venvs are upgraded in a single operation. But old pipx versions (combined with users copying commands to try to fix pipx) can generate mixed Python versions. If we don’t check per-venv Python versions, we can update the docs and tell users to “reinstall-all
to fix things”; if we do, we can detect and address this situation.I think it’s still a good idea for
reinstall-all
to regenerate the shared env, even if it’s supposedly up-to-date according to the “install-time Python version” metadata. This allows the command to fix errors tied to the shared env, rather than any app’s venv. (These errors may exist, I’ve never encountered any other than a Python version mismatch.) This also fixes cases where the Python version has actually changed even though the metadata wrongly indicates the install-time version matches the current version.Is it possible for each app’s shim binary to automatically reinstall the app (and shared env, maybe other apps) if necessary? This makes the reinstallation process transparent to users and hopefully “just work”, but I’m not sure if users want their apps to magically change behavior in edge cases (though they already break right now). And this might slow-down happy-path app startup time (though you can get pretty close to zero overhead by only reinstalling when you catch
ModuleNotFoundError
) (though I don’t know if this fixes both “outdated shared env and app” and “updated shared env but outdated app”). And this doesn’t catch cases wherepipx install --system-site-packages
andpipx upgrade
results in a working app which uses C libraries from the system rather than the venv.Or perhaps pipx itself should catch
ModuleNotFoundError: No module named 'pipx'
and reinstall the shared env and all packages. (I don’t think I’ve seen this particular error since I installed pipx from the Arch repositories rather thanpip install --user
.) This fixes another user-facing failure mode of pipx, but can’t replace my previous suggestions since it fails to catch cases where pipx is updated but the shared env is out-of-date and unusable.I didn’t look into handling the system Python interpreter being uninstalled, and a new one being installed to a different location. This case is more common on Windows where Python is installed to
C:\Python3xx\bin\python
rather than/usr/bin/python
. This may break the pipx binary, and require the user to reinstall pipx by hand, and then ideally pipx updates the shared and app envs from there.Testing
I think it will be difficult to come to a consensus on an optimal design (probably manual testing will help in designing an intuitive interface), and difficult to manually or automatically test updating Python to a new major version (which only happens once per year per Linux installation). Perhaps pyenv will help to test switching Python interpreters quickly. There’s also “build a Docker image with an outdated Python”, but I’m less familiar with that than pyenv, and setting up Docker/Podman requires more upfront setup and invasive system changes (cgroups etc.) than pyenv IMO.
OK, I finally had a chance to run your very complete docker instructions. Thank you for that!
I can reproduce what you’re talking about. And it doesn’t seem to help if you install
pipx
usingpip install --user
.First problem, yq stops working after system python upgrade
What seems to be happening is that in
~/.local/pipx/venv/yq/bin
all the pythons are being linked back only to/usr/bin/python3
. This is not what happens for me on my Mac: there each venv’s python is linked to a very particular version of python on my system (e.g./usr/local/Cellar/python/3.7.7/python3.7
.). So it doesn’t come up empty looking for a non-existent python (which we handle), but the python it finds has a different minor version than before. This causes site packages to be in the wrong place (they are in~/.local/pipx/venvs/yq/lib/python3.7/
but the new python3.8 is looking for~/.local/pipx/venvs/yq/lib/python3.8/
which doesn’t exist)I see the same behavior on my Ubuntu system. The python interpreter link in all the venvs points to
/usr/bin/python3
, not a specific version like/usr/bin/python3.7
.The workaround when you first install yq would be this pipx command:
Then after upgrading the system python
yq
will still work. (I tried it and it works for me.)My suggestion to the pipx developers is that when we determine the system python, maybe we should resolve the link back to the original binary filepath which would get a specific python sub-version. (e.g. in
utils.py
we should useDEFAULT_PYTHON = Path(sys.executable).resolve()
). I’m not sure if that’s the best solution (it would cause sub-minor python upgrades to break pipx venvs) but it’s a solution.Second problem, reinstall of pipx will not install after system upgrade
I think this is similar to the venv problem described above. The shared libs have their own idea of a python binary, and on linux this seems to be linked to a generic binary without a sub-version (i.e.
/usr/bin/python3
) which then screws up the shared lib venv when the python minor version number changes. It appears to pipx that at least the python binary is valid, but the shared_lib venv has no site-packages under the the newpython3.8
only under the oldpython3.7
.shared_libs.py
,_SharedLibs.is_valid()
only checks if we have files of the python binary and the pip binary, but it doesn’t check that the library pip is still accessible for the current python. When the python the shared_libs is linked to gets changed from 3.7 to 3.8, then we no longer have the packagepip
under the right directory in the shared_libs venv.I reinstalled
pipx
usingpip install pipx
after upgrading to Python 3.10.When I ran
pipx reinstall-all
, it printed a lot of errors and removed all my virtualenvs, it failed to reinstall these.https://gist.github.com/Exagone313/99b7aa35b8aec098f431e702d9416f35
After that, running the command again gave an error about the pip package being not found in pipx virtualenv:
I solved it by moving the
~/.local/pipx
directory:mv ~/.local/pipx{,.old}
, then I reinstalled all my packages manually.I think the reinstallation should be protective and keep the list of installed virtualenvs at some place to deal with installation errors. At least I had the log.
Arch Linux, pipx 0.16.4, just upgraded from Python 3.9 to 3.10, and as per tradition all my pipx packages broke.
Old venvs break
Even after recreating the shared env, some of my existing envs don’t load, since they only have
~/.local/pipx/venvs/PACKAGE/lib/python3.9/
:Interestingly, maestral and corrscope (which I installed with
--system-site-packages
so they could use system PyQt5) work without having to reinstall. It seemspipx upgrade-all
somehow produced acorrscope/lib/python3.10
with very recent modification dates (11:02:06 PM), andmaestral/lib/python3.10
(11:02:00 PM). For context, I updated python at 10:57 PM, tried and failed to runpipx upgrade-all
at 11:01:57 PM, and recreated my pipx shared env later at 11:02:39 PM. Interestingly,corrscope/bin
andmaestral/bin
still havepython3.9
binaries, but they now point to/usr/bin/python3.10
(possibly because/usr/bin/python
links to/usr/bin/python3.10
).However,
corrscope
(which I installed with now fails to run when I uninstall system matplotlib (printsModuleNotFoundError: No module named 'matplotlib'
), because the pipx-venv matplotlib contains CPython 3.9 extensions and can no longer be loaded by 3.10.Reinstalling works-ish
pipx reinstall awscli
worked. I tried reinstalling this usingfd -d 1 . ~/.local/pipx/venvs/ --exec pipx reinstall '{/}'
(because I didn’t realize thatreinstall-all
existed). The uninstalls succeeded, but the installs failed:I think that
fd
doesn’t change the current working directory, implying thatpipx reinstall
failed because I ranfd
in a folder deleted by one of thereinstall
commands. Do you consider it a bug that pipx fails when run in a folder deleted bypip install
, and do you plan tocd $HOME
after deleting the venv? Is it a bug that a failedpipx reinstall
deletes the venv folder and makes pipx act like the package never existed?Suggestions
pipx reinstall-all
to first delete (or rename) and recreate the shared venv, before reinstalling all packages from the original spec. (Before I realized thatreinstall-all
existed, I wrote: ideally if reinstalling any package fails, the old environment is left in place, and/or the failing packages are printed.) IDK if this is implemented or necessary.pipx reinstall
not fail if your current working directory is deleted by the uninstall process.pipx reinstall
better handle the case where reinstallation fails (perhaps leave the old folder in place? perhaps rename the old folder instead of deleting it?).We don’t plan to support this for now. On python upgrades I recommend reinstall-all.
@michjnich , what happens with Python 3.9 if you just try the following on your command-line:
If that’s missing, then you might need to use
apt
to reinstall for Python 3.9 the necessary modules for Ubuntu that ensure venv and pip are available: https://pipxproject.github.io/pipx/troubleshooting/#debian-ubuntu-issuesI’m not sure if this is related, but I’ve had very similar issues. After much un/re-installing with manual clear downs of the shared dirs I’ve got rid od f the “can’t find pip” issue, but am now having the following when trying to install something:
I can uninstall pipx, remove the .local/pipx tree completely, then reinstall and try it, and it’s the same problems.
This following an upgrade from 3.8 => 3.9 in Ubuntu on WSL2, using
update-alternatives
to manage the python version.If I flip the python version back to 3.8 via update-alternatives, install pipx for that, then it all works just fine and I can
pipx install
as expected.Then going back to 3.9 requires installing pipx again. After that
pipx list
works, but nothing else - then I get “No module named pip”.I think I encountered a similar problem after upgrading my python install from the brew. The solution that worked for me is
pipx reinstall-all
.To illustrate the problem and how it gets resolved, the following are the commands I did.
btw the workaround after an upgrade of the system python to get pipx working (after reinstalling pipx):
Or at least I assume that should be the workaround, but when I tried it, it found pip but then failed spectacularly trying to build a wheel for jq.