|
| 1 | +import os |
| 2 | +import operator |
1 | 3 | import sys
|
2 | 4 | import contextlib
|
3 | 5 | from distutils.errors import DistutilsOptionError
|
4 | 6 | from unittest import TestLoader
|
5 | 7 |
|
6 | 8 | from setuptools.extern import six
|
7 |
| -from setuptools.extern.six.moves import map |
| 9 | +from setuptools.extern.six.moves import map, filter |
8 | 10 |
|
9 | 11 | from pkg_resources import (resource_listdir, resource_exists, normalize_path,
|
10 | 12 | working_set, _namespace_packages,
|
@@ -112,7 +114,7 @@ def with_project_on_sys_path(self, func):
|
112 | 114 | func()
|
113 | 115 |
|
114 | 116 | @contextlib.contextmanager
|
115 |
| - def project_on_sys_path(self): |
| 117 | + def project_on_sys_path(self, include_dists=[]): |
116 | 118 | with_2to3 = six.PY3 and getattr(self.distribution, 'use_2to3', False)
|
117 | 119 |
|
118 | 120 | if with_2to3:
|
@@ -144,32 +146,69 @@ def project_on_sys_path(self):
|
144 | 146 | old_modules = sys.modules.copy()
|
145 | 147 |
|
146 | 148 | 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) |
148 | 151 | working_set.__init__()
|
149 | 152 | add_activation_listener(lambda dist: dist.activate())
|
150 | 153 | require('%s==%s' % (ei_cmd.egg_name, ei_cmd.egg_version))
|
151 |
| - yield |
| 154 | + with self.paths_on_pythonpath([project_path]): |
| 155 | + yield |
152 | 156 | finally:
|
153 | 157 | sys.path[:] = old_path
|
154 | 158 | sys.modules.clear()
|
155 | 159 | sys.modules.update(old_modules)
|
156 | 160 | working_set.__init__()
|
157 | 161 |
|
| 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 | + |
158 | 188 | def run(self):
|
| 189 | + installed_dists = [] |
159 | 190 | 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 | + )) |
162 | 195 | 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 | + )) |
164 | 200 |
|
165 | 201 | cmd = ' '.join(self._argv)
|
166 | 202 | if self.dry_run:
|
167 | 203 | self.announce('skipping "%s" (dry run)' % cmd)
|
168 | 204 | return
|
169 | 205 |
|
170 | 206 | 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() |
173 | 212 |
|
174 | 213 | def run_tests(self):
|
175 | 214 | # Purge modules under test from sys.modules. The test loader will
|
|
0 commit comments