Commit 5e4eea7d authored by Jason R. Coombs's avatar Jason R. Coombs

In test command, add installed eggs to PYTHONPATH when invoking tests so that...

In test command, add installed eggs to PYTHONPATH when invoking tests so that subprocesses will also have the dependencies available. Fixes #794.
parent 22aa7670
...@@ -2,6 +2,13 @@ ...@@ -2,6 +2,13 @@
CHANGES CHANGES
======= =======
v27.3.0
-------
* #794: In test command, add installed eggs to PYTHONPATH
when invoking tests so that subprocesses will also have the
dependencies available.
v27.2.0 v27.2.0
------- -------
......
import os
import operator
import sys import sys
import contextlib import contextlib
from distutils.errors import DistutilsOptionError from distutils.errors import DistutilsOptionError
from unittest import TestLoader from unittest import TestLoader
from setuptools.extern import six from setuptools.extern import six
from setuptools.extern.six.moves import map from setuptools.extern.six.moves import map, filter
from pkg_resources import (resource_listdir, resource_exists, normalize_path, from pkg_resources import (resource_listdir, resource_exists, normalize_path,
working_set, _namespace_packages, working_set, _namespace_packages,
...@@ -112,7 +114,7 @@ class test(Command): ...@@ -112,7 +114,7 @@ class test(Command):
func() func()
@contextlib.contextmanager @contextlib.contextmanager
def project_on_sys_path(self): def project_on_sys_path(self, include_dists=[]):
with_2to3 = six.PY3 and getattr(self.distribution, 'use_2to3', False) with_2to3 = six.PY3 and getattr(self.distribution, 'use_2to3', False)
if with_2to3: if with_2to3:
...@@ -144,23 +146,57 @@ class test(Command): ...@@ -144,23 +146,57 @@ class test(Command):
old_modules = sys.modules.copy() old_modules = sys.modules.copy()
try: try:
sys.path.insert(0, normalize_path(ei_cmd.egg_base)) project_path = normalize_path(ei_cmd.egg_base)
sys.path.insert(0, project_path)
working_set.__init__() working_set.__init__()
add_activation_listener(lambda dist: dist.activate()) add_activation_listener(lambda dist: dist.activate())
require('%s==%s' % (ei_cmd.egg_name, ei_cmd.egg_version)) require('%s==%s' % (ei_cmd.egg_name, ei_cmd.egg_version))
yield with self.paths_on_pythonpath([project_path]):
yield
finally: finally:
sys.path[:] = old_path sys.path[:] = old_path
sys.modules.clear() sys.modules.clear()
sys.modules.update(old_modules) sys.modules.update(old_modules)
working_set.__init__() working_set.__init__()
@staticmethod
@contextlib.contextmanager
def paths_on_pythonpath(paths):
"""
Add the indicated paths to the head of the PYTHONPATH environment
variable so that subprocesses will also see the packages at
these paths.
Do this in a context that restores the value on exit.
"""
nothing = object()
orig_pythonpath = os.environ.get('PYTHONPATH', nothing)
current_pythonpath = os.environ.get('PYTHONPATH', '')
try:
prefix = os.pathsep.join(paths)
to_join = filter(None, [prefix, current_pythonpath])
new_path = os.pathsep.join(to_join)
if new_path:
os.environ['PYTHONPATH'] = new_path
yield
finally:
if orig_pythonpath is nothing:
os.environ.pop('PYTHONPATH', None)
else:
os.environ['PYTHONPATH'] = orig_pythonpath
def run(self): def run(self):
installed_dists = []
if self.distribution.install_requires: if self.distribution.install_requires:
self.distribution.fetch_build_eggs( installed_dists.extend(
self.distribution.install_requires) self.distribution.fetch_build_eggs(
self.distribution.install_requires,
))
if self.distribution.tests_require: if self.distribution.tests_require:
self.distribution.fetch_build_eggs(self.distribution.tests_require) installed_dists.extend(
self.distribution.fetch_build_eggs(
self.distribution.tests_require,
))
cmd = ' '.join(self._argv) cmd = ' '.join(self._argv)
if self.dry_run: if self.dry_run:
...@@ -168,8 +204,11 @@ class test(Command): ...@@ -168,8 +204,11 @@ class test(Command):
return return
self.announce('running "%s"' % cmd) self.announce('running "%s"' % cmd)
with self.project_on_sys_path():
self.run_tests() paths = map(operator.attrgetter('location'), installed_dists)
with self.paths_on_pythonpath(paths):
with self.project_on_sys_path():
self.run_tests()
def run_tests(self): def run_tests(self):
# Purge modules under test from sys.modules. The test loader will # Purge modules under test from sys.modules. The test loader will
......
...@@ -362,6 +362,7 @@ class Distribution(_Distribution): ...@@ -362,6 +362,7 @@ class Distribution(_Distribution):
) )
for dist in resolved_dists: for dist in resolved_dists:
pkg_resources.working_set.add(dist, replace=True) pkg_resources.working_set.add(dist, replace=True)
return resolved_dists
def finalize_options(self): def finalize_options(self):
_Distribution.finalize_options(self) _Distribution.finalize_options(self)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment