comparison env/lib/python3.7/site-packages/setuptools/command/test.py @ 5:9b1c78e6ba9c draft default tip

"planemo upload commit 6c0a8142489327ece472c84e558c47da711a9142"
author shellac
date Mon, 01 Jun 2020 08:59:25 -0400
parents 79f47841a781
children
comparison
equal deleted inserted replaced
4:79f47841a781 5:9b1c78e6ba9c
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"
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 = six.PY3 and getattr(self.distribution, 'use_2to3', False)
133
134 if with_2to3:
135 # If we run 2to3 we can not do this inplace:
136
137 # Ensure metadata is up-to-date
138 self.reinitialize_command('build_py', inplace=0)
139 self.run_command('build_py')
140 bpy_cmd = self.get_finalized_command("build_py")
141 build_path = normalize_path(bpy_cmd.build_lib)
142
143 # Build extensions
144 self.reinitialize_command('egg_info', egg_base=build_path)
145 self.run_command('egg_info')
146
147 self.reinitialize_command('build_ext', inplace=0)
148 self.run_command('build_ext')
149 else:
150 # Without 2to3 inplace works fine:
151 self.run_command('egg_info')
152
153 # Build extensions in-place
154 self.reinitialize_command('build_ext', inplace=1)
155 self.run_command('build_ext')
156
157 ei_cmd = self.get_finalized_command("egg_info")
158
159 old_path = sys.path[:]
160 old_modules = sys.modules.copy()
161
162 try:
163 project_path = normalize_path(ei_cmd.egg_base)
164 sys.path.insert(0, project_path)
165 working_set.__init__()
166 add_activation_listener(lambda dist: dist.activate())
167 require('%s==%s' % (ei_cmd.egg_name, ei_cmd.egg_version))
168 with self.paths_on_pythonpath([project_path]):
169 yield
170 finally:
171 sys.path[:] = old_path
172 sys.modules.clear()
173 sys.modules.update(old_modules)
174 working_set.__init__()
175
176 @staticmethod
177 @contextlib.contextmanager
178 def paths_on_pythonpath(paths):
179 """
180 Add the indicated paths to the head of the PYTHONPATH environment
181 variable so that subprocesses will also see the packages at
182 these paths.
183
184 Do this in a context that restores the value on exit.
185 """
186 nothing = object()
187 orig_pythonpath = os.environ.get('PYTHONPATH', nothing)
188 current_pythonpath = os.environ.get('PYTHONPATH', '')
189 try:
190 prefix = os.pathsep.join(_unique_everseen(paths))
191 to_join = filter(None, [prefix, current_pythonpath])
192 new_path = os.pathsep.join(to_join)
193 if new_path:
194 os.environ['PYTHONPATH'] = new_path
195 yield
196 finally:
197 if orig_pythonpath is nothing:
198 os.environ.pop('PYTHONPATH', None)
199 else:
200 os.environ['PYTHONPATH'] = orig_pythonpath
201
202 @staticmethod
203 def install_dists(dist):
204 """
205 Install the requirements indicated by self.distribution and
206 return an iterable of the dists that were built.
207 """
208 ir_d = dist.fetch_build_eggs(dist.install_requires)
209 tr_d = dist.fetch_build_eggs(dist.tests_require or [])
210 er_d = dist.fetch_build_eggs(
211 v for k, v in dist.extras_require.items()
212 if k.startswith(':') and evaluate_marker(k[1:])
213 )
214 return itertools.chain(ir_d, tr_d, er_d)
215
216 def run(self):
217 installed_dists = self.install_dists(self.distribution)
218
219 cmd = ' '.join(self._argv)
220 if self.dry_run:
221 self.announce('skipping "%s" (dry run)' % cmd)
222 return
223
224 self.announce('running "%s"' % cmd)
225
226 paths = map(operator.attrgetter('location'), installed_dists)
227 with self.paths_on_pythonpath(paths):
228 with self.project_on_sys_path():
229 self.run_tests()
230
231 def run_tests(self):
232 # Purge modules under test from sys.modules. The test loader will
233 # re-import them from the build location. Required when 2to3 is used
234 # with namespace packages.
235 if six.PY3 and getattr(self.distribution, 'use_2to3', False):
236 module = self.test_suite.split('.')[0]
237 if module in _namespace_packages:
238 del_modules = []
239 if module in sys.modules:
240 del_modules.append(module)
241 module += '.'
242 for name in sys.modules:
243 if name.startswith(module):
244 del_modules.append(name)
245 list(map(sys.modules.__delitem__, del_modules))
246
247 test = unittest.main(
248 None, None, self._argv,
249 testLoader=self._resolve_as_ep(self.test_loader),
250 testRunner=self._resolve_as_ep(self.test_runner),
251 exit=False,
252 )
253 if not test.result.wasSuccessful():
254 msg = 'Test failed: %s' % test.result
255 self.announce(msg, log.ERROR)
256 raise DistutilsError(msg)
257
258 @property
259 def _argv(self):
260 return ['unittest'] + self.test_args
261
262 @staticmethod
263 def _resolve_as_ep(val):
264 """
265 Load the indicated attribute value, called, as a as if it were
266 specified as an entry point.
267 """
268 if val is None:
269 return
270 parsed = EntryPoint.parse("x=" + val)
271 return parsed.resolve()()