Path: blob/main/test/lib/python3.9/site-packages/setuptools/command/test.py
4799 views
import os1import operator2import sys3import contextlib4import itertools5import unittest6from distutils.errors import DistutilsError, DistutilsOptionError7from distutils import log8from unittest import TestLoader910from pkg_resources import (11resource_listdir,12resource_exists,13normalize_path,14working_set,15evaluate_marker,16add_activation_listener,17require,18)19from .._importlib import metadata20from setuptools import Command21from setuptools.extern.more_itertools import unique_everseen22from setuptools.extern.jaraco.functools import pass_none232425class ScanningLoader(TestLoader):26def __init__(self):27TestLoader.__init__(self)28self._visited = set()2930def loadTestsFromModule(self, module, pattern=None):31"""Return a suite of all tests cases contained in the given module3233If the module is a package, load tests from all the modules in it.34If the module has an ``additional_tests`` function, call it and add35the return value to the tests.36"""37if module in self._visited:38return None39self._visited.add(module)4041tests = []42tests.append(TestLoader.loadTestsFromModule(self, module))4344if hasattr(module, "additional_tests"):45tests.append(module.additional_tests())4647if hasattr(module, '__path__'):48for file in resource_listdir(module.__name__, ''):49if file.endswith('.py') and file != '__init__.py':50submodule = module.__name__ + '.' + file[:-3]51else:52if resource_exists(module.__name__, file + '/__init__.py'):53submodule = module.__name__ + '.' + file54else:55continue56tests.append(self.loadTestsFromName(submodule))5758if len(tests) != 1:59return self.suiteClass(tests)60else:61return tests[0] # don't create a nested suite for only one return626364# adapted from jaraco.classes.properties:NonDataProperty65class NonDataProperty:66def __init__(self, fget):67self.fget = fget6869def __get__(self, obj, objtype=None):70if obj is None:71return self72return self.fget(obj)737475class test(Command):76"""Command to run unit tests after in-place build"""7778description = "run unit tests after in-place build (deprecated)"7980user_options = [81('test-module=', 'm', "Run 'test_suite' in specified module"),82(83'test-suite=',84's',85"Run single test, case or suite (e.g. 'module.test_suite')",86),87('test-runner=', 'r', "Test runner to use"),88]8990def initialize_options(self):91self.test_suite = None92self.test_module = None93self.test_loader = None94self.test_runner = None9596def finalize_options(self):9798if self.test_suite and self.test_module:99msg = "You may specify a module or a suite, but not both"100raise DistutilsOptionError(msg)101102if self.test_suite is None:103if self.test_module is None:104self.test_suite = self.distribution.test_suite105else:106self.test_suite = self.test_module + ".test_suite"107108if self.test_loader is None:109self.test_loader = getattr(self.distribution, 'test_loader', None)110if self.test_loader is None:111self.test_loader = "setuptools.command.test:ScanningLoader"112if self.test_runner is None:113self.test_runner = getattr(self.distribution, 'test_runner', None)114115@NonDataProperty116def test_args(self):117return list(self._test_args())118119def _test_args(self):120if not self.test_suite and sys.version_info >= (2, 7):121yield 'discover'122if self.verbose:123yield '--verbose'124if self.test_suite:125yield self.test_suite126127def with_project_on_sys_path(self, func):128"""129Backward compatibility for project_on_sys_path context.130"""131with self.project_on_sys_path():132func()133134@contextlib.contextmanager135def project_on_sys_path(self, include_dists=[]):136self.run_command('egg_info')137138# Build extensions in-place139self.reinitialize_command('build_ext', inplace=1)140self.run_command('build_ext')141142ei_cmd = self.get_finalized_command("egg_info")143144old_path = sys.path[:]145old_modules = sys.modules.copy()146147try:148project_path = normalize_path(ei_cmd.egg_base)149sys.path.insert(0, project_path)150working_set.__init__()151add_activation_listener(lambda dist: dist.activate())152require('%s==%s' % (ei_cmd.egg_name, ei_cmd.egg_version))153with self.paths_on_pythonpath([project_path]):154yield155finally:156sys.path[:] = old_path157sys.modules.clear()158sys.modules.update(old_modules)159working_set.__init__()160161@staticmethod162@contextlib.contextmanager163def paths_on_pythonpath(paths):164"""165Add the indicated paths to the head of the PYTHONPATH environment166variable so that subprocesses will also see the packages at167these paths.168169Do this in a context that restores the value on exit.170"""171nothing = object()172orig_pythonpath = os.environ.get('PYTHONPATH', nothing)173current_pythonpath = os.environ.get('PYTHONPATH', '')174try:175prefix = os.pathsep.join(unique_everseen(paths))176to_join = filter(None, [prefix, current_pythonpath])177new_path = os.pathsep.join(to_join)178if new_path:179os.environ['PYTHONPATH'] = new_path180yield181finally:182if orig_pythonpath is nothing:183os.environ.pop('PYTHONPATH', None)184else:185os.environ['PYTHONPATH'] = orig_pythonpath186187@staticmethod188def install_dists(dist):189"""190Install the requirements indicated by self.distribution and191return an iterable of the dists that were built.192"""193ir_d = dist.fetch_build_eggs(dist.install_requires)194tr_d = dist.fetch_build_eggs(dist.tests_require or [])195er_d = dist.fetch_build_eggs(196v197for k, v in dist.extras_require.items()198if k.startswith(':') and evaluate_marker(k[1:])199)200return itertools.chain(ir_d, tr_d, er_d)201202def run(self):203self.announce(204"WARNING: Testing via this command is deprecated and will be "205"removed in a future version. Users looking for a generic test "206"entry point independent of test runner are encouraged to use "207"tox.",208log.WARN,209)210211installed_dists = self.install_dists(self.distribution)212213cmd = ' '.join(self._argv)214if self.dry_run:215self.announce('skipping "%s" (dry run)' % cmd)216return217218self.announce('running "%s"' % cmd)219220paths = map(operator.attrgetter('location'), installed_dists)221with self.paths_on_pythonpath(paths):222with self.project_on_sys_path():223self.run_tests()224225def run_tests(self):226test = unittest.main(227None,228None,229self._argv,230testLoader=self._resolve_as_ep(self.test_loader),231testRunner=self._resolve_as_ep(self.test_runner),232exit=False,233)234if not test.result.wasSuccessful():235msg = 'Test failed: %s' % test.result236self.announce(msg, log.ERROR)237raise DistutilsError(msg)238239@property240def _argv(self):241return ['unittest'] + self.test_args242243@staticmethod244@pass_none245def _resolve_as_ep(val):246"""247Load the indicated attribute value, called, as a as if it were248specified as an entry point.249"""250return metadata.EntryPoint(value=val, name=None, group=None).load()()251252253