How can a shell function know if it is running within a virtualenv?

20,608

Solution 1

Actually, I just found a similar question, from which one can easily derive an answer to this one:

Python: Determine if running inside virtualenv

E.g., a shell script can use something like

python -c 'import sys; print (sys.real_prefix)' 2>/dev/null && INVENV=1 || INVENV=0

(Thanks to Christian Long for showing how to make this solution work with Python 3 also.)

EDIT: Here's a more direct (hence clearer and cleaner) solution (taking a cue from JuanPablo's comment):

INVENV=$(python -c 'import sys; print ("1" if hasattr(sys, "real_prefix") else "0")')

Solution 2

if [[ "$VIRTUAL_ENV" != "" ]]
then
  INVENV=1
else
  INVENV=0
fi
// or shorter if you like:
[[ "$VIRTUAL_ENV" == "" ]]; INVENV=$?

EDIT: as @ThiefMaster mentions in the comments, in certain conditions (for instance, when starting a new shell – perhaps in tmux or screen – from within an active virtualenv) this check may fail (however, starting new shells from within a virtualenv may cause other issues as well, I wouldn't recommend it).

Solution 3

If you use virtualenvwrappers there are pre/post scripts that run that could set INVENV for you.

Or what I do, put the following in your your .bashrc, and make a file called .venv in your working directory (for django) so that the virtual env is automatically loaded when you cd into the directory

export PREVPWD=`pwd`
export PREVENV_PATH=

handle_virtualenv(){
    if [ "$PWD" != "$PREVPWD" ]; then
        PREVPWD="$PWD";
        if [ -n "$PREVENV_PATH" ]; then
            if [ "`echo "$PWD" | grep -c $PREVENV_PATH`" = "0"  ]; then
                deactivate
                unalias python 2> /dev/null
                PREVENV_PATH=
            fi
        fi

        # activate virtualenv dynamically
        if [ -e "$PWD/.venv" ] && [ "$PWD" != "$PREVENV_PATH" ]; then
            PREVENV_PATH="$PWD"
            workon `basename $PWD`
            if [ -e "manage.py" ]; then
                alias python='python manage.py shell_plus'
            fi
        fi
    fi
}

export PROMPT_COMMAND=handle_virtualenv
Share:
20,608
kjo
Author by

kjo

Updated on January 31, 2022

Comments

  • kjo
    kjo over 2 years

    How should a bash function test whether it is running inside a Python virtualenv?

    The two approaches that come to mind are:

    [[ "$(type -t deactivate)" != function ]]; INVENV=$?
    

    or

    [[ "x$(which python)" != "x$VIRTUAL_ENV/bin/python" ]]; INVENV=$?
    

    (Note: wanting $INVENV to be 1 if we're inside a virtualenv, and 0 otherwise, is what forces the backward-looking tests above.)

    Is there something less hacky?

  • kjo
    kjo about 11 years
    I avoided this approach because I've had situations in which $VIRTUAL_ENV was set, but (for some reason), not in $PATH...
  • robertklep
    robertklep about 11 years
    Not sure what $PATH has to do with it? Or do you mean you want to check if your current working directory is part of a virtualenv?
  • kjo
    kjo about 11 years
    sorry for my confusing comment; if $VIRTUAL_ENV is not in $PATH then the python executable that gets used by everything else will not be the one in the virtualenv. By itself, assigning a value to $VIRTUAL_ENV does nothing.
  • kjo
    kjo about 11 years
    Thanks for the code. BTW, I think that bash already maintains OLDPWD, which the same thing as PREVPWD in your code.
  • robertklep
    robertklep about 11 years
    I agree, but my snippet doesn't assign to $VIRTUAL_ENV, it checks for it. When it exists, it means a virtual environment is active (that variable is set by the activate script and unset by deactivate). Which is what you wanted, right?
  • James Mills
    James Mills almost 11 years
    I found this particular solution valid and worked well for me. Exactly what I was after. Thanks! :)
  • ThiefMaster
    ThiefMaster over 10 years
    This is not reliable. For example, in tmux sessions started while inside a virtualenv the venv is not active but $VIRTUAL_ENV is still set.
  • ThiefMaster
    ThiefMaster over 10 years
    Might be a good idea to mention it in your answer that it can fail in some cases
  • JuanPablo
    JuanPablo over 10 years
    a optional command for check : python -c 'import sys; print hasattr(sys, "real_prefix")'
  • Christian Long
    Christian Long almost 9 years
    Add parentheses to support Python 3 python -c 'import sys; print(sys.real_prefix)' 2>/dev/null && INVENV=1 || INVENV=0
  • BrendanSimon
    BrendanSimon over 4 years
    My python env (python3 -m venv venv) does not have the sys.real_prefix attribute. Would this be ok as an alternative? INVENV=$( python -c 'import sys ; print( 0 if sys.prefix == sys.base_prefix else 1 )' )
  • Broshi
    Broshi about 4 years
    @BrendanSimon looks fine to me.
  • quanta
    quanta almost 4 years
    Works when run directly on the terminal, doesn't appear to be working from inside a bash script (running inside a venv)!
  • Dostrelith
    Dostrelith about 2 years
    Running inside a simple bash script, this answer worked for me (you can also echo the $VIRTUAL_ENV for debugging). The accepted answer did not work from inside a bash script.
  • Dostrelith
    Dostrelith about 2 years
    You can also cast it to int() instead of the if statement: INVENV=$(python3 -c 'import sys; print(int(hasattr(sys, "real_prefix")))') However, running from a simple bash script I could not get confirmation that the script is using the venv. The answer below using $VIRTUAL_ENV variable (set by sourcing the activate script) did.