comparison lib/python3.8/site-packages/setuptools/command/test.py @ 0:9e54283cc701 draft

"planemo upload commit d12c32a45bcd441307e632fca6d9af7d60289d44"
author guerler
date Mon, 27 Jul 2020 03:47:31 -0400
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:9e54283cc701
1 import os
2 import operator
3 import sys
4 import contextlib
5 import itertools
6 import unittest
7 from distutils.errors import DistutilsError, DistutilsOptionError
8 from distutils import log
9 from unittest import TestLoader
10
11 from setuptools.extern import six
12 from setuptools.extern.six.moves import map, filter
13
14 from pkg_resources import (resource_listdir, resource_exists, normalize_path,
15 working_set, _namespace_packages, evaluate_marker,
16 add_activation_listener, require, EntryPoint)
17 from setuptools import Command
18 from .build_py import _unique_everseen
19
20 __metaclass__ = type
21
22
23 class ScanningLoader(TestLoader):
24
25 def __init__(self):
26 TestLoader.__init__(self)
27 self._visited = set()
28
29 def loadTestsFromModule(self, module, pattern=None):
30 """Return a suite of all tests cases contained in the given module
31
32 If the module is a package, load tests from all the modules in it.
33 If the module has an ``additional_tests`` function, call it and add
34 the return value to the tests.
35 """
36 if module in self._visited:
37 return None
38 self._visited.add(module)
39
40 tests = []
41 tests.append(TestLoader.loadTestsFromModule(self, module))
42
43 if hasattr(module, "additional_tests"):
44 tests.append(module.additional_tests())
45
46 if hasattr(module, '__path__'):
47 for file in resource_listdir(module.__name__, ''):
48 if file.endswith('.py') and file != '__init__.py':
49 submodule = module.__name__ + '.' + file[:-3]
50 else:
51 if resource_exists(module.__name__, file + '/__init__.py'):
52 submodule = module.__name__ + '.' + file
53 else:
54 continue
55 tests.append(self.loadTestsFromName(submodule))
56
57 if len(tests) != 1:
58 return self.suiteClass(tests)
59 else:
60 return tests[0] # don't create a nested suite for only one return
61
62
63 # adapted from jaraco.classes.properties:NonDataProperty
64 class NonDataProperty:
65 def __init__(self, fget):
66 self.fget = fget
67
68 def __get__(self, obj, objtype=None):
69 if obj is None:
70 return self
71 return self.fget(obj)
72
73
74 class test(Command):
75 """Command to run unit tests after in-place build"""
76
77 description = "run unit tests after in-place build (deprecated)"
78
79 user_options = [
80 ('test-module=', 'm', "Run 'test_suite' in specified module"),
81 ('test-suite=', 's',
82 "Run single test, case or suite (e.g. 'module.test_suite')"),
83 ('test-runner=', 'r', "Test runner to use"),
84 ]
85
86 def initialize_options(self):
87 self.test_suite = None
88 self.test_module = None
89 self.test_loader = None
90 self.test_runner = None
91
92 def finalize_options(self):
93
94 if self.test_suite and self.test_module:
95 msg = "You may specify a module or a suite, but not both"
96 raise DistutilsOptionError(msg)
97
98 if self.test_suite is None:
99 if self.test_module is None:
100 self.test_suite = self.distribution.test_suite
101 else:
102 self.test_suite = self.test_module + ".test_suite"
103
104 if self.test_loader is None:
105 self.test_loader = getattr(self.distribution, 'test_loader', None)
106 if self.test_loader is None:
107 self.test_loader = "setuptools.command.test:ScanningLoader"
108 if self.test_runner is None:
109 self.test_runner = getattr(self.distribution, 'test_runner', None)
110
111 @NonDataProperty
112 def test_args(self):
113 return list(self._test_args())
114
115 def _test_args(self):
116 if not self.test_suite and sys.version_info >= (2, 7):
117 yield 'discover'
118 if self.verbose:
119 yield '--verbose'
120 if self.test_suite:
121 yield self.test_suite
122
123 def with_project_on_sys_path(self, func):
124 """
125 Backward compatibility for project_on_sys_path context.
126 """
127 with self.project_on_sys_path():
128 func()
129
130 @contextlib.contextmanager
131 def project_on_sys_path(self, include_dists=[]):
132 with_2to3 = not six.PY2 and getattr(
133 self.distribution, 'use_2to3', False)
134
135 if with_2to3:
136 # If we run 2to3 we can not do this inplace:
137
138 # Ensure metadata is up-to-date
139 self.reinitialize_command('build_py', inplace=0)
140 self.run_command('build_py')
141 bpy_cmd = self.get_finalized_command("build_py")
142 build_path = normalize_path(bpy_cmd.build_lib)
143
144 # Build extensions
145 self.reinitialize_command('egg_info', egg_base=build_path)
146 self.run_command('egg_info')
147
148 self.reinitialize_command('build_ext', inplace=0)
149 self.run_command('build_ext')
150 else:
151 # Without 2to3 inplace works fine:
152 self.run_command('egg_info')
153
154 # Build extensions in-place
155 self.reinitialize_command('build_ext', inplace=1)
156 self.run_command('build_ext')
157
158 ei_cmd = self.get_finalized_command("egg_info")
159
160 old_path = sys.path[:]
161 old_modules = sys.modules.copy()
162
163 try:
164 project_path = normalize_path(ei_cmd.egg_base)
165 sys.path.insert(0, project_path)
166 working_set.__init__()
167 add_activation_listener(lambda dist: dist.activate())
168 require('%s==%s' % (ei_cmd.egg_name, ei_cmd.egg_version))
169 with self.paths_on_pythonpath([project_path]):
170 yield
171 finally:
172 sys.path[:] = old_path
173 sys.modules.clear()
174 sys.modules.update(old_modules)
175 working_set.__init__()
176
177 @staticmethod
178 @contextlib.contextmanager
179 def paths_on_pythonpath(paths):
180 """
181 Add the indicated paths to the head of the PYTHONPATH environment
182 variable so that subprocesses will also see the packages at
183 these paths.
184
185 Do this in a context that restores the value on exit.
186 """
187 nothing = object()
188 orig_pythonpath = os.environ.get('PYTHONPATH', nothing)
189 current_pythonpath = os.environ.get('PYTHONPATH', '')
190 try:
191 prefix = os.pathsep.join(_unique_everseen(paths))
192 to_join = filter(None, [prefix, current_pythonpath])
193 new_path = os.pathsep.join(to_join)
194 if new_path:
195 os.environ['PYTHONPATH'] = new_path
196 yield
197 finally:
198 if orig_pythonpath is nothing:
199 os.environ.pop('PYTHONPATH', None)
200 else:
201 os.environ['PYTHONPATH'] = orig_pythonpath
202
203 @staticmethod
204 def install_dists(dist):
205 """
206 Install the requirements indicated by self.distribution and
207 return an iterable of the dists that were built.
208 """
209 ir_d = dist.fetch_build_eggs(dist.install_requires)
210 tr_d = dist.fetch_build_eggs(dist.tests_require or [])
211 er_d = dist.fetch_build_eggs(
212 v for k, v in dist.extras_require.items()
213 if k.startswith(':') and evaluate_marker(k[1:])
214 )
215 return itertools.chain(ir_d, tr_d, er_d)
216
217 def run(self):
218 self.announce(
219 "WARNING: Testing via this command is deprecated and will be "
220 "removed in a future version. Users looking for a generic test "
221 "entry point independent of test runner are encouraged to use "
222 "tox.",
223 log.WARN,
224 )
225
226 installed_dists = self.install_dists(self.distribution)
227
228 cmd = ' '.join(self._argv)
229 if self.dry_run:
230 self.announce('skipping "%s" (dry run)' % cmd)
231 return
232
233 self.announce('running "%s"' % cmd)
234
235 paths = map(operator.attrgetter('location'), installed_dists)
236 with self.paths_on_pythonpath(paths):
237 with self.project_on_sys_path():
238 self.run_tests()
239
240 def run_tests(self):
241 # Purge modules under test from sys.modules. The test loader will
242 # re-import them from the build location. Required when 2to3 is used
243 # with namespace packages.
244 if not six.PY2 and getattr(self.distribution, 'use_2to3', False):
245 module = self.test_suite.split('.')[0]
246 if module in _namespace_packages:
247 del_modules = []
248 if module in sys.modules:
249 del_modules.append(module)
250 module += '.'
251 for name in sys.modules:
252 if name.startswith(module):
253 del_modules.append(name)
254 list(map(sys.modules.__delitem__, del_modules))
255
256 test = unittest.main(
257 None, None, self._argv,
258 testLoader=self._resolve_as_ep(self.test_loader),
259 testRunner=self._resolve_as_ep(self.test_runner),
260 exit=False,
261 )
262 if not test.result.wasSuccessful():
263 msg = 'Test failed: %s' % test.result
264 self.announce(msg, log.ERROR)
265 raise DistutilsError(msg)
266
267 @property
268 def _argv(self):
269 return ['unittest'] + self.test_args
270
271 @staticmethod
272 def _resolve_as_ep(val):
273 """
274 Load the indicated attribute value, called, as a as if it were
275 specified as an entry point.
276 """
277 if val is None:
278 return
279 parsed = EntryPoint.parse("x=" + val)
280 return parsed.resolve()()