Skip to content

Commit d78742a

Browse files
committed
- Issue #16662: load_tests() is now unconditionally run when it is present in
a package's __init__.py. TestLoader.loadTestsFromModule() still accepts use_load_tests, but it is deprecated and ignored. A new keyword-only attribute `pattern` is added and documented. Patch given by Robert Collins, tweaked by Barry Warsaw.
1 parent 238f5aa commit d78742a

File tree

5 files changed

+472
-61
lines changed

5 files changed

+472
-61
lines changed

Diff for: Doc/library/unittest.rst

+39-28
Original file line numberDiff line numberDiff line change
@@ -1561,7 +1561,7 @@ Loading and running tests
15611561
:class:`testCaseClass`.
15621562

15631563

1564-
.. method:: loadTestsFromModule(module)
1564+
.. method:: loadTestsFromModule(module, pattern=None)
15651565

15661566
Return a suite of all tests cases contained in the given module. This
15671567
method searches *module* for classes derived from :class:`TestCase` and
@@ -1578,11 +1578,18 @@ Loading and running tests
15781578

15791579
If a module provides a ``load_tests`` function it will be called to
15801580
load the tests. This allows modules to customize test loading.
1581-
This is the `load_tests protocol`_.
1581+
This is the `load_tests protocol`_. The *pattern* argument is passed as
1582+
the third argument to ``load_tests``.
15821583

15831584
.. versionchanged:: 3.2
15841585
Support for ``load_tests`` added.
15851586

1587+
.. versionchanged:: 3.5
1588+
The undocumented and unofficial *use_load_tests* default argument is
1589+
deprecated and ignored, although it is still accepted for backward
1590+
compatibility. The method also now accepts a keyword-only argument
1591+
*pattern* which is passed to ``load_tests`` as the third argument.
1592+
15861593

15871594
.. method:: loadTestsFromName(name, module=None)
15881595

@@ -1634,18 +1641,18 @@ Loading and running tests
16341641
the start directory is not the top level directory then the top level
16351642
directory must be specified separately.
16361643

1637-
If importing a module fails, for example due to a syntax error, then this
1638-
will be recorded as a single error and discovery will continue. If the
1639-
import failure is due to :exc:`SkipTest` being raised, it will be recorded
1640-
as a skip instead of an error.
1644+
If importing a module fails, for example due to a syntax error, then
1645+
this will be recorded as a single error and discovery will continue. If
1646+
the import failure is due to :exc:`SkipTest` being raised, it will be
1647+
recorded as a skip instead of an error.
16411648

1642-
If a test package name (directory with :file:`__init__.py`) matches the
1643-
pattern then the package will be checked for a ``load_tests``
1644-
function. If this exists then it will be called with *loader*, *tests*,
1645-
*pattern*.
1649+
If a package (a directory containing a file named :file:`__init__.py`) is
1650+
found, the package will be checked for a ``load_tests`` function. If this
1651+
exists then it will be called with *loader*, *tests*, *pattern*.
16461652

1647-
If load_tests exists then discovery does *not* recurse into the package,
1648-
``load_tests`` is responsible for loading all tests in the package.
1653+
If ``load_tests`` exists then discovery does *not* recurse into the
1654+
package, ``load_tests`` is responsible for loading all tests in the
1655+
package.
16491656

16501657
The pattern is deliberately not stored as a loader attribute so that
16511658
packages can continue discovery themselves. *top_level_dir* is stored so
@@ -1664,6 +1671,11 @@ Loading and running tests
16641671
the same even if the underlying file system's ordering is not
16651672
dependent on file name.
16661673

1674+
.. versionchanged:: 3.5
1675+
Found packages are now checked for ``load_tests`` regardless of
1676+
whether their path matches *pattern*, because it is impossible for
1677+
a package name to match the default pattern.
1678+
16671679

16681680
The following attributes of a :class:`TestLoader` can be configured either by
16691681
subclassing or assignment on an instance:
@@ -2032,7 +2044,10 @@ test runs or test discovery by implementing a function called ``load_tests``.
20322044
If a test module defines ``load_tests`` it will be called by
20332045
:meth:`TestLoader.loadTestsFromModule` with the following arguments::
20342046

2035-
load_tests(loader, standard_tests, None)
2047+
load_tests(loader, standard_tests, pattern)
2048+
2049+
where *pattern* is passed straight through from ``loadTestsFromModule``. It
2050+
defaults to ``None``.
20362051

20372052
It should return a :class:`TestSuite`.
20382053

@@ -2054,21 +2069,12 @@ A typical ``load_tests`` function that loads tests from a specific set of
20542069
suite.addTests(tests)
20552070
return suite
20562071

2057-
If discovery is started, either from the command line or by calling
2058-
:meth:`TestLoader.discover`, with a pattern that matches a package
2059-
name then the package :file:`__init__.py` will be checked for ``load_tests``.
2060-
2061-
.. note::
2062-
2063-
The default pattern is ``'test*.py'``. This matches all Python files
2064-
that start with ``'test'`` but *won't* match any test directories.
2065-
2066-
A pattern like ``'test*'`` will match test packages as well as
2067-
modules.
2068-
2069-
If the package :file:`__init__.py` defines ``load_tests`` then it will be
2070-
called and discovery not continued into the package. ``load_tests``
2071-
is called with the following arguments::
2072+
If discovery is started in a directory containing a package, either from the
2073+
command line or by calling :meth:`TestLoader.discover`, then the package
2074+
:file:`__init__.py` will be checked for ``load_tests``. If that function does
2075+
not exist, discovery will recurse into the package as though it were just
2076+
another directory. Otherwise, discovery of the package's tests will be left up
2077+
to ``load_tests`` which is called with the following arguments::
20722078

