如何使 Python 版本的可执行文件在多个 pyenv-virtualenv 虚拟环境中全局化
How to make Python version executables global across multiple pyenv-virtualenv virtual environments
A pyenv
Python 版本(例如 3.10.4
)具有与其关联的“正常”预期 Python 可执行文件(例如,pip
、2to3
, pydoc
)
$ ls "${PYENV_ROOT}/versions/3.10.4/bin"
2to3 idle idle3.10 pip3 pydoc pydoc3.10 python-config python3-config python3.10-config
2to3-3.10 idle3 pip pip3.10 pydoc3 python python3 python3.10 python3.10-gdb.py
和一个 pyenv-virtualenv
虚拟环境只有在虚拟环境目录结构中可以获得的可执行文件
$ pyenv virtualenv 3.10.4 venv
$ ls "${PYENV_ROOT}/versions/venv"
bin include lib lib64 pyvenv.cfg
$ ls "${PYENV_ROOT}/versions/venv/bin/"
Activate.ps1 activate activate.csh activate.fish pip pip3 pip3.10 pydoc python python3 python3.10
默认情况下,venv
虚拟环境在创建后不知道它所关联的 Python 版本的可执行文件,例如 2to3
$ pyenv activate venv
(venv) $ 2to3 --help
pyenv: 2to3: command not found
The `2to3' command exists in these Python versions:
3.10.4
Note: See 'pyenv help global' for tips on allowing both
python2 and python3 to be found.
因此,为了允许像 venv
这样的虚拟环境访问这些可执行文件,您将它和创建它的 Python 添加到 pyenv global
以便 pyenv
将 "fall back" 到 Python 找不到可执行文件时的版本
(venv) $ pyenv deactivate
$ pyenv global venv 3.10.4
(venv) $ pyenv global
venv
3.10.4
(venv) $ 2to3 --help | head -n 3
Usage: 2to3 [options] file|dir ...
Options:
此模式适用于一个虚拟环境,但是当您有多个虚拟环境时,如何保持对 2to3
(或 pipx
等可执行文件的访问,如下所示`)?
(venv) $ pyenv virtualenv 3.10.4 example && pyenv activate example
(example) $ 2to3
pyenv: 2to3: command not found
The `2to3' command exists in these Python versions:
3.10.4
Note: See 'pyenv help global' for tips on allowing both
python2 and python3 to be found.
可重现的例子
使用以下 Dockerfile
FROM debian:bullseye
SHELL ["/bin/bash", "-c"]
USER root
RUN apt-get update -y && \
apt-get install --no-install-recommends -y \
make \
build-essential \
libssl-dev \
zlib1g-dev \
libbz2-dev \
libreadline-dev \
libsqlite3-dev \
wget \
curl \
llvm \
libncurses5-dev \
xz-utils \
tk-dev \
libxml2-dev \
libxmlsec1-dev \
libffi-dev \
liblzma-dev \
g++ && \
apt-get install -y \
git && \
apt-get -y clean && \
apt-get -y autoremove && \
rm -rf /var/lib/apt/lists/*
# Install pyenv and pyenv-virtualenv
ENV PYENV_RELEASE_VERSION=2.3.0
RUN git clone --depth 1 https://github.com/pyenv/pyenv.git \
--branch "v${PYENV_RELEASE_VERSION}" \
--single-branch \
~/.pyenv && \
pushd ~/.pyenv && \
src/configure && \
make -C src && \
echo 'export PYENV_ROOT="${HOME}/.pyenv"' >> ~/.bashrc && \
echo 'export PATH="${PYENV_ROOT}/bin:${PATH}"' >> ~/.bashrc && \
echo 'eval "$(pyenv init -)"' >> ~/.bashrc && \
. ~/.bashrc && \
git clone --depth 1 https://github.com/pyenv/pyenv-virtualenv.git $(pyenv root)/plugins/pyenv-virtualenv && \
echo 'eval "$(pyenv virtualenv-init -)"' >> ~/.bashrc
# Install CPython
ENV PYTHON_VERSION=3.10.4
RUN . ~/.bashrc && \
echo "Install Python ${PYTHON_VERSION}" && \
PYTHON_MAKE_OPTS="-j8" pyenv install "${PYTHON_VERSION}"
# Make 'base' virtual envirionment, add it and its Python version to global for
# executables like 2to3 or pipx to be findable
# c.f. https://github.com/pyenv/pyenv-virtualenv/issues/16#issuecomment-37640961
# and then install pipx into the 'base' virtual environment and use pipx to install
# pepotron
RUN . ~/.bashrc && \
pyenv virtualenv "${PYTHON_VERSION}" base && \
echo "" && echo "Python ${PYTHON_VERSION} has additional executables..." && \
ls -lh "${PYENV_ROOT}/versions/${PYTHON_VERSION}/bin" && \
echo "" && echo "...compared to 'base' virtualenv made with Python ${PYTHON_VERSION}" && \
ls -lh "${PYENV_ROOT}/versions/base/bin" && \
echo "" && echo "...because 'base' is actually a symlink" && \
ls -lh "${PYENV_ROOT}/versions/" && \
pyenv global base "${PYTHON_VERSION}" && \
python -m pip --quiet install --upgrade pip setuptools wheel && \
python -m pip --quiet install pipx && \
python -m pipx ensurepath && \
eval "$(register-python-argcomplete pipx)" && \
pipx install pepotron
WORKDIR /home/data
构建于
docker build . --file Dockerfile --tag pyenv/multiple-virtualenvs:debug
可以运行用下面的来演示问题
$ docker run --rm -ti pyenv/multiple-virtualenvs:debug
(base) root@26dfa530cd82:/home/data# pyenv global
base
3.10.4
(base) root@26dfa530cd82:/home/data# 2to3 --help | head -n 3
Usage: 2to3 [options] file|dir ...
Options:
(base) root@26dfa530cd82:/home/data# pipx list
venvs are in /root/.local/pipx/venvs
apps are exposed on your $PATH at /root/.local/bin
package pepotron 0.6.0, installed using Python 3.10.4
- bpo
- pep
(base) root@26dfa530cd82:/home/data# pep 3.11
https://peps.python.org/pep-0664/
(base) root@26dfa530cd82:/home/data# pyenv virtualenv 3.10.4 example
(base) root@26dfa530cd82:/home/data# pyenv activate example
pyenv-virtualenv: prompt changing will be removed from future release. configure `export PYENV_VIRTUALENV_DISABLE_PROMPT=1' to simulate the behavior.
(example) root@26dfa530cd82:/home/data# 2to3
pyenv: 2to3: command not found
The `2to3' command exists in these Python versions:
3.10.4
Note: See 'pyenv help global' for tips on allowing both
python2 and python3 to be found.
(example) root@26dfa530cd82:/home/data# pipx
pyenv: pipx: command not found
The `pipx' command exists in these Python versions:
3.10.4/envs/base
base
Note: See 'pyenv help global' for tips on allowing both
python2 and python3 to be found.
那么,如果安装在 pyenv-virtualenv
虚拟环境中,那么像 pipx
这样设计用于全局安装的东西怎么能在全球范围内工作,这样您就不必拥有在系统 Python?
中安装了 pip
的任何东西
似乎您不需要使用 pyenv activate
来激活虚拟环境,而是需要停用任何虚拟环境,然后仅使用 pyenv global <virtual environment name> <virtual environment Python version>
来有效地切换环境。我假设这不是在虚拟环境中使用 Python 版本可执行文件的唯一方法,因为这似乎会消除为pyenv-virtualenv
.
你可以直接在pyenv前缀中执行pipx
二进制文件,它应该可以正常工作。
Pyenv 的 shims 机制并不是真正为这样的全局二进制文件设计的。我天真地期望当本地环境没有安装二进制文件时,全局环境会作为回退,但我认为 pyenv 在回退到 $PATH.
之前只查看系统 Python
因此,如果您不将 pipx
安装到 system
(如果您没有安装系统 pip
,我怀疑您正在安装),那么天真的回退不起作用。
另一种方法是 运行 pyenv
使用临时环境,即
PYENV_VERSION=my-pipx-env pyenv exec pipx
.
我想让它成为一个可执行文件,所以我建议将类似这样的东西添加到优先于 pyenv 路径的特殊 PATH 目录中:
#!/usr/bin/env bash
set -eu
export PYENV_VERSION="pipx"
exec "${PYENV_ROOT}/libexec/pyenv" exec pipx "$@"
不过,我很想放弃整个激活逻辑,直接 exec
来自环境 /bin
的 pipx
二进制文件,以避免 运行 进入任何 shell 配置错误。
A pyenv
Python 版本(例如 3.10.4
)具有与其关联的“正常”预期 Python 可执行文件(例如,pip
、2to3
, pydoc
)
$ ls "${PYENV_ROOT}/versions/3.10.4/bin"
2to3 idle idle3.10 pip3 pydoc pydoc3.10 python-config python3-config python3.10-config
2to3-3.10 idle3 pip pip3.10 pydoc3 python python3 python3.10 python3.10-gdb.py
和一个 pyenv-virtualenv
虚拟环境只有在虚拟环境目录结构中可以获得的可执行文件
$ pyenv virtualenv 3.10.4 venv
$ ls "${PYENV_ROOT}/versions/venv"
bin include lib lib64 pyvenv.cfg
$ ls "${PYENV_ROOT}/versions/venv/bin/"
Activate.ps1 activate activate.csh activate.fish pip pip3 pip3.10 pydoc python python3 python3.10
默认情况下,venv
虚拟环境在创建后不知道它所关联的 Python 版本的可执行文件,例如 2to3
$ pyenv activate venv
(venv) $ 2to3 --help
pyenv: 2to3: command not found
The `2to3' command exists in these Python versions:
3.10.4
Note: See 'pyenv help global' for tips on allowing both
python2 and python3 to be found.
因此,为了允许像 venv
这样的虚拟环境访问这些可执行文件,您将它和创建它的 Python 添加到 pyenv global
以便 pyenv
将 "fall back" 到 Python 找不到可执行文件时的版本
(venv) $ pyenv deactivate
$ pyenv global venv 3.10.4
(venv) $ pyenv global
venv
3.10.4
(venv) $ 2to3 --help | head -n 3
Usage: 2to3 [options] file|dir ...
Options:
此模式适用于一个虚拟环境,但是当您有多个虚拟环境时,如何保持对 2to3
(或 pipx
等可执行文件的访问,如下所示`)?
(venv) $ pyenv virtualenv 3.10.4 example && pyenv activate example
(example) $ 2to3
pyenv: 2to3: command not found
The `2to3' command exists in these Python versions:
3.10.4
Note: See 'pyenv help global' for tips on allowing both
python2 and python3 to be found.
可重现的例子
使用以下 Dockerfile
FROM debian:bullseye
SHELL ["/bin/bash", "-c"]
USER root
RUN apt-get update -y && \
apt-get install --no-install-recommends -y \
make \
build-essential \
libssl-dev \
zlib1g-dev \
libbz2-dev \
libreadline-dev \
libsqlite3-dev \
wget \
curl \
llvm \
libncurses5-dev \
xz-utils \
tk-dev \
libxml2-dev \
libxmlsec1-dev \
libffi-dev \
liblzma-dev \
g++ && \
apt-get install -y \
git && \
apt-get -y clean && \
apt-get -y autoremove && \
rm -rf /var/lib/apt/lists/*
# Install pyenv and pyenv-virtualenv
ENV PYENV_RELEASE_VERSION=2.3.0
RUN git clone --depth 1 https://github.com/pyenv/pyenv.git \
--branch "v${PYENV_RELEASE_VERSION}" \
--single-branch \
~/.pyenv && \
pushd ~/.pyenv && \
src/configure && \
make -C src && \
echo 'export PYENV_ROOT="${HOME}/.pyenv"' >> ~/.bashrc && \
echo 'export PATH="${PYENV_ROOT}/bin:${PATH}"' >> ~/.bashrc && \
echo 'eval "$(pyenv init -)"' >> ~/.bashrc && \
. ~/.bashrc && \
git clone --depth 1 https://github.com/pyenv/pyenv-virtualenv.git $(pyenv root)/plugins/pyenv-virtualenv && \
echo 'eval "$(pyenv virtualenv-init -)"' >> ~/.bashrc
# Install CPython
ENV PYTHON_VERSION=3.10.4
RUN . ~/.bashrc && \
echo "Install Python ${PYTHON_VERSION}" && \
PYTHON_MAKE_OPTS="-j8" pyenv install "${PYTHON_VERSION}"
# Make 'base' virtual envirionment, add it and its Python version to global for
# executables like 2to3 or pipx to be findable
# c.f. https://github.com/pyenv/pyenv-virtualenv/issues/16#issuecomment-37640961
# and then install pipx into the 'base' virtual environment and use pipx to install
# pepotron
RUN . ~/.bashrc && \
pyenv virtualenv "${PYTHON_VERSION}" base && \
echo "" && echo "Python ${PYTHON_VERSION} has additional executables..." && \
ls -lh "${PYENV_ROOT}/versions/${PYTHON_VERSION}/bin" && \
echo "" && echo "...compared to 'base' virtualenv made with Python ${PYTHON_VERSION}" && \
ls -lh "${PYENV_ROOT}/versions/base/bin" && \
echo "" && echo "...because 'base' is actually a symlink" && \
ls -lh "${PYENV_ROOT}/versions/" && \
pyenv global base "${PYTHON_VERSION}" && \
python -m pip --quiet install --upgrade pip setuptools wheel && \
python -m pip --quiet install pipx && \
python -m pipx ensurepath && \
eval "$(register-python-argcomplete pipx)" && \
pipx install pepotron
WORKDIR /home/data
构建于
docker build . --file Dockerfile --tag pyenv/multiple-virtualenvs:debug
可以运行用下面的来演示问题
$ docker run --rm -ti pyenv/multiple-virtualenvs:debug
(base) root@26dfa530cd82:/home/data# pyenv global
base
3.10.4
(base) root@26dfa530cd82:/home/data# 2to3 --help | head -n 3
Usage: 2to3 [options] file|dir ...
Options:
(base) root@26dfa530cd82:/home/data# pipx list
venvs are in /root/.local/pipx/venvs
apps are exposed on your $PATH at /root/.local/bin
package pepotron 0.6.0, installed using Python 3.10.4
- bpo
- pep
(base) root@26dfa530cd82:/home/data# pep 3.11
https://peps.python.org/pep-0664/
(base) root@26dfa530cd82:/home/data# pyenv virtualenv 3.10.4 example
(base) root@26dfa530cd82:/home/data# pyenv activate example
pyenv-virtualenv: prompt changing will be removed from future release. configure `export PYENV_VIRTUALENV_DISABLE_PROMPT=1' to simulate the behavior.
(example) root@26dfa530cd82:/home/data# 2to3
pyenv: 2to3: command not found
The `2to3' command exists in these Python versions:
3.10.4
Note: See 'pyenv help global' for tips on allowing both
python2 and python3 to be found.
(example) root@26dfa530cd82:/home/data# pipx
pyenv: pipx: command not found
The `pipx' command exists in these Python versions:
3.10.4/envs/base
base
Note: See 'pyenv help global' for tips on allowing both
python2 and python3 to be found.
那么,如果安装在 pyenv-virtualenv
虚拟环境中,那么像 pipx
这样设计用于全局安装的东西怎么能在全球范围内工作,这样您就不必拥有在系统 Python?
pip
的任何东西
似乎您不需要使用 pyenv activate
来激活虚拟环境,而是需要停用任何虚拟环境,然后仅使用 pyenv global <virtual environment name> <virtual environment Python version>
来有效地切换环境。我假设这不是在虚拟环境中使用 Python 版本可执行文件的唯一方法,因为这似乎会消除为pyenv-virtualenv
.
你可以直接在pyenv前缀中执行pipx
二进制文件,它应该可以正常工作。
Pyenv 的 shims 机制并不是真正为这样的全局二进制文件设计的。我天真地期望当本地环境没有安装二进制文件时,全局环境会作为回退,但我认为 pyenv 在回退到 $PATH.
之前只查看系统 Python因此,如果您不将 pipx
安装到 system
(如果您没有安装系统 pip
,我怀疑您正在安装),那么天真的回退不起作用。
另一种方法是 运行 pyenv
使用临时环境,即
PYENV_VERSION=my-pipx-env pyenv exec pipx
.
我想让它成为一个可执行文件,所以我建议将类似这样的东西添加到优先于 pyenv 路径的特殊 PATH 目录中:
#!/usr/bin/env bash
set -eu
export PYENV_VERSION="pipx"
exec "${PYENV_ROOT}/libexec/pyenv" exec pipx "$@"
不过,我很想放弃整个激活逻辑,直接 exec
来自环境 /bin
的 pipx
二进制文件,以避免 运行 进入任何 shell 配置错误。