Skip to content

Commit b50322d

Browse files
authored
bpo-45162: Revert "Remove many old deprecated unittest features" (GH-30935)
Revert "bpo-45162: Remove many old deprecated unittest features (GH-28268)" This reverts commit b0a6ede. We're deferring this change until 3.12 while upstream projects that use the legacy assertion method names are fixed. See the issue for links to the discussion. Many upstream projects now have issues and PRs filed.
1 parent 9f08814 commit b50322d

File tree

14 files changed

+376
-55
lines changed

14 files changed

+376
-55
lines changed

Doc/library/unittest.rst

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1255,6 +1255,9 @@ Test cases
12551255
:meth:`.assertRegex`.
12561256
.. versionadded:: 3.2
12571257
:meth:`.assertNotRegex`.
1258+
.. versionadded:: 3.5
1259+
The name ``assertNotRegexpMatches`` is a deprecated alias
1260+
for :meth:`.assertNotRegex`.
12581261

12591262

12601263
.. method:: assertCountEqual(first, second, msg=None)
@@ -1620,6 +1623,40 @@ Test cases
16201623
:mod:`unittest`-based test framework.
16211624

16221625

1626+
.. _deprecated-aliases:
1627+
1628+
Deprecated aliases
1629+
##################
1630+
1631+
For historical reasons, some of the :class:`TestCase` methods had one or more
1632+
aliases that are now deprecated. The following table lists the correct names
1633+
along with their deprecated aliases:
1634+
1635+
============================== ====================== =======================
1636+
Method Name Deprecated alias Deprecated alias
1637+
============================== ====================== =======================
1638+
:meth:`.assertEqual` failUnlessEqual assertEquals
1639+
:meth:`.assertNotEqual` failIfEqual assertNotEquals
1640+
:meth:`.assertTrue` failUnless assert\_
1641+
:meth:`.assertFalse` failIf
1642+
:meth:`.assertRaises` failUnlessRaises
1643+
:meth:`.assertAlmostEqual` failUnlessAlmostEqual assertAlmostEquals
1644+
:meth:`.assertNotAlmostEqual` failIfAlmostEqual assertNotAlmostEquals
1645+
:meth:`.assertRegex` assertRegexpMatches
1646+
:meth:`.assertNotRegex` assertNotRegexpMatches
1647+
:meth:`.assertRaisesRegex` assertRaisesRegexp
1648+
============================== ====================== =======================
1649+
1650+
.. deprecated:: 3.1
1651+
The fail* aliases listed in the second column have been deprecated.
1652+
.. deprecated:: 3.2
1653+
The assert* aliases listed in the third column have been deprecated.
1654+
.. deprecated:: 3.2
1655+
``assertRegexpMatches`` and ``assertRaisesRegexp`` have been renamed to
1656+
:meth:`.assertRegex` and :meth:`.assertRaisesRegex`.
1657+
.. deprecated:: 3.5
1658+
The ``assertNotRegexpMatches`` name is deprecated in favor of :meth:`.assertNotRegex`.
1659+
16231660
.. _testsuite-objects:
16241661

16251662
Grouping tests
@@ -1745,7 +1782,7 @@ Loading and running tests
17451782
case is created for that method instead.
17461783

17471784

1748-
.. method:: loadTestsFromModule(module, *, pattern=None)
1785+
.. method:: loadTestsFromModule(module, pattern=None)
17491786

17501787
Return a suite of all test cases contained in the given module. This
17511788
method searches *module* for classes derived from :class:`TestCase` and
@@ -1769,11 +1806,10 @@ Loading and running tests
17691806
Support for ``load_tests`` added.
17701807

17711808
.. versionchanged:: 3.5
1772-
Support for a keyword-only argument *pattern* has been added.
1773-
1774-
.. versionchanged:: 3.11
1775-
The undocumented and unofficial *use_load_tests* parameter has been
1776-
removed.
1809+
The undocumented and unofficial *use_load_tests* default argument is
1810+
deprecated and ignored, although it is still accepted for backward
1811+
compatibility. The method also now accepts a keyword-only argument
1812+
*pattern* which is passed to ``load_tests`` as the third argument.
17771813

