diff options
author | Jason R. Coombs <jaraco@jaraco.com> | 2016-09-18 10:29:13 -0400 |
---|---|---|
committer | Jason R. Coombs <jaraco@jaraco.com> | 2016-09-18 10:29:13 -0400 |
commit | 647f5d9a352919aedc99a6b69f22f85cb7f36455 (patch) | |
tree | 2d8f93109f7f467e1a6310dafc8e3e415789473f | |
parent | eb5243cfd0cd095826cc707f5da8e7a74127e084 (diff) | |
download | pytest-runner-647f5d9a352919aedc99a6b69f22f85cb7f36455.tar.gz |
Enable subprocesses to inherit the dependency context. Fixes #20.
-rw-r--r-- | CHANGES.rst | 9 | ||||
-rw-r--r-- | ptr.py | 84 |
2 files changed, 80 insertions, 13 deletions
diff --git a/CHANGES.rst b/CHANGES.rst index 80bc335..27c189b 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,12 @@ +2.10 +~~~~ + +* #20: Leverage technique in `setuptools 794 + <https://github.com/pypa/setuptools/issues/794>`_ + to populate PYTHONPATH during test runs such that + Python subprocesses will have a dependency context + comparable to the test runner. + 2.9 ~~~ @@ -6,6 +6,14 @@ import os as _os import shlex as _shlex import contextlib as _contextlib import sys as _sys +import operator as _operator +import itertools as _itertools + +try: + # ensure that map has the same meaning on Python 2 + from future_builtins import map +except ImportError: + pass import pkg_resources import setuptools.command.test as orig @@ -22,6 +30,11 @@ def _save_argv(repl=None): _sys.argv[:] = saved +@_contextlib.contextmanager +def null(): + yield + + class PyTest(orig.test): """ >>> import setuptools @@ -61,28 +74,73 @@ class PyTest(orig.test): and pkg_resources.evaluate_marker(marker) ) + @staticmethod + def _install_dists_compat(dist): + """ + Copy of install_dists from setuptools 27.3.0. + """ + ir_d = dist.fetch_build_eggs(dist.install_requires or []) + tr_d = dist.fetch_build_eggs(dist.tests_require or []) + return _itertools.chain(ir_d, tr_d) + + def install_dists(self, dist): + """ + Extend install_dists to include extras support + """ + i_d = getattr(orig.test, 'install_dists', self._install_dists_compat) + return _itertools.chain(i_d(dist), self.install_extra_dists(dist)) + + def install_extra_dists(self, dist): + """ + Install extras that are indicated by markers or + install all extras if '--extras' is indicated. + """ + extras_require = dist.extras_require or {} + + spec_extras = ( + (spec.partition(':'), reqs) + for spec, reqs in extras_require.items() + ) + matching_extras = ( + reqs + for (name, sep, marker), reqs in spec_extras + # never include extras that fail to pass marker eval + if marker and not self.marker_passes(marker) + # include unnamed extras or all if self.extras indicated + and (not name or self.extras) + ) + results = list(map(dist.fetch_build_eggs, matching_extras)) + return _itertools.chain.from_iterable(results) + + @staticmethod + def paths_on_pythonpath(paths): + """ + Backward compatibility for paths_on_pythonpath; + Returns a null context if paths_on_pythonpath is + not implemented in orig.test. + Note that this also means that the paths iterable + is never consumed, which incidentally means that + the None values from dist.fetch_build_eggs in + older Setuptools will be disregarded. + """ + try: + return orig.test.paths_on_pythonpath(paths) + except AttributeError: + return null() + def run(self): """ Override run to ensure requirements are available in this session (but don't install them anywhere). """ self._build_egg_fetcher() - if self.distribution.install_requires: - self.distribution.fetch_build_eggs(self.distribution.install_requires) - if self.distribution.tests_require: - self.distribution.fetch_build_eggs(self.distribution.tests_require) - extras_require = self.distribution.extras_require or {} - for spec, reqs in extras_require.items(): - name, sep, marker = spec.partition(':') - if marker and not self.marker_passes(marker): - continue - # always include unnamed extras - if not name or self.extras: - self.distribution.fetch_build_eggs(reqs) + installed_dists = self.install_dists(self.distribution) if self.dry_run: self.announce('skipping tests (dry run)') return - self.with_project_on_sys_path(self.run_tests) + paths = map(_operator.attrgetter('location'), installed_dists) + with self.paths_on_pythonpath(paths): + self.with_project_on_sys_path(self.run_tests) if self.result_code: raise SystemExit(self.result_code) return self.result_code |