简体   繁体   中英

How does activating a python virtual environment modify sys.path?

I create my python virtual environment using:

python3 -m venv venv3

to activate, I source venv3/bin/activate .

venv3/bin/activate doesn't appear to be all that complex:

# 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

I can see it modifying $PATH, and $PS1, creating a deactivate function, and even backing up old variables that it modifies so it can restore them when the user runs the deactivate function. All this makes sense.

The one thing I don't see is where python's sys.path is modified. On my system, this is what I see:

sys.path outside of virtual environment:

['', '/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 inside of virtual environment:

['', '/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']

Clearly, sys.path gets modified at some point, somehow. This makes sense, since that's how python knows where to find the third-party python libraries that are installed. I would think that this is the main feature of the virtual environment, but I can't see where it gets set.

I'm not trying to accomplish anything - mostly just curious.

sys.path is initiated in site.py , it is set using the relative path of sys.prefix , which is the path of python executable inside the virtual environment.

if the virtual environment is created without option --system-site-packages , which is the default, the config value of key include-system-site-packages set to false in pyvenv.cfg .

virtualenv has an identical option --system-site-packages , but it will write a file named no-global-site-packages.txt into the site dir of venv as a flag.

during python startup, site.py is executed, it will check pyvenv.cfg config file to set sys.path :

If “pyvenv.cfg” (a bootstrap configuration file) contains the key “include-system-site-packages” set to anything other than “true” (case-insensitive), the system-level prefixes will not be searched for site-packages; otherwise they will.

if venv is created with virtualenv , site.py in venv is a modified version , it check the existence of file no-global-site-packages.txt , if this flag file not exists, system-wide site package path will be added to sys.path , which is infered from sys.real_prefix .

update 2022: lastest virtualenv also use pyvenv.cfg .

hope this could answer your question.

The short answer is that activating a virtual environment does not change sys.path . sys.path is determined once Python starts up; see https://docs.python.org/3.7/library/sys.html#sys.path . What the virtual environment does, by adjusting your PATH environment variable, is change what interpreter actually runs when you simply run python .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM