How to use a python virtualenv with sudo?

I am trying to make a python environment separate from the system one (using virtualenv) in a directory which is not under /home, because I need to use it to build other software that has to be accessible to multiple users. I can create and activate the virtualenv all right, but when I sudo some command (for example to make or compile other software), it is the system python that is used (I can tell because of the available modules).

Since on Ubuntu it is not a good thing to use the root user, is there a way to tell sudo to use a virtualenv? Or maybe this is not the correct approach, and I should make a completely new python installation?

I am using the 64bit version of Ubuntu 12.04 (and python 2.7).


Solution 1:

The issue is almost certainly that when you run sudo, the virtualenv environment variables, aliases, functions, etc aren't being carried over.

The solution would be to explicitly run the virtual environment's Python executable with sudo. For example if your virtualenv is ./AwesomeProject, then you could run sudo ./AwesomeProject/bin/python <script> to use the script with the virtualenv with root privileges.

Solution 2:

Depending on your particular use-case this may or may not solve your problem, but this wasn't yet mentioned here. Say, you want to run a Python script that was installed in your virtual environment, and you need to setuid(0) it, i.e. you want to run it as superuser. Say, your script is called myscript.py, then, you can do it like this:

(.venv) $ sudo -E env PATH=$PATH ./myscript.py

Explanation

(.venv) $ is the shell's prompt, you don't need to type it, it just shows that you are currently using some virtual environment, and you are not a privileged user.

sudo -E will tell sudo that when starting a process, it needs to copy the environment variables from your current shell environment. So, if you have any extra variables defined in .venv/bin/activate script, they should stick around while you are root.

env PATH=$PATH when using sudo -E not all environment variables are preserved, specifically, and most importantly, the PATH may be removed. The specifics of this behavior will depend on your sudoers file. This, however, will circumvent whatever's written in sudoers file, as it will copy the PATH explicitly into environment sudo creates for the new process. This is important because when your system resolves shebang comment in your myscript.py, it will look in the PATH variable, and the first directory to match the requirement for interpreter will be used. venv on the other hand, sets up the shell environment in such a way that the directory containing the symlink to the chosen Python interpreter appears first, thus causing system to locate the desired Python before any other Pythons on system path.

Solution 3:

Just stumbled across this and for others who may find the same issue, Ken is correct that the env variables are not being carried over. The solution I used was to add the following lines to my script. This has the added benefit of always loading the virtual environment directly from the script. (Meaning you can use the script with crontab or launchd without any other workarounds.)

base_dir = os.path.dirname(os.path.abspath(__file__))
activate_this = os.path.join(base_dir, 'venv/bin/activate_this.py')
execfile(activate_this, dict(__file__=activate_this))

More background here: https://virtualenv.pypa.io/en/latest/userguide.html#using-virtualenv-without-bin-python