17781814

17791815
.. method:: loadTestsFromName(name, module=None)
@@ -2130,6 +2166,8 @@ Loading and running tests
21302166
:class:`TextTestRunner`.
21312167

21322168
.. versionadded:: 3.2
2169+
This class was previously named ``_TextTestResult``. The old name still
2170+
exists as an alias but is deprecated.
21332171

21342172

21352173
.. data:: defaultTestLoader
@@ -2152,7 +2190,10 @@ Loading and running tests
21522190
By default this runner shows :exc:`DeprecationWarning`,
21532191
:exc:`PendingDeprecationWarning`, :exc:`ResourceWarning` and
21542192
:exc:`ImportWarning` even if they are :ref:`ignored by default
2155-
<warning-ignored>`. This behavior can
2193+
<warning-ignored>`. Deprecation warnings caused by :ref:`deprecated unittest
2194+
methods <deprecated-aliases>` are also special-cased and, when the warning
2195+
filters are ``'default'`` or ``'always'``, they will appear only once
2196+
per-module, in order to avoid too many warning messages. This behavior can
21562197
be overridden using Python's :option:`!-Wd` or :option:`!-Wa` options
21572198
(see :ref:`Warning control <using-on-warnings>`) and leaving
21582199
*warnings* to ``None``.

Doc/whatsnew/3.11.rst

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -489,28 +489,6 @@ Removed
489489
and :class:`fileinput.FileInput`, deprecated since Python 3.9.
490490
(Contributed by Hugo van Kemenade in :issue:`45132`.)
491491

492-
* Removed many old deprecated :mod:`unittest` features:
493-
494-
- :class:`~unittest.TestCase` method aliases ``failUnlessEqual``,
495-
``failIfEqual``, ``failUnless``, ``failIf``, ``failUnlessRaises``,
496-
``failUnlessAlmostEqual``, ``failIfAlmostEqual`` (deprecated in Python 3.1),
497-
``assertEquals``, ``assertNotEquals``, ``assert_``, ``assertAlmostEquals``,
498-
``assertNotAlmostEquals``, ``assertRegexpMatches``, ``assertRaisesRegexp``
499-
(deprecated in Python 3.2), and ``assertNotRegexpMatches`` (deprecated in
500-
Python 3.5).
501-
502-
- Undocumented and broken :class:`~unittest.TestCase` method
503-
``assertDictContainsSubset`` (deprecated in Python 3.2).
504-
505-
- Undocumented :meth:`<unittest.TestLoader.loadTestsFromModule>
506-
TestLoader.loadTestsFromModule` parameter *use_load_tests* (deprecated
507-
and ignored since Python 3.2).
508-
509-
- An alias of the :class:`~unittest.TextTestResult` class:
510-
``_TextTestResult`` (deprecated in Python 3.2).
511-
512-
(Contributed by Serhiy Storchaka in :issue:`45162`.)
513-
514492
* The following deprecated functions and methods are removed in the :mod:`gettext`
515493
module: :func:`~gettext.lgettext`, :func:`~gettext.ldgettext`,
516494
:func:`~gettext.lngettext` and :func:`~gettext.ldngettext`.

Doc/whatsnew/3.2.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1815,7 +1815,8 @@ names.
18151815
=============================== ==============================
18161816

18171817
Likewise, the ``TestCase.fail*`` methods deprecated in Python 3.1 are expected
1818-
to be removed in Python 3.3.
1818+
to be removed in Python 3.3. Also see the :ref:`deprecated-aliases` section in
1819+
the :mod:`unittest` documentation.
18191820

18201821
(Contributed by Ezio Melotti; :issue:`9424`.)
18211822

Lib/unittest/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ def testMultiply(self):
6868
# IsolatedAsyncioTestCase will be imported lazily.
6969
from .loader import makeSuite, getTestCaseNames, findTestCases
7070

71+
# deprecated
72+
_TextTestResult = TextTestResult
73+
7174

7275
# There are no tests here, so don't try to run anything discovered from
7376
# introspecting the symbols (e.g. FunctionTestCase). Instead, all our

Lib/unittest/case.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1139,6 +1139,35 @@ def assertDictEqual(self, d1, d2, msg=None):
11391139
standardMsg = self._truncateMessage(standardMsg, diff)
11401140
self.fail(self._formatMessage(msg, standardMsg))
11411141

1142+
def assertDictContainsSubset(self, subset, dictionary, msg=None):
1143+
"""Checks whether dictionary is a superset of subset."""
1144+
warnings.warn('assertDictContainsSubset is deprecated',
1145+
DeprecationWarning)
1146+
missing = []
1147+
mismatched = []
1148+
for key, value in subset.items():
1149+
if key not in dictionary:
1150+
missing.append(key)
1151+
elif value != dictionary[key]:
1152+
mismatched.append('%s, expected: %s, actual: %s' %
1153+
(safe_repr(key), safe_repr(value),
1154+
safe_repr(dictionary[key])))
1155+
1156+
if not (missing or mismatched):
1157+
return
1158+
1159+
standardMsg = ''
1160+
if missing:
1161+
standardMsg = 'Missing: %s' % ','.join(safe_repr(m) for m in
1162+
missing)
1163+
if mismatched:
1164+
if standardMsg:
1165+
standardMsg += '; '
1166+
standardMsg += 'Mismatched values: %s' % ','.join(mismatched)
1167+
1168+
self.fail(self._formatMessage(msg, standardMsg))
1169+
1170+
11421171
def assertCountEqual(self, first, second, msg=None):
11431172
"""Asserts that two iterables have the same elements, the same number of
11441173
times, without regard to order.
@@ -1302,6 +1331,27 @@ def assertNotRegex(self, text, unexpected_regex, msg=None):
13021331
raise self.failureException(msg)
13031332

13041333

1334+
def _deprecate(original_func):
1335+
def deprecated_func(*args, **kwargs):
1336+
warnings.warn(
1337+
'Please use {0} instead.'.format(original_func.__name__),
1338+
DeprecationWarning, 2)
1339+
return original_func(*args, **kwargs)
1340+
return deprecated_func
1341+
1342+
# see #9424
1343+
failUnlessEqual = assertEquals = _deprecate(assertEqual)
1344+
failIfEqual = assertNotEquals = _deprecate(assertNotEqual)
1345+
failUnlessAlmostEqual = assertAlmostEquals = _deprecate(assertAlmostEqual)
1346+
failIfAlmostEqual = assertNotAlmostEquals = _deprecate(assertNotAlmostEqual)
1347+
failUnless = assert_ = _deprecate(assertTrue)
1348+
failUnlessRaises = _deprecate(assertRaises)
1349+
failIf = _deprecate(assertFalse)
1350+
assertRaisesRegexp = _deprecate(assertRaisesRegex)
1351+
assertRegexpMatches = _deprecate(assertRegex)
1352+
assertNotRegexpMatches = _deprecate(assertNotRegex)
1353+
1354+
13051355

13061356
class FunctionTestCase(TestCase):
13071357
"""A test case that wraps a test function.

Lib/unittest/loader.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,30 @@ def loadTestsFromTestCase(self, testCaseClass):
9393
loaded_suite = self.suiteClass(map(testCaseClass, testCaseNames))
9494
return loaded_suite
9595

96-
def loadTestsFromModule(self, module, *, pattern=None):
96+
# XXX After Python 3.5, remove backward compatibility hacks for
97+
# use_load_tests deprecation via *args and **kws. See issue 16662.
98+
def loadTestsFromModule(self, module, *args, pattern=None, **kws):
9799
"""Return a suite of all test cases contained in the given module"""
100+
# This method used to take an undocumented and unofficial
101+
# use_load_tests argument. For backward compatibility, we still
102+
# accept the argument (which can also be the first position) but we
103+
# ignore it and issue a deprecation warning if it's present.
104+
if len(args) > 0 or 'use_load_tests' in kws:
105+
warnings.warn('use_load_tests is deprecated and ignored',
106+
DeprecationWarning)
107+
kws.pop('use_load_tests', None)
108+
if len(args) > 1:
109+
# Complain about the number of arguments, but don't forget the
110+
# required `module` argument.
111+
complaint = len(args) + 1
112+
raise TypeError('loadTestsFromModule() takes 1 positional argument but {} were given'.format(complaint))
113+
if len(kws) != 0:
114+
# Since the keyword arguments are unsorted (see PEP 468), just
115+
# pick the alphabetically sorted first argument to complain about,
116+
# if multiple were given. At least the error message will be
117+
# predictable.
118+
complaint = sorted(kws)[0]
119+
raise TypeError("loadTestsFromModule() got an unexpected keyword argument '{}'".format(complaint))
98120
tests = []
99121
for name in dir(module):
100122
obj = getattr(module, name)

Lib/unittest/runner.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,15 @@ def run(self, test):
200200
if self.warnings:
201201
# if self.warnings is set, use it to filter all the warnings
202202
warnings.simplefilter(self.warnings)
203+
# if the filter is 'default' or 'always', special-case the
204+
# warnings from the deprecated unittest methods to show them
205+
# no more than once per module, because they can be fairly
206+
# noisy. The -Wd and -Wa flags can be used to bypass this
207+
# only when self.warnings is None.
208+
if self.warnings in ['default', 'always']:
209+
warnings.filterwarnings('module',
210+
category=DeprecationWarning,
211+
message=r'Please use assert\w+ instead.')
203212
startTime = time.perf_counter()
204213
startTestRun = getattr(result, 'startTestRun', None)
205214
if startTestRun is not None:

Lib/unittest/test/_test_warnings.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,17 @@ def warnfun():
1818
warnings.warn('rw', RuntimeWarning)
1919

2020
class TestWarnings(unittest.TestCase):
21+
# unittest warnings will be printed at most once per type (max one message
22+
# for the fail* methods, and one for the assert* methods)
23+
def test_assert(self):
24+
self.assertEquals(2+2, 4)
25+
self.assertEquals(2*2, 4)
26+
self.assertEquals(2**2, 4)
27+
28+
def test_fail(self):
29+
self.failUnless(1)
30+
self.failUnless(True)
31+
2132
def test_other_unittest(self):
2233
self.assertAlmostEqual(2+2, 4)
2334
self.assertNotAlmostEqual(4+4, 2)

Lib/unittest/test/test_assertions.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,15 @@ def testAssertDictEqual(self):
271271
r"\+ \{'key': 'value'\}$",
272272
r"\+ \{'key': 'value'\} : oops$"])
273273

274+
def testAssertDictContainsSubset(self):
275+
with warnings.catch_warnings():
276+
warnings.simplefilter("ignore", DeprecationWarning)
277+
278+
self.assertMessages('assertDictContainsSubset', ({'key': 'value'}, {}),
279+
["^Missing: 'key'$", "^oops$",
280+
"^Missing: 'key'$",
281+
"^Missing: 'key' : oops$"])
282+
274283
def testAssertMultiLineEqual(self):
275284
self.assertMessages('assertMultiLineEqual', ("", "foo"),
276285
[r"\+ foo$", "^oops$",

Lib/unittest/test/test_case.py

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -698,6 +698,36 @@ def testAssertIn(self):
698698
self.assertRaises(self.failureException, self.assertNotIn, 'cow',
699699
animals)
700700

701+
def testAssertDictContainsSubset(self):
702+
with warnings.catch_warnings():
703+
warnings.simplefilter("ignore", DeprecationWarning)
704+
705+
self.assertDictContainsSubset({}, {})
706+
self.assertDictContainsSubset({}, {'a': 1})
707+
self.assertDictContainsSubset({'a': 1}, {'a': 1})
708+
self.assertDictContainsSubset({'a': 1}, {'a': 1, 'b': 2})
709+
self.assertDictContainsSubset({'a': 1, 'b': 2}, {'a': 1, 'b': 2})
710+
711+
with self.assertRaises(self.failureException):
712+
self.assertDictContainsSubset({1: "one"}, {})
713+
714+
with self.assertRaises(self.failureException):
715+
self.assertDictContainsSubset({'a': 2}, {'a': 1})
716+
717+
with self.assertRaises(self.failureException):
718+
self.assertDictContainsSubset({'c': 1}, {'a': 1})
719+
720+
with self.assertRaises(self.failureException):
721+
self.assertDictContainsSubset({'a': 1, 'c': 1}, {'a': 1})
722+
723+
with self.assertRaises(self.failureException):
724+
self.assertDictContainsSubset({'a': 1, 'c': 1}, {'a': 1})
725+
726+
one = ''.join(chr(i) for i in range(255))
727+
# this used to cause a UnicodeDecodeError constructing the failure msg
728+
with self.assertRaises(self.failureException):
729+
self.assertDictContainsSubset({'foo': one}, {'foo': '\uFFFD'})
730+
701731
def testAssertEqual(self):
702732
equal_pairs = [
703733
((), ()),
@@ -1760,18 +1790,45 @@ def testAssertNoLogsYieldsNone(self):
17601790
pass
17611791
self.assertIsNone(value)
17621792

1763-
def testDeprecatedFailMethods(self):
1764-
"""Test that the deprecated fail* methods get removed in 3.11"""
1793+
def testDeprecatedMethodNames(self):
1794+
"""
1795+
Test that the deprecated methods raise a DeprecationWarning. See #9424.
1796+
"""
1797+
old = (
1798+
(self.failIfEqual, (3, 5)),
1799+
(self.assertNotEquals, (3, 5)),
1800+
(self.failUnlessEqual, (3, 3)),
1801+
(self.assertEquals, (3, 3)),
1802+
(self.failUnlessAlmostEqual, (2.0, 2.0)),
1803+
(self.assertAlmostEquals, (2.0, 2.0)),
1804+
(self.failIfAlmostEqual, (3.0, 5.0)),
1805+
(self.assertNotAlmostEquals, (3.0, 5.0)),
1806+
(self.failUnless, (True,)),
1807+
(self.assert_, (True,)),
1808+
(self.failUnlessRaises, (TypeError, lambda _: 3.14 + 'spam')),
1809+
(self.failIf, (False,)),
1810+
(self.assertDictContainsSubset, (dict(a=1, b=2), dict(a=1, b=2, c=3))),
1811+
(self.assertRaisesRegexp, (KeyError, 'foo', lambda: {}['foo'])),
1812+
(self.assertRegexpMatches, ('bar', 'bar')),
1813+
)
1814+
for meth, args in old:
1815+
with self.assertWarns(DeprecationWarning):
1816+
meth(*args)
1817+
1818+
# disable this test for now. When the version where the fail* methods will
1819+
# be removed is decided, re-enable it and update the version
1820+
def _testDeprecatedFailMethods(self):
1821+
"""Test that the deprecated fail* methods get removed in 3.x"""
1822+
if sys.version_info[:2] < (3, 3):
1823+
return
17651824
deprecated_names = [
17661825
'failIfEqual', 'failUnlessEqual', 'failUnlessAlmostEqual',
17671826
'failIfAlmostEqual', 'failUnless', 'failUnlessRaises', 'failIf',
1768-
'assertNotEquals', 'assertEquals', 'assertAlmostEquals',
1769-
'assertNotAlmostEquals', 'assert_', 'assertDictContainsSubset',
1770-
'assertRaisesRegexp', 'assertRegexpMatches'
1827+
'assertDictContainsSubset',
17711828
]
17721829
for deprecated_name in deprecated_names:
17731830
with self.assertRaises(AttributeError):
1774-
getattr(self, deprecated_name)
1831+
getattr(self, deprecated_name) # remove these in 3.x
17751832

17761833
def testDeepcopy(self):
17771834
# Issue: 5660

0 commit comments

Comments
 (0)