Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(105020)

Side by Side Diff: Lib/unittest/loader.py

Issue 17457: Unittest discover fails with namespace packages and builtin modules
Patch Set: Created 6 years ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | Lib/unittest/test/test_discovery.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 """Loading unittests.""" 1 """Loading unittests."""
2 2
3 import os 3 import os
4 import re 4 import re
5 import sys 5 import sys
6 import traceback 6 import traceback
7 import types 7 import types
8 import functools 8 import functools
9 9
10 from fnmatch import fnmatch 10 from fnmatch import fnmatch
(...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after
193 193
194 if not top_level_dir in sys.path: 194 if not top_level_dir in sys.path:
195 # all test modules must be importable from the top level directory 195 # all test modules must be importable from the top level directory
196 # should we *unconditionally* put the start directory in first 196 # should we *unconditionally* put the start directory in first
197 # in sys.path to minimise likelihood of conflicts between installed 197 # in sys.path to minimise likelihood of conflicts between installed
198 # modules and development versions? 198 # modules and development versions?
199 sys.path.insert(0, top_level_dir) 199 sys.path.insert(0, top_level_dir)
200 self._top_level_dir = top_level_dir 200 self._top_level_dir = top_level_dir
201 201
202 is_not_importable = False 202 is_not_importable = False
203 is_namespace = False
204 tests = []
203 if os.path.isdir(os.path.abspath(start_dir)): 205 if os.path.isdir(os.path.abspath(start_dir)):
204 start_dir = os.path.abspath(start_dir) 206 start_dir = os.path.abspath(start_dir)
205 if start_dir != top_level_dir: 207 if start_dir != top_level_dir:
206 is_not_importable = not os.path.isfile(os.path.join(start_dir, ' __init__.py')) 208 is_not_importable = not os.path.isfile(os.path.join(start_dir, ' __init__.py'))
207 else: 209 else:
208 # support for discovery from dotted module names 210 # support for discovery from dotted module names
209 try: 211 try:
210 __import__(start_dir) 212 __import__(start_dir)
211 except ImportError: 213 except ImportError:
212 is_not_importable = True 214 is_not_importable = True
213 else: 215 else:
214 the_module = sys.modules[start_dir] 216 the_module = sys.modules[start_dir]
215 top_part = start_dir.split('.')[0] 217 top_part = start_dir.split('.')[0]
216 start_dir = os.path.abspath(os.path.dirname((the_module.__file__ ))) 218 try:
219 start_dir = os.path.abspath(
220 os.path.dirname((the_module.__file__)))
221 except AttributeError:
222 # look for namespace packages
223 if hasattr(the_module.__loader__, '_path'):
eric.snow 2013/11/18 23:16:42 Using _path isn't okay. Once PEP 451 lands in the
224 is_namespace = True
225 for path in the_module.__loader__._path:
eric.snow 2013/11/18 23:16:42 Regardless of using the spec, you should not be us
226 if (not set_implicit_top and
227 not path.startswith(top_level_dir)):
228 continue
229 self._top_level_dir = \
230 (path.split(the_module.__name__
231 .replace(".", os.path.sep))[0])
232 tests.extend(self._find_tests(path,
233 pattern,
234 namespace=True))
235 else:
236 # builtin module
237 raise TypeError('Can not use builtin modules '
238 'as dotted module names') from None
239
217 if set_implicit_top: 240 if set_implicit_top:
218 self._top_level_dir = self._get_directory_containing_module( top_part) 241 if not is_namespace:
219 sys.path.remove(top_level_dir) 242 self._top_level_dir = \
243 self._get_directory_containing_module(top_part)
244 sys.path.remove(top_level_dir)
245 else:
246 sys.path.remove(top_level_dir)
220 247
221 if is_not_importable: 248 if is_not_importable:
222 raise ImportError('Start directory is not importable: %r' % start_di r) 249 raise ImportError('Start directory is not importable: %r' % start_di r)
223 250
224 tests = list(self._find_tests(start_dir, pattern)) 251 if not is_namespace:
252 tests = list(self._find_tests(start_dir, pattern))
225 return self.suiteClass(tests) 253 return self.suiteClass(tests)
226 254
227 def _get_directory_containing_module(self, module_name): 255 def _get_directory_containing_module(self, module_name):
228 module = sys.modules[module_name] 256 module = sys.modules[module_name]
229 full_path = os.path.abspath(module.__file__) 257 full_path = os.path.abspath(module.__file__)
230 258
231 if os.path.basename(full_path).lower().startswith('__init__.py'): 259 if os.path.basename(full_path).lower().startswith('__init__.py'):
232 return os.path.dirname(os.path.dirname(full_path)) 260 return os.path.dirname(os.path.dirname(full_path))
233 else: 261 else:
234 # here we have been given a module rather than a package - so 262 # here we have been given a module rather than a package - so
(...skipping 12 matching lines...) Expand all
247 return name 275 return name
248 276
249 def _get_module_from_name(self, name): 277 def _get_module_from_name(self, name):
250 __import__(name) 278 __import__(name)
251 return sys.modules[name] 279 return sys.modules[name]
252 280
253 def _match_path(self, path, full_path, pattern): 281 def _match_path(self, path, full_path, pattern):
254 # override this method to use alternative matching strategy 282 # override this method to use alternative matching strategy
255 return fnmatch(path, pattern) 283 return fnmatch(path, pattern)
256 284
257 def _find_tests(self, start_dir, pattern): 285 def _find_tests(self, start_dir, pattern, namespace=False):
258 """Used by discovery. Yields test suites it loads.""" 286 """Used by discovery. Yields test suites it loads."""
259 paths = sorted(os.listdir(start_dir)) 287 paths = sorted(os.listdir(start_dir))
260 288
261 for path in paths: 289 for path in paths:
262 full_path = os.path.join(start_dir, path) 290 full_path = os.path.join(start_dir, path)
263 if os.path.isfile(full_path): 291 if os.path.isfile(full_path):
264 if not VALID_MODULE_NAME.match(path): 292 if not VALID_MODULE_NAME.match(path):
265 # valid Python identifiers only 293 # valid Python identifiers only
266 continue 294 continue
267 if not self._match_path(path, full_path, pattern): 295 if not self._match_path(path, full_path, pattern):
(...skipping 12 matching lines...) Expand all
280 fullpath_noext = _jython_aware_splitext(os.path.realpath(ful l_path)) 308 fullpath_noext = _jython_aware_splitext(os.path.realpath(ful l_path))
281 if realpath.lower() != fullpath_noext.lower(): 309 if realpath.lower() != fullpath_noext.lower():
282 module_dir = os.path.dirname(realpath) 310 module_dir = os.path.dirname(realpath)
283 mod_name = _jython_aware_splitext(os.path.basename(full_ path)) 311 mod_name = _jython_aware_splitext(os.path.basename(full_ path))
284 expected_dir = os.path.dirname(full_path) 312 expected_dir = os.path.dirname(full_path)
285 msg = ("%r module incorrectly imported from %r. Expected %r. " 313 msg = ("%r module incorrectly imported from %r. Expected %r. "
286 "Is this module globally installed?") 314 "Is this module globally installed?")
287 raise ImportError(msg % (mod_name, module_dir, expected_ dir)) 315 raise ImportError(msg % (mod_name, module_dir, expected_ dir))
288 yield self.loadTestsFromModule(module) 316 yield self.loadTestsFromModule(module)
289 elif os.path.isdir(full_path): 317 elif os.path.isdir(full_path):
290 if not os.path.isfile(os.path.join(full_path, '__init__.py')): 318 if (not namespace and
291 continue 319 not os.path.isfile(os.path.join(full_path, '__init__.py'))):
320 continue
292 321
293 load_tests = None 322 load_tests = None
294 tests = None 323 tests = None
295 if fnmatch(path, pattern): 324 if fnmatch(path, pattern):
296 # only check load_tests if the package directory itself matc hes the filter 325 # only check load_tests if the package directory itself matc hes the filter
297 name = self._get_name_from_path(full_path) 326 name = self._get_name_from_path(full_path)
298 package = self._get_module_from_name(name) 327 package = self._get_module_from_name(name)
299 load_tests = getattr(package, 'load_tests', None) 328 load_tests = getattr(package, 'load_tests', None)
300 tests = self.loadTestsFromModule(package, use_load_tests=Fal se) 329 tests = self.loadTestsFromModule(package, use_load_tests=Fal se)
301 330
302 if load_tests is None: 331 if load_tests is None:
303 if tests is not None: 332 if tests is not None:
304 # tests loaded from package file 333 # tests loaded from package file
305 yield tests 334 yield tests
306 # recurse into the package 335 # recurse into the package
307 yield from self._find_tests(full_path, pattern) 336 yield from self._find_tests(full_path, pattern,
337 namespace=namespace)
308 else: 338 else:
309 try: 339 try:
310 yield load_tests(self, tests, pattern) 340 yield load_tests(self, tests, pattern)
311 except Exception as e: 341 except Exception as e:
312 yield _make_failed_load_tests(package.__name__, e, 342 yield _make_failed_load_tests(package.__name__, e,
313 self.suiteClass) 343 self.suiteClass)
314 344
315 defaultTestLoader = TestLoader() 345 defaultTestLoader = TestLoader()
316 346
317 347
(...skipping 10 matching lines...) Expand all
328 358
329 def makeSuite(testCaseClass, prefix='test', sortUsing=util.three_way_cmp, 359 def makeSuite(testCaseClass, prefix='test', sortUsing=util.three_way_cmp,
330 suiteClass=suite.TestSuite): 360 suiteClass=suite.TestSuite):
331 return _makeLoader(prefix, sortUsing, suiteClass).loadTestsFromTestCase( 361 return _makeLoader(prefix, sortUsing, suiteClass).loadTestsFromTestCase(
332 testCaseClass) 362 testCaseClass)
333 363
334 def findTestCases(module, prefix='test', sortUsing=util.three_way_cmp, 364 def findTestCases(module, prefix='test', sortUsing=util.three_way_cmp,
335 suiteClass=suite.TestSuite): 365 suiteClass=suite.TestSuite):
336 return _makeLoader(prefix, sortUsing, suiteClass).loadTestsFromModule(\ 366 return _makeLoader(prefix, sortUsing, suiteClass).loadTestsFromModule(\
337 module) 367 module)
OLDNEW
« no previous file with comments | « no previous file | Lib/unittest/test/test_discovery.py » ('j') | no next file with comments »

RSS Feeds Recent Issues | This issue
This is Rietveld 894c83f36cb7+