mirror of
https://github.com/python/cpython.git
synced 2025-08-14 22:01:08 +00:00
[3.12] gh-59215: unittest: restore _top_level_dir at end of discovery (GH-15242) (GH-117508)
* gh-59215: unittest: restore _top_level_dir at end of discovery (GH-15242)
(cherry picked from commit fc5f68e58e
)
Co-authored-by: Zackery Spytz <zspytz@gmail.com>
Co-authored-by: Petr Viktorin <encukou@gmail.com>
This commit is contained in:
parent
30725f0ad2
commit
fbe29e639d
4 changed files with 40 additions and 6 deletions
|
@ -1872,8 +1872,8 @@ Loading and running tests
|
||||||
Python identifiers) will be loaded.
|
Python identifiers) will be loaded.
|
||||||
|
|
||||||
All test modules must be importable from the top level of the project. If
|
All test modules must be importable from the top level of the project. If
|
||||||
the start directory is not the top level directory then the top level
|
the start directory is not the top level directory then *top_level_dir*
|
||||||
directory must be specified separately.
|
must be specified separately.
|
||||||
|
|
||||||
If importing a module fails, for example due to a syntax error, then
|
If importing a module fails, for example due to a syntax error, then
|
||||||
this will be recorded as a single error and discovery will continue. If
|
this will be recorded as a single error and discovery will continue. If
|
||||||
|
@ -1893,9 +1893,11 @@ Loading and running tests
|
||||||
package.
|
package.
|
||||||
|
|
||||||
The pattern is deliberately not stored as a loader attribute so that
|
The pattern is deliberately not stored as a loader attribute so that
|
||||||
packages can continue discovery themselves. *top_level_dir* is stored so
|
packages can continue discovery themselves.
|
||||||
``load_tests`` does not need to pass this argument in to
|
|
||||||
``loader.discover()``.
|
*top_level_dir* is stored internally, and used as a default to any
|
||||||
|
nested calls to ``discover()``. That is, if a package's ``load_tests``
|
||||||
|
calls ``loader.discover()``, it does not need to pass this argument.
|
||||||
|
|
||||||
*start_dir* can be a dotted module name as well as a directory.
|
*start_dir* can be a dotted module name as well as a directory.
|
||||||
|
|
||||||
|
@ -1922,6 +1924,9 @@ Loading and running tests
|
||||||
*start_dir* can not be a :term:`namespace packages <namespace package>`.
|
*start_dir* can not be a :term:`namespace packages <namespace package>`.
|
||||||
It has been broken since Python 3.7 and Python 3.11 officially remove it.
|
It has been broken since Python 3.7 and Python 3.11 officially remove it.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.12.4
|
||||||
|
*top_level_dir* is only stored for the duration of *discover* call.
|
||||||
|
|
||||||
|
|
||||||
The following attributes of a :class:`TestLoader` can be configured either by
|
The following attributes of a :class:`TestLoader` can be configured either by
|
||||||
subclassing or assignment on an instance:
|
subclassing or assignment on an instance:
|
||||||
|
|
|
@ -406,10 +406,34 @@ class TestDiscovery(unittest.TestCase):
|
||||||
top_level_dir = os.path.abspath('/foo/bar')
|
top_level_dir = os.path.abspath('/foo/bar')
|
||||||
start_dir = os.path.abspath('/foo/bar/baz')
|
start_dir = os.path.abspath('/foo/bar/baz')
|
||||||
self.assertEqual(suite, "['tests']")
|
self.assertEqual(suite, "['tests']")
|
||||||
self.assertEqual(loader._top_level_dir, top_level_dir)
|
self.assertEqual(loader._top_level_dir, os.path.abspath('/foo'))
|
||||||
self.assertEqual(_find_tests_args, [(start_dir, 'pattern')])
|
self.assertEqual(_find_tests_args, [(start_dir, 'pattern')])
|
||||||
self.assertIn(top_level_dir, sys.path)
|
self.assertIn(top_level_dir, sys.path)
|
||||||
|
|
||||||
|
def test_discover_should_not_persist_top_level_dir_between_calls(self):
|
||||||
|
original_isfile = os.path.isfile
|
||||||
|
original_isdir = os.path.isdir
|
||||||
|
original_sys_path = sys.path[:]
|
||||||
|
def restore():
|
||||||
|
os.path.isfile = original_isfile
|
||||||
|
os.path.isdir = original_isdir
|
||||||
|
sys.path[:] = original_sys_path
|
||||||
|
self.addCleanup(restore)
|
||||||
|
|
||||||
|
os.path.isfile = lambda path: True
|
||||||
|
os.path.isdir = lambda path: True
|
||||||
|
loader = unittest.TestLoader()
|
||||||
|
loader.suiteClass = str
|
||||||
|
dir = '/foo/bar'
|
||||||
|
top_level_dir = '/foo'
|
||||||
|
|
||||||
|
loader.discover(dir, top_level_dir=top_level_dir)
|
||||||
|
self.assertEqual(loader._top_level_dir, None)
|
||||||
|
|
||||||
|
loader._top_level_dir = dir2 = '/previous/dir'
|
||||||
|
loader.discover(dir, top_level_dir=top_level_dir)
|
||||||
|
self.assertEqual(loader._top_level_dir, dir2)
|
||||||
|
|
||||||
def test_discover_start_dir_is_package_calls_package_load_tests(self):
|
def test_discover_start_dir_is_package_calls_package_load_tests(self):
|
||||||
# This test verifies that the package load_tests in a package is indeed
|
# This test verifies that the package load_tests in a package is indeed
|
||||||
# invoked when the start_dir is a package (and not the top level).
|
# invoked when the start_dir is a package (and not the top level).
|
||||||
|
|
|
@ -254,6 +254,7 @@ class TestLoader(object):
|
||||||
Paths are sorted before being imported to ensure reproducible execution
|
Paths are sorted before being imported to ensure reproducible execution
|
||||||
order even on filesystems with non-alphabetical ordering like ext3/4.
|
order even on filesystems with non-alphabetical ordering like ext3/4.
|
||||||
"""
|
"""
|
||||||
|
original_top_level_dir = self._top_level_dir
|
||||||
set_implicit_top = False
|
set_implicit_top = False
|
||||||
if top_level_dir is None and self._top_level_dir is not None:
|
if top_level_dir is None and self._top_level_dir is not None:
|
||||||
# make top_level_dir optional if called from load_tests in a package
|
# make top_level_dir optional if called from load_tests in a package
|
||||||
|
@ -307,6 +308,7 @@ class TestLoader(object):
|
||||||
raise ImportError('Start directory is not importable: %r' % start_dir)
|
raise ImportError('Start directory is not importable: %r' % start_dir)
|
||||||
|
|
||||||
tests = list(self._find_tests(start_dir, pattern))
|
tests = list(self._find_tests(start_dir, pattern))
|
||||||
|
self._top_level_dir = original_top_level_dir
|
||||||
return self.suiteClass(tests)
|
return self.suiteClass(tests)
|
||||||
|
|
||||||
def _get_directory_containing_module(self, module_name):
|
def _get_directory_containing_module(self, module_name):
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
:meth:`unittest.TestLoader.discover` now saves the original value of
|
||||||
|
``unittest.TestLoader._top_level_dir`` and restores it at the end of the
|
||||||
|
call.
|
Loading…
Add table
Add a link
Reference in a new issue