激活Python虚拟环境如何修改sys.path?

18

我使用以下命令创建Python虚拟环境:

python3 -m venv venv3

要激活,我运行 source venv3/bin/activate

venv3/bin/activate 看起来并不复杂:

# This file must be used with "source bin/activate" *from bash*
# you cannot run it directly

deactivate () {
    # reset old environment variables
    if [ -n "$_OLD_VIRTUAL_PATH" ] ; then
        PATH="$_OLD_VIRTUAL_PATH"
        export PATH
        unset _OLD_VIRTUAL_PATH
    fi
    if [ -n "$_OLD_VIRTUAL_PYTHONHOME" ] ; then
        PYTHONHOME="$_OLD_VIRTUAL_PYTHONHOME"
        export PYTHONHOME
        unset _OLD_VIRTUAL_PYTHONHOME
    fi

    # This should detect bash and zsh, which have a hash command that must
    # be called to get it to forget past commands.  Without forgetting
    # past commands the $PATH changes we made may not be respected
    if [ -n "$BASH" -o -n "$ZSH_VERSION" ] ; then
        hash -r
    fi

    if [ -n "$_OLD_VIRTUAL_PS1" ] ; then
        PS1="$_OLD_VIRTUAL_PS1"
        export PS1
        unset _OLD_VIRTUAL_PS1
    fi

    unset VIRTUAL_ENV
    if [ ! "$1" = "nondestructive" ] ; then
    # Self destruct!
        unset -f deactivate
    fi
}

# unset irrelevant variables
deactivate nondestructive

VIRTUAL_ENV="/home/pi/django-test/venv3"
export VIRTUAL_ENV

_OLD_VIRTUAL_PATH="$PATH"
PATH="$VIRTUAL_ENV/bin:$PATH"
export PATH

# unset PYTHONHOME if set
# this will fail if PYTHONHOME is set to the empty string (which is bad anyway)
# could use `if (set -u; : $PYTHONHOME) ;` in bash
if [ -n "$PYTHONHOME" ] ; then
    _OLD_VIRTUAL_PYTHONHOME="$PYTHONHOME"
    unset PYTHONHOME
fi

if [ -z "$VIRTUAL_ENV_DISABLE_PROMPT" ] ; then
    _OLD_VIRTUAL_PS1="$PS1"
    if [ "x(venv3) " != x ] ; then
    PS1="(venv3) $PS1"
    else
    if [ "`basename \"$VIRTUAL_ENV\"`" = "__" ] ; then
        # special case for Aspen magic directories
        # see http://www.zetadev.com/software/aspen/
        PS1="[`basename \`dirname \"$VIRTUAL_ENV\"\``] $PS1"
    else
        PS1="(`basename \"$VIRTUAL_ENV\"`)$PS1"
    fi
    fi
    export PS1
fi

# This should detect bash and zsh, which have a hash command that must
# be called to get it to forget past commands.  Without forgetting
# past commands the $PATH changes we made may not be respected
if [ -n "$BASH" -o -n "$ZSH_VERSION" ] ; then
    hash -r
fi

我可以看到它会修改 $PATH 和 $PS1,创建一个 deactivate 函数,甚至备份旧的变量以便在用户运行 deactivate 函数时恢复它们。所有这些都很有道理。

唯一我没看到的是 Python 的 sys.path 被修改的地方。在我的系统上,我看到的是:

虚拟环境外的 sys.path:

['', '/usr/lib/python35.zip', '/usr/lib/python3.5', '/usr/lib/python3.5/plat-arm-linux-gnueabihf', '/usr/lib/python3.5/lib-dynload', '/usr/local/lib/python3.5/dist-packages', '/usr/lib/python3/dist-packages']
在虚拟环境中的sys.path:
['', '/usr/lib/python35.zip', '/usr/lib/python3.5', '/usr/lib/python3.5/plat-arm-linux-gnueabihf', '/usr/lib/python3.5/lib-dynload', '/home/pi/django-test/venv3/lib/python3.5/site-packages']

很明显,sys.path在某个时刻以某种方式被修改了。这是有道理的,因为这就是Python知道在哪里找到已安装的第三方Python库的方法。我认为这是虚拟环境的主要功能,但我看不出它在哪里被设置了。

我并没有尝试去完成什么事情 - 大多数只是出于好奇。


我相信Python使用sys.prefix(它将引用虚拟环境)来初始化sys.path - chepner
我可以确认,在虚拟环境之外,我的系统上sys.prefix被设置为/usr,而在虚拟环境内,它被设置为/home/pi/django-test/venv3。是什么设置了这个变量?似乎activate脚本并没有这样做。sys.path如何使用这个变量? - John
sys.prefix 的值是由 Python 自己决定的,基于解释器的安装位置。当你激活虚拟环境时,你的 PATH 会被修改,以便 python 解析为存储在你的虚拟环境目录中的可执行文件(可能只是一个符号链接)。 - chepner
venv 如何创建修改后的可执行文件?从源代码来看,它似乎只是复制东西。 - segfault
2个回答

14

sys.path是在site.py中初始化的,它使用sys.prefix的相对路径进行设置,而sys.prefix是虚拟环境内Python可执行文件的路径。

如果虚拟环境是没有使用--system-site-packages选项创建的,则默认情况下,键include-system-site-packages的配置值在pyvenv.cfg中设置为false

virtualenv有一个相同的选项--system-site-packages,但它将写入一个名为no-global-site-packages.txt的文件作为标志,放置在venv的site目录中。

在Python启动期间,会执行site.py,它将检查pyvenv.cfg配置文件以设置sys.path

如果“pyvenv.cfg”(引导配置文件)包含键“include-system-site-packages”,其设置为不区分大小写的任何其他值,则不会搜索系统级前缀以获取站点程序包;否则,它们将被搜索。

如果使用virtualenv创建虚拟环境,则在venv中的site.py修改过的版本,它会检查文件no-global-site-packages.txt的存在性,如果该标志文件不存在,则系统范围内的站点包路径将被添加到sys.path中,该路径是从sys.real_prefix推断出来的。
更新2022:最新的virtualenv也使用pyvenv.cfg
希望这能回答您的问题。

为什么要假设 OP 正在使用 virtualenv 并基于此回答问题,如果问题明确说明环境是使用 python3 -m venv 创建的? - MestreLion

5
激活虚拟环境并不会改变sys.path路径。sys.path是在Python启动时确定的,具体请参见https://docs.python.org/3.7/library/sys.html#sys.path。虚拟环境通过调整PATH环境变量来改变在简单运行python命令时实际运行的解释器。

创建虚拟环境时,有一个可选参数称为 --system-site-packages,允许在虚拟环境内部的 Python 使用全局安装的第三方包。尽管我现在可以看到 Python 如何使用相对路径与可执行文件相关联来查找库,但是当使用 --system-site-packages 时,虚拟环境必须有一种方法来告诉 Python 包含/排除系统范围的 Python 库 - 对吗?此设置影响 sys.path。我也没有在 activate 中找到任何相关的东西。 - John

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接