23

I'm a novice in shell scripting, but I want to make a bash script for activate/deactivate a virtual enviroment using virtualenv. Then I want to use this script like a service in Ubuntu copying it inside /etc/init.d folder.

In my script, I have a variable like this: VENV=/opt/odoo/odoo_server/venv_oddo/bin

This variable represents the bin path in my virtual enviroment.

Inside the script, I can activate the virtual enviroment with this statement: . ${VENV}/activate

This is possible because activate is a file inside bin directory in the virtual enviroment.

But I don't know the statement to use in my script to deactivate my virtual enviroment. I can't do this: . ${VENV}/deactivate

The problem is that doesn't exist a file named deactivate, but deactivated is a function inside the bin/activate file in the virtual enviroment.

edkalel
  • 415
  • 2
  • 5
  • 12
  • 3
    If your script runs in a subprocess, the virtualenv will cease to exist when the script exists. Explicitly calling `deactivate` before exit is neither necessary nor useful. – tripleee Sep 27 '15 at 11:59

6 Answers6

11

Just deactivate. It will work in the script as well as in command line, as long as you're using bash.

Edit: also in most cases it is a better idea to spell full python path in your scripts and services. It is stateless, more portable and works pretty much everywhere. So instead of doing

. $VENV/bin/activate
/path/to/my/script.py --parameters

it is usually preferable to do

$VENV/bin/python /path/to/my/script --parameters

Trust me, it will save you debugging time)

thule
  • 4,034
  • 21
  • 31
  • 8
    Thank you for your answer, but if I use `deactivate` inside of the bash script, when executing it, I get the following message in the shell: `deactivate: command not found` – edkalel Apr 12 '15 at 14:32
  • 6
    You cannot use `deactivate` in a shell script without first sourcing the script that defines this function . See my separate answer on this question. – Andreas Maier Feb 14 '17 at 10:17
10

It'll be hard to make a service like that useful.

. ${VENV}/activate # note the dot

or

source ${VENV}/activate

will source the activate script, i.e. run its contents as if they were part of the shell or script where you source them. virtualenvironment's activate is designed for this usage. In contrast, just executing the script normally with

${VENV}/activate # note: NO dot and NO 'source' command

will run its content in a subshell and won't have any useful effect.

However, your service script will already run in a subshell of its own. So except for any python commands you run as part of the service start process, it won't have any effect.

On the plus side, you won't even have to care about de-activating the environment, unless you want to run even more python stuff in the service start process, but outside of your virtualenv.

das-g
  • 9,718
  • 4
  • 38
  • 80
  • 1
    Thank you for your answer, but I don't understand when you said: _Just executing the script normally (so that its content will run in a subshell) won't have any useful effect._ – edkalel Apr 12 '15 at 14:40
  • 2
    I've edited my answer. @edkalel, is it now easier to understand? The key point is that `activate` only affects the shell/script where it's sourced, not the whole system. – das-g Apr 12 '15 at 15:53
  • 2
    Thank you for your explanation. I think that I've already understood it. Then, if I use `activate` inside my bash script (with . or source), the virtual enviroment will be activated only inside the script, not outside in the shell. – edkalel Apr 13 '15 at 17:15
9

The deactivate "command" provided by virtualenvwrapper is actually a shell function, likewise so for workon. If you have a virtual env active, you can list the names of these functions with typeset -F.

In order to use them in a script, they need to be defined there, because shell functions do not propagate to child shells.

To define these functions, source the virtualenvwrapper.sh script in the shell script where you intend to invoke these functions, e.g.:

source $(which virtualenvwrapper.sh)

That allows you to invoke these functions in your shell script like you would do in the shell:

deactivate

Update: What I described works for the other functions provided by virtualenvwrapper (e.g. workon). I incorrectly assumed it would work also for deactivate, but that one is a more complicated case, because it is a function that will be defined only in the shell where workon or activate was run.

Andreas Maier
  • 2,724
  • 1
  • 26
  • 30
  • This won't work for plain `virtualenv` or `python3 -m venv`. – MarSoft Jul 29 '17 at 01:18
  • Will probably have to use brasizy's method (although I don't like it). Actually I think it is usually enough to just restore $PATH. – MarSoft Jul 29 '17 at 01:30
2

copy deactivate code in ${VENV}/activate.

paste your ~/.bashrc

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
}

run command.

$ $VENV/activate
$ deactivate

I have selectively used without problems python 2.7 and python 3.5 in this way.

I want to know the reason for the negative evaluation.

brasizy
  • 41
  • 2
  • 1
    I think minuses are because (1) this solution is not clean, and (2) it doesn't directly solve the mentioned problem. Usually .bashrc is only sourced for interactive shell, while the question is for bash scripts. But I don't see any really clean solution. – MarSoft Jul 29 '17 at 01:32
  • UPD: it does not work, because _OLD_VIRTUAL_PATH is a local variable and not exported... Alas. No good solution. No solution at all. – MarSoft Jul 29 '17 at 01:36
2

Somehow deactivate also can't be found in my case (usually I work under far2l inside bash). I use the solution:

unset VIRTUAL_ENV & deactivate

After that pip -V is showing path in .local.

Unreal Qw
  • 81
  • 1
  • 2
0

If you only need to programatically disable / change virtualenv, you can use a shell function instead of a shell script. For example, put at the end of your ~/.bashrc or ~/.bash_aliases (if you have it set up) or ~/.zshrc or ~/.zsh_aliases (if you use zsh):

function ch() {

  # change this to your directory
  cd ~/git-things/my_other_py_project

  # this works, as a shell function won't spawn a subshell as the script would
  deactivate

  # re-source to change the virtualenv (my use case; change to fit yours)
  source .venv-myotherpyproject/bin/activate

}

Restart the shell or re-source the file you changed with source ~/.zsh_aliases and use the command ch to execute the function.

Iacchus
  • 2,671
  • 2
  • 29
  • 24