Skip to content

Commit 5c43e9c

Browse files
committed
Revert "bpo-45162: Remove many old deprecated unittest features (pythonGH-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.
1 parent 1319408 commit 5c43e9c

File tree

13 files changed

+373
-55
lines changed

13 files changed

+373
-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)