24

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.

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
Chris Cooper
  • 17,276
  • 9
  • 52
  • 70
  • Yes, the activation script depends on your platform: https://docs.python.org/3/library/venv.html – Simeon Visser Dec 13 '14 at 19:13
  • 1
    Also, activating within a Python process doesn't entirely make sense. You can only activate a virtualenv and then afterwards use the Python interpreter that belongs to that virtualenv. – Simeon Visser Dec 13 '14 at 19:19
  • I think if you read here, you'll see why it makes sense: http://virtualenv.readthedocs.org/en/latest/virtualenv.html#using-virtualenv-without-bin-python This is the feature I'm looking for which appears to be gone in pyvenv. – Chris Cooper Dec 13 '14 at 19:35
  • Hmm, that's true. It could be a missing feature; Python 3.x isn't used much yet so it could be a legitimate missing feature. – Simeon Visser Dec 13 '14 at 20:28
  • Could be. I'll probably just end up messing with Apache's httpd.conf and try to point it at the right interpreter/python home. – Chris Cooper Dec 13 '14 at 20:49
  • 1
    @ChrisCooper the above link is broken. Which version of virtualenv? I got 12.1.0 and `activate_this.py` is there for me or I didn't get something right from your question. – Wtower Apr 24 '15 at 12:55
  • 2
    "Python 3.x isn't used much yet" That is definitely not true. – Harald Nordgren Jan 22 '17 at 19:30

3 Answers3

9

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.

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
  • Thanks, I looted it from [here](https://github.com/pypa/virtualenv/blob/master/virtualenv_embedded/activate_this.py) as [pyenv-virtualenv](https://github.com/pyenv/pyenv-virtualenv) did not seem to have one. – cardamom Jul 03 '17 at 17:35
1

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())
warvariuc
  • 57,116
  • 41
  • 173
  • 227
0

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
Jorj
  • 2,495
  • 1
  • 19
  • 12