30

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
  • 27,601
  • 42
  • 124
  • 225

3 Answers3

43
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).

robertklep
  • 174,329
  • 29
  • 336
  • 330
  • I avoided this approach because I've had situations in which `$VIRTUAL_ENV` was set, but (for some reason), not in `$PATH`... – kjo Mar 16 '13 at 20:37
  • 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? – robertklep Mar 16 '13 at 20:39
  • 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 Mar 16 '13 at 21:06
  • 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? – robertklep Mar 17 '13 at 06:22
  • I found this particular solution valid and worked well for me. Exactly what I was after. Thanks! :) – James Mills Jun 04 '13 at 01:47
  • 3
    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 Jan 09 '14 at 09:21
  • Might be a good idea to mention it in your answer that it can fail in some cases – ThiefMaster Jan 09 '14 at 10:07
17

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")')
Community
  • 1
  • 1
kjo
  • 27,601
  • 42
  • 124
  • 225
  • 2
    a optional command for check : `python -c 'import sys; print hasattr(sys, "real_prefix")' ` – JuanPablo Feb 14 '14 at 15:10
  • 2
    Add parentheses to support Python 3 `python -c 'import sys; print(sys.real_prefix)' 2>/dev/null && INVENV=1 || INVENV=0` – Christian Long Jun 15 '15 at 15:40
  • 2
    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 )' )` – BrendanSimon Dec 05 '19 at 04:25
  • @BrendanSimon looks fine to me. – Broshi Apr 30 '20 at 09:06
  • Works when run directly on the terminal, doesn't appear to be working from inside a bash script (running inside a venv)! – quanta Jun 11 '20 at 09:04
1

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
Kurt
  • 1,948
  • 2
  • 23
  • 22
  • Thanks for the code. BTW, I think that bash already maintains `OLDPWD`, which the same thing as `PREVPWD` in your code. – kjo Mar 16 '13 at 21:12