comparison env/lib/python3.9/site-packages/setuptools/command/test.py @ 0:4f3585e2f14b draft default tip

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