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

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

Issue 17457: Unittest discover fails with namespace packages and builtin modules
Patch Set: Created 6 years, 7 months 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(os.path.dirname((the_module.__fi le__)))
220 except AttributeError:
221 # look for namespace packages
222 if hasattr(the_module.__loader__, '_path'):
223 is_namespace = True
224 for path in the_module.__loader__._path:
225 if not set_implicit_top and not path.startswith(top_ level_dir):
226 continue
227 self._top_level_dir = path.split(the_module.__name__ .replace(".", os.path.sep))[0]
228 tests.extend(self._find_tests(path, pattern, namespa ce=True))
229 else:
230 # builtin module
231 raise TypeError('Can not use builtin modules as dotted m odule names') from None
ezio.melotti 2013/11/17 01:43:32 Some of these lines are too long.
232
217 if set_implicit_top: 233 if set_implicit_top:
218 self._top_level_dir = self._get_directory_containing_module( top_part) 234 if not is_namespace:
219 sys.path.remove(top_level_dir) 235 self._top_level_dir = self._get_directory_containing_mod ule(top_part)
236 sys.path.remove(top_level_dir)
237 else:
238 sys.path.remove(top_level_dir)
220 239
221 if is_not_importable: 240 if is_not_importable:
222 raise ImportError('Start directory is not importable: %r' % start_di r) 241 raise ImportError('Start directory is not importable: %r' % start_di r)
223 242
224 tests = list(self._find_tests(start_dir, pattern)) 243 if not is_namespace:
244 tests = list(self._find_tests(start_dir, pattern))
225 return self.suiteClass(tests) 245 return self.suiteClass(tests)
226 246
227 def _get_directory_containing_module(self, module_name): 247 def _get_directory_containing_module(self, module_name):
228 module = sys.modules[module_name] 248 module = sys.modules[module_name]
229 full_path = os.path.abspath(module.__file__) 249 full_path = os.path.abspath(module.__file__)
230 250
231 if os.path.basename(full_path).lower().startswith('__init__.py'): 251 if os.path.basename(full_path).lower().startswith('__init__.py'):
232 return os.path.dirname(os.path.dirname(full_path)) 252 return os.path.dirname(os.path.dirname(full_path))
233 else: 253 else:
234 # here we have been given a module rather than a package - so 254 # here we have been given a module rather than a package - so
(...skipping 12 matching lines...) Expand all
247 return name 267 return name
248 268
249 def _get_module_from_name(self, name): 269 def _get_module_from_name(self, name):
250 __import__(name) 270 __import__(name)
251 return sys.modules[name] 271 return sys.modules[name]
252 272
253 def _match_path(self, path, full_path, pattern): 273 def _match_path(self, path, full_path, pattern):
254 # override this method to use alternative matching strategy 274 # override this method to use alternative matching strategy
255 return fnmatch(path, pattern) 275 return fnmatch(path, pattern)
256 276
257 def _find_tests(self, start_dir, pattern): 277 def _find_tests(self, start_dir, pattern, namespace=False):
258 """Used by discovery. Yields test suites it loads.""" 278 """Used by discovery. Yields test suites it loads."""
259 paths = sorted(os.listdir(start_dir)) 279 paths = sorted(os.listdir(start_dir))
260 280
261 for path in paths: 281 for path in paths:
262 full_path = os.path.join(start_dir, path) 282 full_path = os.path.join(start_dir, path)
263 if os.path.isfile(full_path): 283 if os.path.isfile(full_path):
264 if not VALID_MODULE_NAME.match(path): 284 if not VALID_MODULE_NAME.match(path):
265 # valid Python identifiers only 285 # valid Python identifiers only
266 continue 286 continue
267 if not self._match_path(path, full_path, pattern): 287 if not self._match_path(path, full_path, pattern):
(...skipping 11 matching lines...) Expand all
279 realpath = _jython_aware_splitext(mod_file) 299 realpath = _jython_aware_splitext(mod_file)
280 fullpath_noext = _jython_aware_splitext(full_path) 300 fullpath_noext = _jython_aware_splitext(full_path)
281 if realpath.lower() != fullpath_noext.lower(): 301 if realpath.lower() != fullpath_noext.lower():
282 module_dir = os.path.dirname(realpath) 302 module_dir = os.path.dirname(realpath)
283 mod_name = _jython_aware_splitext(os.path.basename(full_ path)) 303 mod_name = _jython_aware_splitext(os.path.basename(full_ path))
284 expected_dir = os.path.dirname(full_path) 304 expected_dir = os.path.dirname(full_path)
285 msg = ("%r module incorrectly imported from %r. Expected %r. " 305 msg = ("%r module incorrectly imported from %r. Expected %r. "
286 "Is this module globally installed?") 306 "Is this module globally installed?")
287 raise ImportError(msg % (mod_name, module_dir, expected_ dir)) 307 raise ImportError(msg % (mod_name, module_dir, expected_ dir))
288 yield self.loadTestsFromModule(module) 308 yield self.loadTestsFromModule(module)
289 elif os.path.isdir(full_path): 309 elif os.path.isdir(full_path):
290 if not os.path.isfile(os.path.join(full_path, '__init__.py')): 310 if not namespace and not os.path.isfile(os.path.join(full_path, '__init__.py')):
291 continue 311 continue
292 312
ezio.melotti 2013/11/17 01:43:32 There's some trailing space that should be removed
293 load_tests = None 313 load_tests = None
294 tests = None 314 tests = None
295 if fnmatch(path, pattern): 315 if fnmatch(path, pattern):
296 # only check load_tests if the package directory itself matc hes the filter 316 # only check load_tests if the package directory itself matc hes the filter
297 name = self._get_name_from_path(full_path) 317 name = self._get_name_from_path(full_path)
298 package = self._get_module_from_name(name) 318 package = self._get_module_from_name(name)
299 load_tests = getattr(package, 'load_tests', None) 319 load_tests = getattr(package, 'load_tests', None)
300 tests = self.loadTestsFromModule(package, use_load_tests=Fal se) 320 tests = self.loadTestsFromModule(package, use_load_tests=Fal se)
301 321
302 if load_tests is None: 322 if load_tests is None:
303 if tests is not None: 323 if tests is not None:
304 # tests loaded from package file 324 # tests loaded from package file
305 yield tests 325 yield tests
306 # recurse into the package 326 # recurse into the package
307 yield from self._find_tests(full_path, pattern) 327 yield from self._find_tests(full_path, pattern, namespace=na mespace)
308 else: 328 else:
309 try: 329 try:
310 yield load_tests(self, tests, pattern) 330 yield load_tests(self, tests, pattern)
311 except Exception as e: 331 except Exception as e:
312 yield _make_failed_load_tests(package.__name__, e, 332 yield _make_failed_load_tests(package.__name__, e,
313 self.suiteClass) 333 self.suiteClass)
314 334
315 defaultTestLoader = TestLoader() 335 defaultTestLoader = TestLoader()
316 336
317 337
(...skipping 10 matching lines...) Expand all
328 348
329 def makeSuite(testCaseClass, prefix='test', sortUsing=util.three_way_cmp, 349 def makeSuite(testCaseClass, prefix='test', sortUsing=util.three_way_cmp,
330 suiteClass=suite.TestSuite): 350 suiteClass=suite.TestSuite):
331 return _makeLoader(prefix, sortUsing, suiteClass).loadTestsFromTestCase( 351 return _makeLoader(prefix, sortUsing, suiteClass).loadTestsFromTestCase(
332 testCaseClass) 352 testCaseClass)
333 353
334 def findTestCases(module, prefix='test', sortUsing=util.three_way_cmp, 354 def findTestCases(module, prefix='test', sortUsing=util.three_way_cmp,
335 suiteClass=suite.TestSuite): 355 suiteClass=suite.TestSuite):
336 return _makeLoader(prefix, sortUsing, suiteClass).loadTestsFromModule(\ 356 return _makeLoader(prefix, sortUsing, suiteClass).loadTestsFromModule(\
337 module) 357 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+