aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorZackery Spytz <zspytz@gmail.com>2024-04-03 07:17:13 -0700
committerGitHub <noreply@github.com>2024-04-03 16:17:13 +0200
commitfc5f68e58ecfbc8c452e1c2f33a2a53d3f2d7ea2 (patch)
tree0138ade12916a1453a5cf316b5176984ee1ca867
parentea94b3b149eeadf33c2f7c46f16dcda0adc7cf4e (diff)
downloadcpython-fc5f68e58ecfbc8c452e1c2f33a2a53d3f2d7ea2.tar.gz
cpython-fc5f68e58ecfbc8c452e1c2f33a2a53d3f2d7ea2.zip
gh-59215: unittest: restore _top_level_dir at end of discovery (GH-15242)
-rw-r--r--Doc/library/unittest.rst15
-rw-r--r--Lib/test/test_unittest/test_discovery.py26
-rw-r--r--Lib/unittest/loader.py2
-rw-r--r--Misc/NEWS.d/next/Library/2019-08-12-19-08-06.bpo-15010.3bY2CF.rst3
4 files changed, 40 insertions, 6 deletions
diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst
index e6140ac70eb..3af29f19c80 100644
--- a/Doc/library/unittest.rst
+++ b/Doc/library/unittest.rst
@@ -1880,8 +1880,8 @@ Loading and running tests
Python identifiers) will be loaded.
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
- directory must be specified separately.
+ the start directory is not the top level directory then *top_level_dir*
+ must be specified separately.
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
@@ -1901,9 +1901,11 @@ Loading and running tests
package.
The pattern is deliberately not stored as a loader attribute so that
- packages can continue discovery themselves. *top_level_dir* is stored so
- ``load_tests`` does not need to pass this argument in to
- ``loader.discover()``.
+ packages can continue discovery themselves.
+
+ *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.
@@ -1930,6 +1932,9 @@ Loading and running tests
*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.
+ .. versionchanged:: 3.13
+ *top_level_dir* is only stored for the duration of *discover* call.
+
The following attributes of a :class:`TestLoader` can be configured either by
subclassing or assignment on an instance:
diff --git a/Lib/test/test_unittest/test_discovery.py b/Lib/test/test_unittest/test_discovery.py
index 004898ed431..a44b18406c0 100644
--- a/Lib/test/test_unittest/test_discovery.py
+++ b/Lib/test/test_unittest/test_discovery.py
@@ -406,10 +406,34 @@ class TestDiscovery(unittest.TestCase):
top_level_dir = os.path.abspath('/foo/bar')
start_dir = os.path.abspath('/foo/bar/baz')
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.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):
# 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).
diff --git a/Lib/unittest/loader.py b/Lib/unittest/loader.py
index 9a3e5cc4bf3..22797b83a68 100644
--- a/Lib/unittest/loader.py
+++ b/Lib/unittest/loader.py
@@ -254,6 +254,7 @@ class TestLoader(object):
Paths are sorted before being imported to ensure reproducible execution
order even on filesystems with non-alphabetical ordering like ext3/4.
"""
+ original_top_level_dir = self._top_level_dir
set_implicit_top = False
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
@@ -307,6 +308,7 @@ class TestLoader(object):
raise ImportError('Start directory is not importable: %r' % start_dir)
tests = list(self._find_tests(start_dir, pattern))
+ self._top_level_dir = original_top_level_dir
return self.suiteClass(tests)
def _get_directory_containing_module(self, module_name):
diff --git a/Misc/NEWS.d/next/Library/2019-08-12-19-08-06.bpo-15010.3bY2CF.rst b/Misc/NEWS.d/next/Library/2019-08-12-19-08-06.bpo-15010.3bY2CF.rst
new file mode 100644
index 00000000000..f61a45ed98a
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2019-08-12-19-08-06.bpo-15010.3bY2CF.rst
@@ -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.