Reputation: 11068
I want to activate a virtualenv instance from a Python script.
I know it's quite easy to do, but all the examples I've seen use it to run commands within the env and then close the subprocess.
I simply want to activate the virtualenv and return to the shell, the same way that bin/activate does.
Something like this:
$me: my-script.py -d env-name
$(env-name)me:
Is this possible?
Relevant:
virtualenv › Invoking an env from a script
Upvotes: 142
Views: 211242
Reputation: 10290
First, let me say how I'm interpreting this question:
Is there a way that I can run a command which will cause a certain virtualenv (among several) to become active in my interactive shell?
And the short answer is:
Not without help from the shell -- because any command that you write in a normal way will be a sub-process, and will not be able to modify settings in the shell directly.
That said, there are ways to get help from the shell, so, for example, suppose one has a script that's something like the following (pseudo-code):
#!/usr/bin/env python3
import sys
# writing this function is an exercise for the reader...
# I don't know what you're wanting to do here, but I'll
# assume you have some way to choose a venv directory
# based on command-line arguments:
env_dir = choose_env_dir_based_on(sys.ARGV)
print(f". #{env_dir}/bin/activate")
Now, supposing (based on the question text) you want to be able to run this as my-script.py -d env-name
(I'd probably drop the .py
from that, but I'll run with it, since that's how the question was phrased), let's save that in a file called, say, ~/bin/_my-script.py
. From there, you need to teach your shell how to run that command in a way that can modify its own environment. You can do this in a couple of different ways. The easiestway is probably with an alias:
alias my-script.py='eval `~/bin/_myscript.py`'
However, this has the drawback that it doesn't have a good way to get arguments to it, so running my-script.py -d env-name
won't really work (though if you just need to run my-script.py
, then it would). Instead, one could do it with a shell function:
my-script.py () { eval `~/bin/_my-script.py "$@"` }
Whichever you go with, you'd presumably save that in your ~/.bashrc
, ~/.zshrc
, or whatever equivalent for the shell you happen to be using.
With the function form, this then allows one to type my-script.py -d env-name
, while having it actually do the following:
~/bin/_my-script.py
(with -d env-name
supplied as arguments).eval
it (see, e.g., this Q&A for more information), so that it runs within the context of your shell._my-script.py
actually does is to print out a shell command to source the activate script for the relevant directory, that will then happen.So, the above is all answering the question that was asked. I came to this answer trying to find a solution for a different question, though. My question could be summarized as:
Can I have a python script run as though it had been run with a particular venv already set?
For this, something like the following could be run (I found my own solution). For the sake of this example, I'll assume that the venv is in a directory called .env
that exists alongside the script that's being run.
#!/usr/bin/env python3
# the above "shebang" line references whatever python3
# we happen to have, whether or not an environment is set.
# But we want to run with the environment in .env relative
# to the current script, unless an env is activated (in
# which case, we'd use that, and do nothing special here):
import os, sys
if 'VIRTUAL_ENV' not in os.environ:
# assuming .env is next to this script:
venv = os.path.join(os.path.dirname(__file__), '.env')
# finding the python from that env:
newpython = os.path.join(venv, 'bin', 'python')
# setting up a new ARGV, to start with the new python:
args = [newpython] + sys.argv
# Note: setting VIRTUAL_ENV isn't what causes the
# virtualenv to be utilized. Python has some other means
# of doing that. But it does signal to the if condition
# above to not run this code more than once (lest we
# go into an infinite execv loop).
os.environ['VIRTUAL_ENV'] = venv
# With that all set up, we re-run under the new python:
os.execv(newpython, args)
import somemodule # something pip installed inside the venv.
# rest of code here
Or, for a terse version of just what we need to force the venv:
#!/usr/bin/env python3
import os, sys
if 'VIRTUAL_ENV' not in os.environ:
venv = os.path.join(os.path.dirname(__file__), '.env')
newpython = os.path.join(venv, 'bin', 'python')
args = [newpython] + sys.argv
os.environ['VIRTUAL_ENV'] = venv
os.execv(newpython, args)
("Reasonable" error messaging upon the lack of existence of the .env
directory is left as an exercise for the reader.
As-is, a FileNotFoundError
error will be raised when attempting to run os.execv
. What's "reasonable" is presumably dependent upon what one wants -- to automatically create the venv? To just report an error? Other? Hence leaving it as an exercise.)
Upvotes: 1
Reputation: 11
Related to this response about activate_this.py
If that's your use case (activate the virtualenv on the same process), you may be interested in using activate-virtualenv, a context manager that does the activation and deactivation of virtualenvs automatically, based on the activate_this.py
script.
Example of usage:
from activate_virtualenv import activate_virtualenv
venv_path = "/path/to/virtualenv"
with activate_virtualenv(venv_path):
import subscript1
Then "subscript1.py
" will be imported with the virtualenv activated. It will be able to load any modules installed on the activated virtualenv. AFter the with
block ends, the virtualenv will be automatically deactivated and the changes made by activate_this.py
will be undone, so it will not affect the rest of the script.
Upvotes: 1
Reputation: 64923
If you want to run a Python subprocess under the virtualenv, you can do that by running the script using the Python interpreter that lives inside virtualenv's bin/
directory:
import subprocess
# Path to a Python interpreter that runs any Python script
# under the virtualenv /path/to/virtualenv/
python_bin = "/path/to/virtualenv/bin/python"
# Path to the script that must run under the virtualenv
script_file = "must/run/under/virtualenv/script.py"
subprocess.Popen([python_bin, script_file])
However, if you want to activate the virtualenv under the current Python interpreter instead of a subprocess, you can call exec
passing the activate_this.py
script, like this:
# Doing exec() on this file will alter the current interpreter's
# environment so you can import libraries in the virtualenv
activate_this_file = "/path/to/virtualenv/bin/activate_this.py"
exec(open(activate_this_file).read(), {'__file__': activate_this_file})
Note that you need to use the virtualenv library, not venv, for the above.
If you use venv, you can copy the implementation of virtualenv's activate_this.py, it should still more or less work with venv with just a few minor changes.
Upvotes: 125
Reputation: 81
I had the same issue and there was no activate_this.py
in the Scripts
directory of my environment.
activate_this.py
"""By using execfile(this_file, dict(__file__=this_file)) you will
activate this virtualenv environment.
This can be used when you must use an existing Python interpreter, not
the virtualenv bin/python
"""
try:
__file__
except NameError:
raise AssertionError(
"You must run this like execfile('path/to/active_this.py', dict(__file__='path/to/activate_this.py'))")
import sys
import os
base = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if(sys.platform=='win32'):
site_packages = os.path.join(base, 'Lib', 'site-packages')
else:
site_packages = os.path.join(base, 'lib', 'python%s' % sys.version[:3], 'site-packages')
prev_sys_path = list(sys.path)
import site
site.addsitedir(site_packages)
sys.real_prefix = sys.prefix
sys.prefix = base
# Move the added items to the front of the path:
new_sys_path = []
for item in list(sys.path):
if item not in prev_sys_path:
new_sys_path.append(item)
sys.path.remove(item)
sys.path[:0] = new_sys_path
Copy the file to the Scripts
directory of your environment and use it like this:
def activate_virtual_environment(environment_root):
"""Configures the virtual environment starting at ``environment_root``."""
activate_script = os.path.join(
environment_root, 'Scripts', 'activate_this.py')
execfile(activate_script, {'__file__': activate_script})
activate_virtual_environment('path/to/your/venv')
Refrence: https://github.com/dcreager/virtualenv/blob/master/virtualenv_support/activate_this.py
Upvotes: 2
Reputation: 21
For python2/3, Using below code snippet we can activate virtual env.
activate_this = "/home/<--path-->/<--virtual env name -->/bin/activate_this.py" #for ubuntu
activate_this = "D:\<-- path -->\<--virtual env name -->\Scripts\\activate_this.py" #for windows
with open(activate_this) as f:
code = compile(f.read(), activate_this, 'exec')
exec(code, dict(__file__=activate_this))
Upvotes: 1
Reputation: 4759
To run another Python environment according to the official Virtualenv documentation, in the command line you can specify the full path to the executable Python binary, just that (no need to active the virtualenv before):
/path/to/virtualenv/bin/python
The same applies if you want to invoke a script from the command line with your virtualenv. You don't need to activate it before:
me$ /path/to/virtualenv/bin/python myscript.py
The same for a Windows environment (whether it is from the command line or from a script):
> \path\to\env\Scripts\python.exe myscript.py
Upvotes: 31
Reputation: 81
The top answer only works for Python 2.x
For Python 3.x, use this:
activate_this_file = "/path/to/virtualenv/bin/activate_this.py"
exec(compile(open(activate_this_file, "rb").read(), activate_this_file, 'exec'), dict(__file__=activate_this_file))
Reference: What is an alternative to execfile in Python 3?
Upvotes: 8
Reputation: 101
You should create all your virtualenv
s in one folder, such as virt
.
Assuming your virtualenv folder name is virt, if not change it
cd
mkdir custom
Copy the below lines...
#!/usr/bin/env bash
ENV_PATH="$HOME/virt/$1/bin/activate"
bash --rcfile $ENV_PATH -i
Create a shell script file and paste the above lines...
touch custom/vhelper
nano custom/vhelper
Grant executable permission to your file:
sudo chmod +x custom/vhelper
Now export that custom folder path so that you can find it on the command-line by clicking tab...
export PATH=$PATH:"$HOME/custom"
Now you can use it from anywhere by just typing the below command...
vhelper YOUR_VIRTUAL_ENV_FOLDER_NAME
Suppose it is abc then...
vhelper abc
Upvotes: -1
Reputation: 1267
The simplest solution to run your script under virtualenv's interpreter is to replace the default shebang line with path to your virtualenv's interpreter like so at the beginning of the script:
#!/path/to/project/venv/bin/python
Make the script executable:
chmod u+x script.py
Run the script:
./script.py
Voila!
Upvotes: 93
Reputation: 4929
Just a simple solution that works for me. I don't know why you need the Bash script which basically does a useless step (am I wrong ?)
import os
os.system('/bin/bash --rcfile flask/bin/activate')
Which basically does what you need:
[hellsing@silence Foundation]$ python2.7 pythonvenv.py
(flask)[hellsing@silence Foundation]$
Then instead of deactivating the virtual environment, just Ctrl + D or exit. Is that a possible solution or isn't that what you wanted?
Upvotes: 8
Reputation: 11068
It turns out that, yes, the problem is not simple, but the solution is.
First I had to create a shell script to wrap the "source" command. That said I used the "." instead, because I've read that it's better to use it than source for Bash scripts.
#!/bin/bash
. /path/to/env/bin/activate
Then from my Python script I can simply do this:
import os
os.system('/bin/bash --rcfile /path/to/myscript.sh')
The whole trick lies within the --rcfile
argument.
When the Python interpreter exits it leaves the current shell in the activated environment.
Win!
Upvotes: 25
Reputation: 83666
The child process environment is lost in the moment it ceases to exist, and moving the environment content from there to the parent is somewhat tricky.
You probably need to spawn a shell script (you can generate one dynamically to /tmp) which will output the virtualenv environment variables to a file, which you then read in the parent Python process and put in os.environ.
Or you simply parse the activate script in using for the line in open("bin/activate"), manually extract stuff, and put in os.environ. It is tricky, but not impossible.
Upvotes: 2