diff --git a/Lib/unittest/loader.py b/Lib/unittest/loader.py --- a/Lib/unittest/loader.py +++ b/Lib/unittest/loader.py @@ -35,6 +35,14 @@ return testFailure +def _ispackage(path): + """Guess whether a path refers to a package directory.""" + if os.path.isdir(path): + for ext in ('.py', '.pyc'): + if os.path.isfile(os.path.join(path, '__init__' + ext)): + return True + return False + def _make_failed_import_test(name, suiteClass): message = 'Failed to import test module: %s\n%s' % ( name, traceback.format_exc()) @@ -283,7 +291,7 @@ if os.path.isdir(os.path.abspath(start_dir)): start_dir = os.path.abspath(start_dir) if start_dir != top_level_dir: - is_not_importable = not os.path.isfile(os.path.join(start_dir, '__init__.py')) + is_not_importable = not _ispackage(start_dir) else: # support for discovery from dotted module names try: @@ -451,8 +459,7 @@ msg % (mod_name, module_dir, expected_dir)) return self.loadTestsFromModule(module, pattern=pattern), False elif os.path.isdir(full_path): - if (not namespace and - not os.path.isfile(os.path.join(full_path, '__init__.py'))): + if not namespace and not _ispackage(full_path): return None, False load_tests = None diff --git a/Lib/unittest/test/test_discovery.py b/Lib/unittest/test/test_discovery.py --- a/Lib/unittest/test/test_discovery.py +++ b/Lib/unittest/test/test_discovery.py @@ -414,22 +414,6 @@ # invoked when the start_dir is a package (and not the top level). # http://bugs.python.org/issue22457 - # Test data: we expect the following: - # an isfile to verify the package, then importing and scanning - # as per _find_tests' normal behaviour. - # We expect to see our load_tests hook called once. - vfs = {abspath('/toplevel'): ['startdir'], - abspath('/toplevel/startdir'): ['__init__.py']} - def list_dir(path): - return list(vfs[path]) - self.addCleanup(setattr, os, 'listdir', os.listdir) - os.listdir = list_dir - self.addCleanup(setattr, os.path, 'isfile', os.path.isfile) - os.path.isfile = lambda path: path.endswith('.py') - self.addCleanup(setattr, os.path, 'isdir', os.path.isdir) - os.path.isdir = lambda path: not path.endswith('.py') - self.addCleanup(sys.path.remove, abspath('/toplevel')) - class Module(object): paths = [] load_tests_args = [] @@ -443,16 +427,29 @@ def __eq__(self, other): return self.path == other.path - loader = unittest.TestLoader() - loader._get_module_from_name = lambda name: Module(name) - loader.suiteClass = lambda thing: thing + # Test data: we expect the following: + # an isfile to verify the package, then importing and scanning + # as per _find_tests' normal behaviour. + # We expect to see our load_tests hook called once. + self.addCleanup(setattr, os.path, 'isfile', os.path.isfile) + self.addCleanup(setattr, os.path, 'isdir', os.path.isdir) + self.addCleanup(sys.path.remove, abspath('/toplevel')) + for ext in ('.py', '.pyc'): + with self.subTest(ext=ext): + os.path.isfile = lambda path: path.endswith('__init__' + ext) + os.path.isdir = lambda path: not os.path.isfile(path) - suite = loader.discover('/toplevel/startdir', top_level_dir='/toplevel') + loader = unittest.TestLoader() + loader._get_module_from_name = lambda name: Module(name) + loader.suiteClass = lambda thing: thing - # We should have loaded tests from the package __init__. - # (normally this would be nested TestSuites.) - self.assertEqual(suite, - [['load_tests called startdir']]) + suite = loader.discover('/toplevel/startdir', + top_level_dir='/toplevel') + + # We should have loaded tests from the package __init__. + # (normally this would be nested TestSuites.) + self.assertEqual(suite, + [['load_tests called startdir']]) def setup_import_issue_tests(self, fakefile): listdir = os.listdir