How can I activate a pyvenv virtualenv from within python? (activate_this.py was removed?)

9.4k views Asked by At

I'm using Python 3.4, and having created a pyvenv, I'm looking to activate it from within a python process. With virtualenv, I used to use activate_this.py, but that appears to be gone in pyvenv.

Is there now an easy way to effectively change the current interpreter to the virtualenv interpreter? I could probably mess around with the PATH (which is what activate_this.py did), but I'd like a simpler and stabler way.

This is for use in a wsgi.py.

3

There are 3 answers

1
warvariuc On

I used a different approach used in virtualenv itself:

# the current Python interpreter is not from the virtual environment
file = __file__
if file.endswith('.pyc'):
    file = file[:-1]
venv_executable = PROJECT_DIR / 'venv' / 'bin' / 'python'
popen = subprocess.Popen([venv_executable, file] + sys.argv[1:])
raise SystemExit(popen.wait())
0
Jorj On

save the following as the file activate_this.py

# -*- coding: utf-8 -*-
"""Activate virtualenv for current interpreter:

Use exec(open(this_file).read(), {'__file__': this_file}).

This can be used when you must use an existing Python interpreter, not the virtualenv bin/python.
"""
import os
import site
import sys

try:
    abs_file = os.path.abspath(__file__)
except NameError:
    raise AssertionError("You must use exec(open(this_file).read(), {'__file__': this_file}))")

bin_dir = os.path.dirname(abs_file)
base = bin_dir[: -len("__BIN_NAME__") - 1]  # strip away the bin part from the __file__, plus the path separator

# prepend bin to PATH (this file is inside the bin directory)
os.environ["PATH"] = os.pathsep.join([bin_dir] + os.environ.get("PATH", "").split(os.pathsep))
os.environ["VIRTUAL_ENV"] = base  # virtual env is right above bin directory

# add the virtual environments libraries to the host python import mechanism
prev_length = len(sys.path)
for lib in "__LIB_FOLDERS__".split(os.pathsep):
    path = os.path.realpath(os.path.join(bin_dir, lib))
    site.addsitedir(path.decode("utf-8") if "__DECODE_PATH__" else path)
sys.path[:] = sys.path[prev_length:] + sys.path[0:prev_length]

sys.real_prefix = sys.prefix
sys.prefix = base
3
ShadowRanger On

pyvenv and the venv module don't support this out of the box. The third party virtualenv package does support this using activate_this.py, but that feature was not included in the built-in venv module.

You could try to borrow a copy of activate_this.py from a virtualenv based environment; it seems to work, though I can't swear it will be perfect (venv/pyvenv uses some magic during startup; unclear if all of it is replicated via activate_this.py).

The virtualenv docs for it are out of date for Python 3 (they claim you use execfile, which doesn't exist). The Python 3 compatible alternative would be:

activator = 'some/path/to/activate_this.py'  # Looted from virtualenv; should not require modification, since it's defined relatively
with open(activator) as f:
    exec(f.read(), {'__file__': activator})

Nothing activate_this.py does is magical, so you could manually perform the same changes without looting from virtualenv (adjusting PATH, sys.path, sys.prefix, etc.), but borrowing makes it much simpler in this case.