Skip to content

Commit 5e4eea7

Browse files
committed
In test command, add installed eggs to PYTHONPATH when invoking tests so that subprocesses will also have the dependencies available. Fixes #794.
1 parent 22aa767 commit 5e4eea7

File tree

3 files changed

+56
-9
lines changed

3 files changed

+56
-9
lines changed

CHANGES.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22
CHANGES
33
=======
44

5+
v27.3.0
6+
-------
7+
8+
* #794: In test command, add installed eggs to PYTHONPATH
9+
when invoking tests so that subprocesses will also have the
10+
dependencies available.
11+
512
v27.2.0
613
-------
714

setuptools/command/test.py

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
import os
2+
import operator
13
import sys
24
import contextlib
35
from distutils.errors import DistutilsOptionError
46
from unittest import TestLoader
57

68
from setuptools.extern import six
7-
from setuptools.extern.six.moves import map
9+
from setuptools.extern.six.moves import map, filter
810

911
from pkg_resources import (resource_listdir, resource_exists, normalize_path,
1012
working_set, _namespace_packages,
@@ -112,7 +114,7 @@ def with_project_on_sys_path(self, func):
112114
func()
113115

114116
@contextlib.contextmanager
115-
def project_on_sys_path(self):
117+
def project_on_sys_path(self, include_dists=[]):
116118
with_2to3 = six.PY3 and getattr(self.distribution, 'use_2to3', False)
117119

118120
if with_2to3:
@@ -144,32 +146,69 @@ def project_on_sys_path(self):
144146
old_modules = sys.modules.copy()
145147

146148
try:
147-
sys.path.insert(0, normalize_path(ei_cmd.egg_base))
149+
project_path = normalize_path(ei_cmd.egg_base)
150+
sys.path.insert(0, project_path)
148151
working_set.__init__()
149152
add_activation_listener(lambda dist: dist.activate())
150153
require('%s==%s' % (ei_cmd.egg_name, ei_cmd.egg_version))
151-
yield
154+
with self.paths_on_pythonpath([project_path]):
155+
yield
152156
finally:
153157
sys.path[:] = old_path
154158
sys.modules.clear()
155159
sys.modules.update(old_modules)
156160
working_set.__init__()
157161

162+
@staticmethod
163+
@contextlib.contextmanager
164+
def paths_on_pythonpath(paths):
165+
"""
166+
Add the indicated paths to the head of the PYTHONPATH environment
167+
variable so that subprocesses will also see the packages at
168+
these paths.
169+
170+
Do this in a context that restores the value on exit.
171+
"""
172+
nothing = object()
173+
orig_pythonpath = os.environ.get('PYTHONPATH', nothing)
174+
current_pythonpath = os.environ.get('PYTHONPATH', '')
175+
try:
176+
prefix = os.pathsep.join(paths)
177+
to_join = filter(None, [prefix, current_pythonpath])
178+
new_path = os.pathsep.join(to_join)
179+
if new_path:
180+
os.environ['PYTHONPATH'] = new_path
181+
yield
182+
finally:
183+
if orig_pythonpath is nothing:
184+
os.environ.pop('PYTHONPATH', None)
185+
else:
186+
os.environ['PYTHONPATH'] = orig_pythonpath
187+
158188
def run(self):
189+
installed_dists = []
159190
if self.distribution.install_requires:
160-
self.distribution.fetch_build_eggs(
161-
self.distribution.install_requires)
191+
installed_dists.extend(
192+
self.distribution.fetch_build_eggs(
193+
self.distribution.install_requires,
194+
))
162195
if self.distribution.tests_require:
163-
self.distribution.fetch_build_eggs(self.distribution.tests_require)
196+
installed_dists.extend(
197+
self.distribution.fetch_build_eggs(
198+
self.distribution.tests_require,
199+
))
164200

165201
cmd = ' '.join(self._argv)
166202
if self.dry_run:
167203
self.announce('skipping "%s" (dry run)' % cmd)
168204
return
169205

170206
self.announce('running "%s"' % cmd)
171-
with self.project_on_sys_path():
172-
self.run_tests()
207+
208+
paths = map(operator.attrgetter('location'), installed_dists)
209+
with self.paths_on_pythonpath(paths):
210+
with self.project_on_sys_path():
211+
self.run_tests()
173212

174213
def run_tests(self):
175214
# Purge modules under test from sys.modules. The test loader will

setuptools/dist.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,7 @@ def fetch_build_eggs(self, requires):
362362
)
363363
for dist in resolved_dists:
364364
pkg_resources.working_set.add(dist, replace=True)
365+
return resolved_dists
365366

366367
def finalize_options(self):
367368
_Distribution.finalize_options(self)

0 commit comments

Comments
 (0)