I have a script go.py
, which needs to run unit tests (with nose) on several different modules, each with their own virtual envs.
How can I activate each virtual env prior to testing, and deactivate it after?
i.e. I want to do this (pseudo-code):
for fn in functions_to_test:
activate(path_to_env)
run_test(fn)
deactivate()
activate()
Inside a virtual env, there is ./bin/activate_this.py
This does what I want. So in go.py
I say
import os
activate_this_file = os.path.join(env_dir,'bin/deactivate_this.py')
execfile(activate_this_file, dict(__file__=activate_this_file))
run_test()
I've currently got run_test()
working using
suite_x = TestLoader().loadTestsFromName(test_module + ":" + test_class)
r = run(suite = suite_x, argv = [sys.argv[0], "--verbosity=0", "-s"])
deactivate
This is the part that I can't figure out.
What's the deactivation equivalent of env/bin/activate_this.py
?
Broader Context
Each module will be uploaded to AWS by go.py
as a lambda function. (Where 'lambda function' has a specific meaning in an AWS context, and is not related to lambda x:foo(x)
)
I want go.py
to run unit tests on each lambda function, inside their respective virtual env (since they'll be executed in those virtual envs once deployed to AWS). Each lambda function uses different libraries, hence they have different virtual envs.
The activate_this.py
script is not meant to be used to switch virtual environments in the middle of a computation. It is meant to be used ASAP at the start of your process and not be touched ever again. If you look at the content of the script you'll see that it does not take care to record anything for a future deactivation. Once the activate_this.py
script is done, what the state of the interpreter was before the script started is lost. Moreover the documentation also warns (with emphasis added):
Also, this cannot undo the activation of other environments, or modules that have been imported. You shouldn’t try to, for instance, activate an environment before a web request; you should activate one environment as early as possible, and not do it again in that process.
Instead of the approach you were hoping to use, I'd have the orchestrator spawn the python interpreter (with subprocess
) that is specific to the virtual environment that needs to be used and pass to it the test runner ("nosetests", presumably) with the arguments needed for it to find the tests it needs to run in that environment.
There isn't an easy, complete, and generic way to do this. The reason being that activate_this.py does not just modify the module search path, but it also does site configurations with site.addsitedir(), which may execute sitecustomize or usercustomize in the same the python process. In comparison, the shell script version of activate simply modifies environment variables and have each python process reexecute site customization themselves, so cleanup is significantly easier.
How to work around this issue? There are several possibilities:
You might want to run your tests under tox. This is the solution I think would be most preferred.
If you are sure that none of the packages in your virtualenv have irreversible sitecustomize/usercustomize, you can write a deactivate() that undoes virtualenv's modification to sys.path, os.environ, and sys.prefix or one that remembers those value in activate() so deactivate() can undo them.
You can fork or create a subprocess in activate()
before execfile("activate_this.py")
. To deactivate the virtual environment, simply return to parent process. You'll have to figure out how the child process can return the test results so the parent/main process can compile the final test report.