I use pyenv
(installed with homebrew) to manage my macOS python-versions.
Python 3.11 just came out, but pyenv
doesn't see this version. However from googling it appears others are able to see/install it.
How to fix?
This caused me some trouble, so I'm gona jot my solution down in case it's useful (and in case others can improve upon the solution path).
EDIT: On a fresh macOS, I bumped into: UserWarning: Could not import the lzma module. Your installed Python is incomplete and (as per answers there) did brew install xz
before pyenv install 3.11.0
.
The first issue was that the homebrew-installed pyenv
doesn't see the new 3.11. So I had to uninstall it and re-install using pyenv's recommended method:
> brew uninstall pyenv
> # rm -rf ~/.pyenv
> curl https://pyenv.run | bash
> pyenv install 3.11.0
> pyenv global 3.11.0
> which python ; python --version # sanity check
(Note: You'll lose your current .pyenv-s if you rm -rf ~/.pyenv
-- I did it to keep things simple but don't know if it's needed)
Now to test it out:
> python -m venv --upgrade-deps .venv
> which python ; which pip
So far so good, but if I create a foo.py
that does import requests
, and:
> pip install requests
> python foo.py
... I get "ModuleNotFoundError: No module named 'requests'"
Which is confusing.
An engineer suggested I inspect python -m site
but I lost the original output.
Next I see at the bottom of my ~/.bash_profile
:
# recommendation by homebrew from `brew install pyenv`
if which pyenv-virtualenv-init > /dev/null; then eval "$(pyenv virtualenv-init -)"; fi
export PYENV_ROOT="$HOME/.pyenv"
command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"
Now I'm pretty sure the last time I installed pyenv
(using homebrew) it suggested this, so I copy+pasted it into my ~/.bash_profile
.
But it turns out I needed an additional:
eval "$(pyenv init --path)"
Now I restart my terminal and boom it works.
> pyenv init --path
PATH="$(bash --norc -ec 'IFS=:; paths=($PATH); for i in ${!paths[@]}; do if [[ ${paths[i]} == "'/Users/pi/.pyenv/shims'" ]]; then unset '\''paths[i]'\''; fi; done; echo "${paths[*]}"')"
export PATH="/Users/pi/.pyenv/shims:${PATH}"
command pyenv rehash 2>/dev/null
This appears to be removing any pyenv shims from the PATH env-var and prepending the correct shim to the PATH.
Now what about the original pyenv init -
command? What does that do?
> pyenv init -
PATH="$(bash --norc -ec 'IFS=:; paths=($PATH); for i in ${!paths[@]}; do if [[ ${paths[i]} == "'/Users/pi/.pyenv/shims'" ]]; then unset '\''paths[i]'\''; fi; done; echo "${paths[*]}"')"
export PATH="/Users/pi/.pyenv/shims:${PATH}"
export PYENV_SHELL=bash
source '/Users/pi/.pyenv/libexec/../completions/pyenv.bash'
command pyenv rehash 2>/dev/null
pyenv() {
local command
command="${1:-}"
if [ "$#" -gt 0 ]; then
shift
fi
case "$command" in
activate|deactivate|rehash|shell)
eval "$(pyenv "sh-$command" "$@")"
;;
*)
command pyenv "$command" "$@"
;;
esac
}
This all seems confusing. The above is documented in https://github.com/pyenv/pyenv#advanced-configuration but do I really need such a complex path to achieve something that feels like it should be 'out-of-the-box'?