20732079
load_tests(loader, standard_tests, pattern)
20742080

@@ -2087,6 +2093,11 @@ continue (and potentially modify) test discovery. A 'do nothing'
20872093
standard_tests.addTests(package_tests)
20882094
return standard_tests
20892095

2096+
.. versionchanged:: 3.5
2097+
Discovery no longer checks package names for matching *pattern* due to the
2098+
impossibility of package names matching the default pattern.
2099+
2100+
20902101

20912102
Class and Module Fixtures
20922103
-------------------------

Diff for: Lib/unittest/loader.py

+37-16
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import traceback
77
import types
88
import functools
9+
import warnings
910

1011
from fnmatch import fnmatch
1112

@@ -70,8 +71,27 @@ def loadTestsFromTestCase(self, testCaseClass):
7071
loaded_suite = self.suiteClass(map(testCaseClass, testCaseNames))
7172
return loaded_suite
7273

73-
def loadTestsFromModule(self, module, use_load_tests=True):
74+
# XXX After Python 3.5, remove backward compatibility hacks for
75+
# use_load_tests deprecation via *args and **kws. See issue 16662.
76+
def loadTestsFromModule(self, module, *args, pattern=None, **kws):
7477
"""Return a suite of all tests cases contained in the given module"""
78+
# This method used to take an undocumented and unofficial
79+
# use_load_tests argument. For backward compatibility, we still
80+
# accept the argument (which can also be the first position) but we
81+
# ignore it and issue a deprecation warning if it's present.
82+
if len(args) == 1 or 'use_load_tests' in kws:
83+
warnings.warn('use_load_tests is deprecated and ignored',
84+
DeprecationWarning)
85+
kws.pop('use_load_tests', None)
86+
if len(args) > 1:
87+
raise TypeError('loadTestsFromModule() takes 1 positional argument but {} were given'.format(len(args)))
88+
if len(kws) != 0:
89+
# Since the keyword arguments are unsorted (see PEP 468), just
90+
# pick the alphabetically sorted first argument to complain about,
91+
# if multiple were given. At least the error message will be
92+
# predictable.
93+
complaint = sorted(kws)[0]
94+
raise TypeError("loadTestsFromModule() got an unexpected keyword argument '{}'".format(complaint))
7595
tests = []
7696
for name in dir(module):
7797
obj = getattr(module, name)
@@ -80,9 +100,9 @@ def loadTestsFromModule(self, module, use_load_tests=True):
80100

81101
load_tests = getattr(module, 'load_tests', None)
82102
tests = self.suiteClass(tests)
83-
if use_load_tests and load_tests is not None:
103+
if load_tests is not None:
84104
try:
85-
return load_tests(self, tests, None)
105+
return load_tests(self, tests, pattern)
86106
except Exception as e:
87107
return _make_failed_load_tests(module.__name__, e,
88108
self.suiteClass)
@@ -325,34 +345,35 @@ def _find_tests(self, start_dir, pattern, namespace=False):
325345
msg = ("%r module incorrectly imported from %r. Expected %r. "
326346
"Is this module globally installed?")
327347
raise ImportError(msg % (mod_name, module_dir, expected_dir))
328-
yield self.loadTestsFromModule(module)
348+
yield self.loadTestsFromModule(module, pattern=pattern)
329349
elif os.path.isdir(full_path):
330350
if (not namespace and
331351
not os.path.isfile(os.path.join(full_path, '__init__.py'))):
332352
continue
333353

334354
load_tests = None
335355
tests = None
336-
if fnmatch(path, pattern):
337-
# only check load_tests if the package directory itself matches the filter
338-
name = self._get_name_from_path(full_path)
356+
name = self._get_name_from_path(full_path)
357+
try:
339358
package = self._get_module_from_name(name)
359+
except case.SkipTest as e:
360+
yield _make_skipped_test(name, e, self.suiteClass)
361+
except:
362+
yield _make_failed_import_test(name, self.suiteClass)
363+
else:
340364
load_tests = getattr(package, 'load_tests', None)
341-
tests = self.loadTestsFromModule(package, use_load_tests=False)
342-
343-
if load_tests is None:
365+
tests = self.loadTestsFromModule(package, pattern=pattern)
344366
if tests is not None:
345367
# tests loaded from package file
346368
yield tests
369+
370+
if load_tests is not None:
371+
# loadTestsFromModule(package) has load_tests for us.
372+
continue
347373
# recurse into the package
348374
yield from self._find_tests(full_path, pattern,
349375
namespace=namespace)
350-
else:
351-
try:
352-
yield load_tests(self, tests, pattern)
353-
except Exception as e:
354-
yield _make_failed_load_tests(package.__name__, e,
355-
self.suiteClass)
376+
356377

357378
defaultTestLoader = TestLoader()
358379

0 commit comments

Comments
 (0)