pipx: All virtualenvs break when Python version is upgraded
I ran
$ pipx install poetry
just yesterday, and everything worked fine. Then I ran
$ brew upgrade
which upgraded my system Python from 3.7.2 to 3.7.3. Now pipx no longer works:
% poetry --version
zsh: /Users/raxod502/.local/bin/poetry: bad interpreter: /Users/raxod502/.local/pipx/venvs/poetry/bin/python: no such file or directory
% pipx run poetry --version
⚠️ poetry is already on your PATH and installed at /Users/raxod502/.local/bin/poetry. Downloading and running anyway.
Traceback (most recent call last):
File "/Users/raxod502/.local/bin/pipx", line 10, in <module>
sys.exit(cli())
File "/Users/raxod502/.local/lib/python/site-packages/pipx/main.py", line 525, in cli
exit(run_pipx_command(parsed_pipx_args, binary_args))
File "/Users/raxod502/.local/lib/python/site-packages/pipx/main.py", line 141, in run_pipx_command
use_cache,
File "/Users/raxod502/.local/lib/python/site-packages/pipx/commands.py", line 101, in run
retval = venv.run_binary(binary, binary_args)
File "/Users/raxod502/.local/lib/python/site-packages/pipx/Venv.py", line 106, in run_binary
return _run(cmd, check=False)
File "/Users/raxod502/.local/lib/python/site-packages/pipx/Venv.py", line 127, in _run
returncode = subprocess.run(cmd_str_list).returncode
File "/usr/local/Cellar/python/3.7.3/Frameworks/Python.framework/Versions/3.7/lib/python3.7/subprocess.py", line 472, in run
with Popen(*popenargs, **kwargs) as process:
File "/usr/local/Cellar/python/3.7.3/Frameworks/Python.framework/Versions/3.7/lib/python3.7/subprocess.py", line 775, in __init__
restore_signals, start_new_session)
File "/usr/local/Cellar/python/3.7.3/Frameworks/Python.framework/Versions/3.7/lib/python3.7/subprocess.py", line 1522, in _execute_child
raise child_exception_type(errno_num, err_msg, err_filename)
FileNotFoundError: [Errno 2] No such file or directory: '/Users/raxod502/.local/pipx/.cache/d7fe01e92227b36/bin/poetry': '/Users/raxod502/.local/pipx/.cache/d7fe01e92227b36/bin/poetry'
% pipx upgrade poetry
Traceback (most recent call last):
File "/Users/raxod502/.local/bin/pipx", line 10, in <module>
sys.exit(cli())
File "/Users/raxod502/.local/lib/python/site-packages/pipx/main.py", line 525, in cli
exit(run_pipx_command(parsed_pipx_args, binary_args))
File "/Users/raxod502/.local/lib/python/site-packages/pipx/main.py", line 184, in run_pipx_command
include_deps=args.include_deps,
File "/Users/raxod502/.local/lib/python/site-packages/pipx/commands.py", line 213, in upgrade
old_version = venv.get_venv_metadata_for_package(package).package_version
File "/Users/raxod502/.local/lib/python/site-packages/pipx/Venv.py", line 74, in get_venv_metadata_for_package
stdout=subprocess.PIPE,
File "/usr/local/Cellar/python/3.7.3/Frameworks/Python.framework/Versions/3.7/lib/python3.7/subprocess.py", line 472, in run
with Popen(*popenargs, **kwargs) as process:
File "/usr/local/Cellar/python/3.7.3/Frameworks/Python.framework/Versions/3.7/lib/python3.7/subprocess.py", line 775, in __init__
restore_signals, start_new_session)
File "/usr/local/Cellar/python/3.7.3/Frameworks/Python.framework/Versions/3.7/lib/python3.7/subprocess.py", line 1522, in _execute_child
raise child_exception_type(errno_num, err_msg, err_filename)
FileNotFoundError: [Errno 2] No such file or directory: '/Users/raxod502/.local/pipx/venvs/poetry/bin/python': '/Users/raxod502/.local/pipx/venvs/poetry/bin/python'
% pipx list
venvs are in /Users/raxod502/.local/pipx/venvs
binaries are exposed on your $PATH at /Users/raxod502/.local/bin
multiprocessing.pool.RemoteTraceback:
"""
Traceback (most recent call last):
File "/usr/local/Cellar/python/3.7.3/Frameworks/Python.framework/Versions/3.7/lib/python3.7/multiprocessing/pool.py", line 121, in worker
result = (True, func(*args, **kwds))
File "/usr/local/Cellar/python/3.7.3/Frameworks/Python.framework/Versions/3.7/lib/python3.7/multiprocessing/pool.py", line 44, in mapstar
return list(map(*args))
File "/Users/raxod502/.local/lib/python/site-packages/pipx/commands.py", line 538, in _get_package_summary
metadata = venv.get_venv_metadata_for_package(package)
File "/Users/raxod502/.local/lib/python/site-packages/pipx/Venv.py", line 74, in get_venv_metadata_for_package
stdout=subprocess.PIPE,
File "/usr/local/Cellar/python/3.7.3/Frameworks/Python.framework/Versions/3.7/lib/python3.7/subprocess.py", line 472, in run
with Popen(*popenargs, **kwargs) as process:
File "/usr/local/Cellar/python/3.7.3/Frameworks/Python.framework/Versions/3.7/lib/python3.7/subprocess.py", line 775, in __init__
restore_signals, start_new_session)
File "/usr/local/Cellar/python/3.7.3/Frameworks/Python.framework/Versions/3.7/lib/python3.7/subprocess.py", line 1522, in _execute_child
raise child_exception_type(errno_num, err_msg, err_filename)
FileNotFoundError: [Errno 2] No such file or directory: '/Users/raxod502/.local/pipx/venvs/poetry/bin/python': '/Users/raxod502/.local/pipx/venvs/poetry/bin/python'
"""
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/Users/raxod502/.local/bin/pipx", line 10, in <module>
sys.exit(cli())
File "/Users/raxod502/.local/lib/python/site-packages/pipx/main.py", line 525, in cli
exit(run_pipx_command(parsed_pipx_args, binary_args))
File "/Users/raxod502/.local/lib/python/site-packages/pipx/main.py", line 187, in run_pipx_command
commands.list_packages(PIPX_LOCAL_VENVS)
File "/Users/raxod502/.local/lib/python/site-packages/pipx/commands.py", line 599, in list_packages
for package_summary in p.map(_get_package_summary, dirs):
File "/usr/local/Cellar/python/3.7.3/Frameworks/Python.framework/Versions/3.7/lib/python3.7/multiprocessing/pool.py", line 268, in map
return self._map_async(func, iterable, mapstar, chunksize).get()
File "/usr/local/Cellar/python/3.7.3/Frameworks/Python.framework/Versions/3.7/lib/python3.7/multiprocessing/pool.py", line 657, in get
raise self._value
FileNotFoundError: [Errno 2] No such file or directory: '/Users/raxod502/.local/pipx/venvs/poetry/bin/python': '/Users/raxod502/.local/pipx/venvs/poetry/bin/python'
% pipx install poetry
'poetry' already seems to be installed. Not modifying existing installation in '/Users/raxod502/.local/pipx/venvs/poetry'. Pass '--force' to force installation
% pipx install poetry --force
Installing to existing directory '/Users/raxod502/.local/pipx/venvs/poetry'
Error: [Errno 2] No such file or directory: '/Users/raxod502/.local/pipx/venvs/poetry/bin/python3.7': '/Users/raxod502/.local/pipx/venvs/poetry/bin/python3.7'
'/usr/local/opt/python/bin/python3.7 -m venv /Users/raxod502/.local/pipx/venvs/poetry' failed
% pipx reinstall-all python3
Traceback (most recent call last):
File "/Users/raxod502/.local/bin/pipx", line 10, in <module>
sys.exit(cli())
File "/Users/raxod502/.local/lib/python/site-packages/pipx/main.py", line 516, in cli
exit(run_pipx_command(parsed_pipx_args, binary_args))
File "/Users/raxod502/.local/lib/python/site-packages/pipx/main.py", line 209, in run_pipx_command
skip=args.skip,
File "/Users/raxod502/.local/lib/python/site-packages/pipx/commands.py", line 467, in reinstall_all
uninstall(venv_dir, package, local_bin_dir, verbose)
File "/Users/raxod502/.local/lib/python/site-packages/pipx/commands.py", line 426, in uninstall
metadata = venv.get_venv_metadata_for_package(package)
File "/Users/raxod502/.local/lib/python/site-packages/pipx/Venv.py", line 85, in get_venv_metadata_for_package
stdout=subprocess.PIPE,
File "/usr/local/Cellar/python/3.7.3/Frameworks/Python.framework/Versions/3.7/lib/python3.7/subprocess.py", line 472, in run
with Popen(*popenargs, **kwargs) as process:
File "/usr/local/Cellar/python/3.7.3/Frameworks/Python.framework/Versions/3.7/lib/python3.7/subprocess.py", line 775, in __init__
restore_signals, start_new_session)
File "/usr/local/Cellar/python/3.7.3/Frameworks/Python.framework/Versions/3.7/lib/python3.7/subprocess.py", line 1522, in _execute_child
raise child_exception_type(errno_num, err_msg, err_filename)
FileNotFoundError: [Errno 2] No such file or directory: '/Users/raxod502/.local/pipx/venvs/poetry/bin/python': '/Users/raxod502/.local/pipx/venvs/poetry/bin/python'
% pipx uninstall-all
Traceback (most recent call last):
File "/Users/raxod502/.local/bin/pipx", line 10, in <module>
sys.exit(cli())
File "/Users/raxod502/.local/lib/python/site-packages/pipx/main.py", line 516, in cli
exit(run_pipx_command(parsed_pipx_args, binary_args))
File "/Users/raxod502/.local/lib/python/site-packages/pipx/main.py", line 191, in run_pipx_command
commands.uninstall_all(PIPX_LOCAL_VENVS, LOCAL_BIN_DIR, verbose)
File "/Users/raxod502/.local/lib/python/site-packages/pipx/commands.py", line 449, in uninstall_all
uninstall(venv_dir, package, local_bin_dir, verbose)
File "/Users/raxod502/.local/lib/python/site-packages/pipx/commands.py", line 426, in uninstall
metadata = venv.get_venv_metadata_for_package(package)
File "/Users/raxod502/.local/lib/python/site-packages/pipx/Venv.py", line 85, in get_venv_metadata_for_package
stdout=subprocess.PIPE,
File "/usr/local/Cellar/python/3.7.3/Frameworks/Python.framework/Versions/3.7/lib/python3.7/subprocess.py", line 472, in run
with Popen(*popenargs, **kwargs) as process:
File "/usr/local/Cellar/python/3.7.3/Frameworks/Python.framework/Versions/3.7/lib/python3.7/subprocess.py", line 775, in __init__
restore_signals, start_new_session)
File "/usr/local/Cellar/python/3.7.3/Frameworks/Python.framework/Versions/3.7/lib/python3.7/subprocess.py", line 1522, in _execute_child
raise child_exception_type(errno_num, err_msg, err_filename)
FileNotFoundError: [Errno 2] No such file or directory: '/Users/raxod502/.local/pipx/venvs/poetry/bin/python': '/Users/raxod502/.local/pipx/venvs/poetry/bin/python'
I assume the problem is that when pipx creates the virtual environments, it resolves /usr/local/bin/python3
as a symbolic link, which is problematic since the destination of that symlink changes every time Homebrew upgrades Python (thus making the old virtualenv’s Python executable no longer work). I have experienced this problem with Pip as well, but presumably pipx could wrap this behavior somehow to make it work, or at least display a useful error message. At the least, it would be nice to have pipx reinstall-all python3
work properly.
Workaround is something like the following:
% mv ~/.local/pipx /tmp/pipx
% ls /tmp/pipx/venvs | xargs -L 1 pipx install
and manually prune any broken symlinks from ~/.local/bin
.
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 3
- Comments: 36 (17 by maintainers)
Commits related to this issue
- Get cached venv metadata from pipxrc. Fixes #146 by allowing reinstall-all() to call uninstall() without breaking. — committed to itsayellow/pipx by itsayellow 5 years ago
- Get cached venv metadata from pipxrc. Fixes #146 by allowing reinstall-all() to call uninstall() without breaking. — committed to itsayellow/pipx by itsayellow 5 years ago
- Add --clear to venv create. This fixes a fatal error that would come with a invalid python symlink in an existing shared library venv. This is necessary for reinstall-all to be used successfully to u... — committed to itsayellow/pipx by itsayellow 5 years ago
- Add --clear to venv create. (#225) This fixes a fatal error that would come with a invalid python symlink in an existing shared library venv. This is necessary for reinstall-all to be used successf... — committed to pypa/pipx by itsayellow 5 years ago
- Get cached venv metadata from pipxrc. Fixes #146 by allowing reinstall-all() to call uninstall() without breaking. — committed to itsayellow/pipx by itsayellow 5 years ago
- Get cached venv metadata from pipxrc. Fixes #146 by allowing reinstall-all() to call uninstall() without breaking. — committed to itsayellow/pipx by itsayellow 5 years ago
my simple and dirty workaround is force pipx to re-create its shared virtualenv by deleting it, and reinstall all packages:
The current best way to fix a homebrew upgrade of the system python would be:
If you installed or reinstalled your packages after pipx version 0.15.0.0, then all options, package specs, and injected packages will be remembered and
reinstall-all
should recreate everything just like you installed them.I’m on mac, and homebrew upgrades my python all the time. All my pipx packages are easily fixed when this happens by:
This workaround doesn’t seem too hacky to me.
Personally, I like symlinks and would prefer to keep them. I wonder instead of
pipx
could have a command to repair venvs after its underlying Python changes. Actually, I guess I’d probably push this upstream and find out what is the best recommendation for any project usingvenvs
to do after a Python upgrade… and then automate that recommendation in pipx.I ran into this also, and used something like this to repair broken venvs. I don’t think it’s necessarily the right or best way to handle the problem, or at the very least it would need to be fleshed out a bit (there’s no error handling or usage info, sloppily hardcoded to one python version, etc). But it’s something that works in a pinch and could be the bones of something smarter.
I think this is fundamentally similar to OP’s workaround, just operates one level lower (as a venv repair rather than a pipx reinstall).
Thanks, @integralist, this is a bug, but separate from the original issue here. Could you please start a new issue with the above comment?
In addition, could you show what version of pipx you are using? (i.e. the output from
pipx --version
) The syntax forreinstall-all
you are using indicates your version of pipx may be very old. Currently to specify the python executable withreinstall-all
the syntax ispipx reinstall-all --python python3
.It may be that your version of pipx is so old that you don’t have many of the updates that made
reinstall-all
work successfully in situations like these. Also updates to pipx have made it fail gracefully to missing python executables.I use the following and this helps, I can gist this later into a shell script
basically
reinstall
The solution to this would be to convince the Homebrew people in some way to maintain a symlink for the Python major.minor version (
/usr/local/bin/python3.7
), and have that exposed as the path, because that’s the level at which things don’t break if you upgrade Python. Then pipx would use that and not have its paths disappear underneath it upon a bugfix update (3.7.5 -> 3.7.6) of Python, where the later version is meant to be drop-in compatible with the earlier one. A bunch of other Python software would want that, too.I also hit this problem. All of the solutions listed here feel more like workarounds to me. Users shouldn’t have to think about this. Anyone understands why the symlink to python3.X is automatically getting expanded, and which bit of code is doing that?
assuming this is specific to homebrew, an alternative solution is to suggest users set
export HOMEBREW_NO_INSTALL_CLEANUP=1
. Starting from homebrew 2.0.0 this auto cleanup feature was addeddetails: https://discourse.brew.sh/t/when-did-brew-upgrade-and-reinstall-start-removing-versions/4054
FWIW, all virtualenvs that directly depend on a particular version of a homebrew python will break if that version of python is deleted so this problem isn’t specific to pipx
I’ve run into this a few times. My solution has been to manage a
python-latest
symlink that I point to from the pipx virtualenv. If pipx or any pipx-managed application ever gives me a stacktrace like above, I check the symlink and take an appropriate action.One thing I’ve noticed (and even looked through the code a bit to try to resolve) is that either pipx or venv itself will dereference the path to the realpath. If I use my
python-latest
symlink to create a new tool using pipx, e.g.pipx install --python ~/.pyenv/versions/python-latest/bin/python pycowsay
, the python symlink in the virtualenv’sbin
will point to /Users/user/.pyenv/versions/3.7.3/Python.framework/Versions/3.7/bin/python3.7, assuming ~/.pyenv/versions/python-latest` points to 3.7.3.I’ve been meaning to look into exactly how pipx handles this realpath resolution (or if venv does this itself), since it would be nice to have a more stable pipx that doesn’t break on random python upgrades. Admittedly, this is rare with pyenv (unlike homebrew now), but I’ve had it happen before.
Here is a python script I hacked together that will fix all your virtual environments after an upgrade. It seems to work for me, but mileage may vary, some stuff is hardcoded, etc. Use at your own risk:
As virtualenv maintainer, I tend to disagree. I think the path forward here will be for pipx shim to automatically check if the home python changed (can just check the venvs pyenv.cfg home key), and if it did trigger a reinstall against the new Python (recreate venv + reinstall packages). @cs01?
I’ve just uninstalled the homebrew version of pipx due to this error. I don’t know if anyone else noticed the connection but by default
pipx
choses to create its virtualenvs using the python binary found bysys.executable
of the python interpreter currently runningpipx
. Which combined with the default behaviour of homebrew means that this is going to break… quite often.It actually surprised me that
pipx
wasnt using my default current shell’s python binary (set bypyenv
not homebrew).If this is going to work reliably with homebrew, it has to use a stable python target, like the system python, a python installed using the python.com installer, or one provided by pyenv. You cant swap virtualenv symlinks around reliably, ( it works sometimes, but don’t let that fool you 😉 ) and as @chrish42 pointed out, things like
HOMEBREW_NO_INSTALL_CLEANUP=1
are just workarounds, (that in particular is a rather ugly one, that will leave junk around on users machines) so it would be good to have a proper fix.Edit: Ive just done more digging and it looks like this came up before… https://github.com/pipxproject/pipx/issues/113 is still open and refers to https://github.com/pipxproject/pipx/issues/17 as why
pipx
usessys.executable
.From the comments on those issues, it looks like this issue might require some careful choices about just which python is chosen, and perhaps more explicit warnings about how an environment has to be setup, such as
~/.local/bin
coming before~/.pyenv/shims
in a users path for things to work correctly.Looks like
--upgrade
might be they way to go based on this answer. Want to give it a try, @raxod502?