diff -r 0842c5411ed6 Lib/unittest/loader.py --- a/Lib/unittest/loader.py Mon Mar 18 09:59:15 2013 +0100 +++ b/Lib/unittest/loader.py Mon Mar 18 14:04:53 2013 +0200 @@ -8,6 +8,7 @@ import functools from fnmatch import fnmatch +from itertools import chain from . import case, suite, util @@ -197,6 +198,7 @@ self._top_level_dir = top_level_dir is_not_importable = False + tests = [] if os.path.isdir(os.path.abspath(start_dir)): start_dir = os.path.abspath(start_dir) if start_dir != top_level_dir: @@ -210,15 +212,32 @@ else: the_module = sys.modules[start_dir] top_part = start_dir.split('.')[0] - start_dir = os.path.abspath(os.path.dirname((the_module.__file__))) + is_namespace = False + try: + start_dir = os.path.abspath(os.path.dirname((the_module.__file__))) + except AttributeError: + # look for namespace packages + if hasattr(the_module.__loader__, '_path'): + self._top_level_dir = '' + is_namespace = True + tests = list(chain.from_iterable(self._find_tests(path, pattern) + for path in the_module.__loader__._path)) + else: + # builtin module + raise TypeError('Can not use builtin modules as dotted module names') from None + if set_implicit_top: - self._top_level_dir = self._get_directory_containing_module(top_part) - sys.path.remove(top_level_dir) + if not is_namespace: + self._top_level_dir = self._get_directory_containing_module(top_part) + sys.path.remove(top_level_dir) + else: + sys.path.remove(top_level_dir) if is_not_importable: raise ImportError('Start directory is not importable: %r' % start_dir) - tests = list(self._find_tests(start_dir, pattern)) + if not tests: + tests = list(self._find_tests(start_dir, pattern)) return self.suiteClass(tests) def _get_directory_containing_module(self, module_name): @@ -283,9 +302,9 @@ "Is this module globally installed?") raise ImportError(msg % (mod_name, module_dir, expected_dir)) yield self.loadTestsFromModule(module) - elif os.path.isdir(full_path): - if not os.path.isfile(os.path.join(full_path, '__init__.py')): - continue + elif os.path.isdir(full_path): + # if not os.path.isfile(os.path.join(full_path, '__init__.py')): + # continue load_tests = None tests = None diff -r 0842c5411ed6 Lib/unittest/test/test_discovery.py --- a/Lib/unittest/test/test_discovery.py Mon Mar 18 09:59:15 2013 +0100 +++ b/Lib/unittest/test/test_discovery.py Mon Mar 18 14:04:53 2013 +0200 @@ -1,6 +1,8 @@ import os import re import sys +import types +import builtins import unittest @@ -49,7 +51,7 @@ path_lists = [['test1.py', 'test2.py', 'not_a_test.py', 'test_dir', 'test.foo', 'test-not-a-module.py', 'another_dir'], ['test3.py', 'test4.py', ]] - os.listdir = lambda path: path_lists.pop(0) + os.listdir = lambda path: path_lists.pop(0) if path_lists else [] self.addCleanup(restore_listdir) def isdir(path): @@ -424,5 +426,52 @@ self.assertEqual(suite._tests, tests) + def test_discovery_from_dotted_path_builtin_modules(self): + + loader = unittest.TestLoader() + + listdir = os.listdir + os.listdir = lambda _: ['test_this_does_not_exist.py'] + isfile = os.path.isfile + isdir = os.path.isdir + os.path.isdir = lambda _: False + orig_sys_path = sys.path[:] + def restore(): + os.path.isfile = isfile + os.path.isdir = isdir + os.listdir = listdir + sys.path[:] = orig_sys_path + self.addCleanup(restore) + + with self.assertRaises(TypeError): + loader.discover('sys') + + def test_discovery_from_dotted_namespace_packages(self): + loader = unittest.TestLoader() + + orig_import = __import__ + package = types.ModuleType('package') + package.__loader__ = types.SimpleNamespace( _path=['/a', '/b']) + + def _import(packagename, *args, **kwargs): + sys.modules[packagename] = package + return package + + def cleanup(): + builtins.__import__ = orig_import + self.addCleanup(cleanup) + builtins.__import__ = _import + + _find_tests_args = [] + def _find_tests(start_dir, pattern): + _find_tests_args.append((start_dir, pattern)) + return ['%s/tests' % start_dir] + + loader._find_tests = _find_tests + loader.suiteClass = list + suite = loader.discover('package') + self.assertEqual(suite, ['/a/tests', '/b/tests']) + + if __name__ == '__main__': unittest.main()