6

I need a python script that will activate a virtualenv, run another python program inside the virtualenv, and then close the virutalenv after the second python program closes. Here is my code:

import os
import subprocess
from subprocess import Popen

activate_dir = "C:/Users/JohnDoe/theprogram/Scripts/"
os.chdir(activate_dir)
subprocess.Popen(["activate.bat"])

cal_dir = "C:/Users/JohnDoe/theprogram/"
os.chdir(cal_dir)
os.system('python program_file.py')

However, when this code run, I get an import error from the program_file which means the virtualenv is not activated. How can I fix this?

Thanks

Edit: This is on a Windows environment.

GreenSaber
  • 1,118
  • 2
  • 26
  • 53
  • Possible duplicate of this question maybe? https://stackoverflow.com/questions/6943208/activate-a-virtualenv-with-a-python-script – BoboDarph Sep 05 '17 at 14:08
  • From my understanding, the question linked is about using a python script to exclusively activate a virtualenv. My questions adds to that by changing a directory and running another python file while the virtualenv is activated. – GreenSaber Sep 05 '17 at 14:11

2 Answers2

7

The issue is that you are creating a new process with subprocess.Popen(["activate.bat"]) that is using that virtual environment, you're not changing your environment. What you need to do is to either call the python script in the same process you span:

os.system("source activate;python -V")

Or you could write a shell script that starts the virtual environment and calls any python script you send to it. In bash (on linux) this would be:

#!/bin/bash
# start a virtual environment and call a python module
# usage: ./runVirenvPythonModule module.py
source activate
python $1 # this is the first cmd line argument passed in
Simon Hobbs
  • 990
  • 7
  • 11
  • Ah, nice answer! If I use the `os.system` command, how would I go from the `Scripts` dir where I call `activate` to the `program` dir where I call the python script? – GreenSaber Sep 05 '17 at 14:01
  • Please note the M$ Windows-style path in OP's code. And the fact that the activation is done via a batch file. Using bash syntax in windows will be interesting. – BoboDarph Sep 05 '17 at 14:03
4

I've found a method to detect, activate, and create (if needed) a virtual environment inside a Python script and run inside that virtual environment while remaining inside that script and without the use of shell commands issued from that script (except to display installed packages via pip list). Without the use of shell commands, the script becomes OS agnostic.

Here is some example code. You simply run it from whatever OS shell you are using (Windows, Linux, MacOS):

import os
import sys
import venv

def isvirtualenv():
    return sys.prefix != sys.base_prefix

def findfile(startdir, pattern):
    for root, dirs, files in os.walk(startdir):
        for name in files:
            if name.find(pattern) >= 0:
                return root + os.sep + name

    return None

venv_path = 'venv'            # This is an example path

if isvirtualenv():
    print('Already in virtual environment.')
else:
    if findfile(os.getcwd(), 'activate') is None:
        print('No virtual environment found. Creating one.')
        env = venv.EnvBuilder(with_pip = True)
        env.create(venv_path)
    else:
        print('Not in virtual environment. Virtual environment directory found.')

    # This is the heart of this script that puts you inside the virtual environment. 
    # There is no need to undo this. When this script ends, your original path will 
    # be restored.
    os.environ['PATH'] = os.path.dirname(findfile(os.getcwd(), 'activate')) + os.pathsep + os.environ['PATH']
    sys.path.insert(1, os.path.dirname(findfile(venv_path, 'easy_install.py')))

# Run your script inside the virtual environment from here
print(os.environ['PATH'])
os.system('pip list')
Johnas Cukier
  • 670
  • 1
  • 7
  • 11
  • At least on my system (Windows 10) and in my virtual environments, the last line will point not to `site-packages` within the virtual environment (I think that's the intention) but to `site-packages/setuptools/command`. Maybe a better alternative would be `findfile` for `setuptools` directory, directly? – Raúl Núñez de Arenas Coronado Mar 05 '22 at 20:12