From 1eb074a4e46d229a71a26179702f45c7ac3f9382 Mon Sep 17 00:00:00 2001 From: Andrei Fokau Date: Fri, 24 Feb 2017 21:39:53 +0000 Subject: [PATCH] bpo-29642: Load tests from implicit packages. Implicit PEP-420-style packages (i.e. missing __init__.py) were omitted, and this commit enables searching for tests in them. --- Doc/library/unittest.rst | 3 ++- Lib/unittest/loader.py | 10 +--------- Lib/unittest/test/test_discovery.py | 6 +++--- 3 files changed, 6 insertions(+), 13 deletions(-) diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst index 92e567d12824fa..eb7f92952ab165 100644 --- a/Doc/library/unittest.rst +++ b/Doc/library/unittest.rst @@ -1674,7 +1674,8 @@ Loading and running tests the import failure is due to :exc:`SkipTest` being raised, it will be recorded as a skip instead of an error. - If a package (a directory containing a file named :file:`__init__.py`) is + If a package (a directory containing a file named :file:`__init__.py` or + a :term:`namespace package `) is found, the package will be checked for a ``load_tests`` function. If this exists then it will be called ``package.load_tests(loader, tests, pattern)``. Test discovery takes care diff --git a/Lib/unittest/loader.py b/Lib/unittest/loader.py index e860debc0f3920..142619bda00002 100644 --- a/Lib/unittest/loader.py +++ b/Lib/unittest/loader.py @@ -241,7 +241,7 @@ def discover(self, start_dir, pattern='test*.py', top_level_dir=None): If the start directory is not the top level directory then the top level directory must be specified separately. - If a test package name (directory with '__init__.py') matches the + If a test package name (directory with or w/o '__init__.py') matches the pattern then the package will be checked for a 'load_tests' function. If this exists then it will be called with (loader, tests, pattern) unless the package has already had load_tests called from the same discovery @@ -282,8 +282,6 @@ def discover(self, start_dir, pattern='test*.py', top_level_dir=None): tests = [] 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')) else: # support for discovery from dotted module names try: @@ -451,12 +449,6 @@ def _find_test_path(self, full_path, pattern, namespace=False): 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'))): - return None, False - - load_tests = None - tests = None name = self._get_name_from_path(full_path) try: package = self._get_module_from_name(name) diff --git a/Lib/unittest/test/test_discovery.py b/Lib/unittest/test/test_discovery.py index 1996a8ee5d3680..9d146de9971f17 100644 --- a/Lib/unittest/test/test_discovery.py +++ b/Lib/unittest/test/test_discovery.py @@ -54,7 +54,7 @@ def restore_isdir(): path_lists = [['test2.py', 'test1.py', 'not_a_test.py', 'test_dir', 'test.foo', 'test-not-a-module.py', 'another_dir'], - ['test4.py', 'test3.py', ]] + [], ['test4.py', 'test3.py']] os.listdir = lambda path: path_lists.pop(0) self.addCleanup(restore_listdir) @@ -65,7 +65,7 @@ def isdir(path): def isfile(path): # another_dir is not a package and so shouldn't be recursed into - return not path.endswith('dir') and not 'another_dir' in path + return not path.endswith('dir') and 'another_dir' not in path os.path.isfile = isfile self.addCleanup(restore_isfile) @@ -85,7 +85,7 @@ def loadTestsFromModule(module, pattern=None): # The test suites found should be sorted alphabetically for reliable # execution order. expected = [[name + ' module tests'] for name in - ('test1', 'test2', 'test_dir')] + ('another_dir', 'test1', 'test2', 'test_dir')] expected.extend([[('test_dir.%s' % name) + ' module tests'] for name in ('test3', 'test4')]) self.assertEqual(suite, expected)