From a9c998f5aa16795e77b61bad6c9cc69d9b739987 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 10 Jan 2025 14:55:33 +0200 Subject: [PATCH 1/7] gh-71339: Add additional assertion methods for unittest Add the following methods: * assertHasAttr() and assertNotHasAttr() * assertIsSubclass() and assertNotIsSubclass() * assertStartswith() and assertNotStartswith() * assertEndswith() and assertNotEndswith() Also improve error messages for assertIsInstance() and assertNotIsInstance(). --- Doc/library/unittest.rst | 57 ++++ Doc/whatsnew/3.14.rst | 17 + Lib/test/test_descr.py | 8 - Lib/test/test_gdb/test_misc.py | 6 +- Lib/test/test_gdb/util.py | 5 - .../resources/test_functional.py | 10 +- Lib/test/test_pyclbr.py | 10 +- Lib/test/test_typing.py | 126 +++---- Lib/test/test_unittest/test_case.py | 307 +++++++++++++++++- Lib/test/test_unittest/test_loader.py | 2 +- Lib/test/test_unittest/test_program.py | 10 +- Lib/test/test_unittest/test_result.py | 2 +- Lib/test/test_unittest/testmock/testasync.py | 14 +- .../test_unittest/testmock/testcallable.py | 14 +- .../test_unittest/testmock/testhelpers.py | 2 +- .../testmock/testmagicmethods.py | 12 +- Lib/test/test_unittest/testmock/testmock.py | 16 +- Lib/test/test_unittest/testmock/testpatch.py | 6 +- Lib/test/test_venv.py | 10 +- Lib/unittest/case.py | 142 +++++++- ...5-01-10-15-06-45.gh-issue-71339.EKnpzw.rst | 9 + 21 files changed, 624 insertions(+), 161 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-01-10-15-06-45.gh-issue-71339.EKnpzw.rst diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst index 7f8b710f611002..f1f3f887de81ef 100644 --- a/Doc/library/unittest.rst +++ b/Doc/library/unittest.rst @@ -883,6 +883,12 @@ Test cases | :meth:`assertNotIsInstance(a, b) | ``not isinstance(a, b)`` | 3.2 | | ` | | | +-----------------------------------------+-----------------------------+---------------+ + | :meth:`assertIsSubclass(a, b) | ``issubclass(a, b)`` | 3.14 | + | ` | | | + +-----------------------------------------+-----------------------------+---------------+ + | :meth:`assertNotIsSubclass(a, b) | ``not issubclass(a, b)`` | 3.14 | + | ` | | | + +-----------------------------------------+-----------------------------+---------------+ All the assert methods accept a *msg* argument that, if specified, is used as the error message on failure (see also :data:`longMessage`). @@ -960,6 +966,14 @@ Test cases .. versionadded:: 3.2 + .. method:: assertIsSubclass(cls, superclass, msg=None) + assertNotIsSubclass(cls, superclass, msg=None) + + Test that *cls* is (or is not) a subclass of *superclass* (which can be a + class or a tuple of classes, as supported by :func:`issubclass`). + To check for the exact type, use :func:`assertIs(cls, superclass) `. + + .. versionadded:: next It is also possible to check the production of exceptions, warnings, and @@ -1210,6 +1224,24 @@ Test cases | ` | elements in the same number, | | | | regardless of their order. | | +---------------------------------------+--------------------------------+--------------+ + | :meth:`assertStartswith(a, b) | ``a.startswith(b)`` | 3.14 | + | ` | | | + +---------------------------------------+--------------------------------+--------------+ + | :meth:`assertNotStartswith(a, b) | ``not a.startswith(b)`` | 3.14 | + | ` | | | + +---------------------------------------+--------------------------------+--------------+ + | :meth:`assertEndswith(a, b) | ``a.endswith(b)`` | 3.14 | + | ` | | | + +---------------------------------------+--------------------------------+--------------+ + | :meth:`assertNotEndswith(a, b) | ``not a.endswith(b)`` | 3.14 | + | ` | | | + +---------------------------------------+--------------------------------+--------------+ + | :meth:`assertHasAttr(a, b) | ``hastattr(a, b)`` | 3.14 | + | ` | | | + +---------------------------------------+--------------------------------+--------------+ + | :meth:`assertNotHasAttr(a, b) | ``not hastattr(a, b)`` | 3.14 | + | ` | | | + +---------------------------------------+--------------------------------+--------------+ .. method:: assertAlmostEqual(first, second, places=7, msg=None, delta=None) @@ -1278,6 +1310,31 @@ Test cases .. versionadded:: 3.2 + .. method:: assertStartswith(s, prefix, msg=None) + .. method:: assertNotStartswith(s, prefix, msg=None) + + Test that the unicode or byte string *s* starts (or does not start) + with a *prefix*. + *prefix* can also be a tuple of strings to try. + + .. versionadded:: next + + .. method:: assertEndswith(s, suffix, msg=None) + .. method:: assertNotEndswith(s, suffix, msg=None) + + Test that the unicode or byte string *s* ends (or does not end) + with a *suffix*. + *suffix* can also be a tuple of strings to try. + + .. versionadded:: next + + .. method:: assertHasAttr(obj, name, msg=None) + .. method:: assertNotHasAttr(obj, name, msg=None) + + Test that the object *obj* has (or has not) an attribute *name*. + + .. versionadded:: next + .. _type-specific-methods: diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 72abfebd46f2b9..29f520ed05c030 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -670,6 +670,23 @@ unittest directory again. It was removed in Python 3.11. (Contributed by Jacob Walls in :gh:`80958`.) +* A number of new methods were added in the :class:`~unittest.TestCase` class + that provide more specialized tests. + + - :meth:`~unittest.TestCase.assertHasAttr` and + :meth:`~unittest.TestCase.assertNotHasAttr` check whether the object + has a particular attribute. + - :meth:`~unittest.TestCase.assertIsSubclass` and + :meth:`~unittest.TestCase.assertNotIsSubclass` check whether the object + is a subclass of a particular class, or of one of a tuple of classes. + - :meth:`~unittest.TestCase.assertStartsWith`, + :meth:`~unittest.TestCase.assertNotStartsWith`, + :meth:`~unittest.TestCase.assertEndsWith` and + :meth:`~unittest.TestCase.assertNotEndsWith` check whether the unicode + or byte string starts or ends with particular string(s). + + (Contributed by Serhiy Storchaka in :gh:`71339`.) + urllib ------ diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 168b78a477ee9c..51f97bb51f7bd2 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -405,14 +405,6 @@ def test_wrap_lenfunc_bad_cast(self): class ClassPropertiesAndMethods(unittest.TestCase): - def assertHasAttr(self, obj, name): - self.assertTrue(hasattr(obj, name), - '%r has no attribute %r' % (obj, name)) - - def assertNotHasAttr(self, obj, name): - self.assertFalse(hasattr(obj, name), - '%r has unexpected attribute %r' % (obj, name)) - def test_python_dicts(self): # Testing Python subclass of dict... self.assertTrue(issubclass(dict, dict)) diff --git a/Lib/test/test_gdb/test_misc.py b/Lib/test/test_gdb/test_misc.py index 1047f4867c1d03..a66ab8d574b747 100644 --- a/Lib/test/test_gdb/test_misc.py +++ b/Lib/test/test_gdb/test_misc.py @@ -28,7 +28,7 @@ def gdb_has_frame_select(): "Python was compiled with optimizations") class PyListTests(DebuggerTests): def assertListing(self, expected, actual): - self.assertEndsWith(actual, expected) + self.assertEndswith(actual, expected) def test_basic_command(self): 'Verify that the "py-list" command works' @@ -103,7 +103,7 @@ def test_down_at_bottom(self): 'Verify handling of "py-down" at the bottom of the stack' bt = self.get_stack_trace(script=SAMPLE_SCRIPT, cmds_after_breakpoint=['py-down']) - self.assertEndsWith(bt, + self.assertEndswith(bt, 'Unable to find a newer python frame\n') @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") @@ -111,7 +111,7 @@ def test_up_at_top(self): 'Verify handling of "py-up" at the top of the stack' bt = self.get_stack_trace(script=SAMPLE_SCRIPT, cmds_after_breakpoint=['py-up'] * 5) - self.assertEndsWith(bt, + self.assertEndswith(bt, 'Unable to find an older python frame\n') @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") diff --git a/Lib/test/test_gdb/util.py b/Lib/test/test_gdb/util.py index 8fe9cfc543395e..8097fd52ababe6 100644 --- a/Lib/test/test_gdb/util.py +++ b/Lib/test/test_gdb/util.py @@ -280,11 +280,6 @@ def get_stack_trace(self, source=None, script=None, return out - def assertEndsWith(self, actual, exp_end): - '''Ensure that the given "actual" string ends with "exp_end"''' - self.assertTrue(actual.endswith(exp_end), - msg='%r did not end with %r' % (actual, exp_end)) - def assertMultilineMatches(self, actual, pattern): m = re.match(pattern, actual, re.DOTALL) if not m: diff --git a/Lib/test/test_importlib/resources/test_functional.py b/Lib/test/test_importlib/resources/test_functional.py index 4317abf3162c52..8070fce20bb3c9 100644 --- a/Lib/test/test_importlib/resources/test_functional.py +++ b/Lib/test/test_importlib/resources/test_functional.py @@ -43,12 +43,6 @@ def _gen_resourcetxt_path_parts(self): with self.subTest(path_parts=path_parts): yield path_parts - def assertEndsWith(self, string, suffix): - """Assert that `string` ends with `suffix`. - - Used to ignore an architecture-specific UTF-16 byte-order mark.""" - self.assertEqual(string[-len(suffix) :], suffix) - def test_read_text(self): self.assertEqual( resources.read_text(self.anchor01, 'utf-8.file'), @@ -89,7 +83,7 @@ def test_read_text(self): ), '\x00\x01\x02\x03', ) - self.assertEndsWith( # ignore the BOM + self.assertEndswith( # ignore the BOM resources.read_text( self.anchor01, 'utf-16.file', @@ -141,7 +135,7 @@ def test_open_text(self): 'utf-16.file', errors='backslashreplace', ) as f: - self.assertEndsWith( # ignore the BOM + self.assertEndswith( # ignore the BOM f.read(), 'Hello, UTF-16 world!\n'.encode('utf-16-le').decode( errors='backslashreplace', diff --git a/Lib/test/test_pyclbr.py b/Lib/test/test_pyclbr.py index 4bf0576586cca5..25b313f6c25a4e 100644 --- a/Lib/test/test_pyclbr.py +++ b/Lib/test/test_pyclbr.py @@ -31,14 +31,6 @@ def assertListEq(self, l1, l2, ignore): print("l1=%r\nl2=%r\nignore=%r" % (l1, l2, ignore), file=sys.stderr) self.fail("%r missing" % missing.pop()) - def assertHasattr(self, obj, attr, ignore): - ''' succeed iff hasattr(obj,attr) or attr in ignore. ''' - if attr in ignore: return - if not hasattr(obj, attr): print("???", attr) - self.assertTrue(hasattr(obj, attr), - 'expected hasattr(%r, %r)' % (obj, attr)) - - def assertHaskey(self, obj, key, ignore): ''' succeed iff key in obj or key in ignore. ''' if key in ignore: return @@ -86,7 +78,7 @@ def ismethod(oclass, obj, name): for name, value in dict.items(): if name in ignore: continue - self.assertHasattr(module, name, ignore) + self.assertHasAttr(module, name, ignore) py_item = getattr(module, name) if isinstance(value, pyclbr.Function): self.assertIsInstance(py_item, (FunctionType, BuiltinFunctionType)) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index c51ee763890af2..931ce8ed09fc29 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -59,20 +59,6 @@ class BaseTestCase(TestCase): - def assertIsSubclass(self, cls, class_or_tuple, msg=None): - if not issubclass(cls, class_or_tuple): - message = '%r is not a subclass of %r' % (cls, class_or_tuple) - if msg is not None: - message += ' : %s' % msg - raise self.failureException(message) - - def assertNotIsSubclass(self, cls, class_or_tuple, msg=None): - if issubclass(cls, class_or_tuple): - message = '%r is a subclass of %r' % (cls, class_or_tuple) - if msg is not None: - message += ' : %s' % msg - raise self.failureException(message) - def clear_caches(self): for f in typing._cleanups: f() @@ -1252,10 +1238,6 @@ class Gen[*Ts]: ... class TypeVarTupleTests(BaseTestCase): - def assertEndsWith(self, string, tail): - if not string.endswith(tail): - self.fail(f"String {string!r} does not end with {tail!r}") - def test_name(self): Ts = TypeVarTuple('Ts') self.assertEqual(Ts.__name__, 'Ts') @@ -1444,31 +1426,31 @@ def test_variadic_class_repr_is_correct(self): class A(Generic[*Ts]): pass class B(Generic[Unpack[Ts]]): pass - self.assertEndsWith(repr(A[()]), 'A[()]') - self.assertEndsWith(repr(B[()]), 'B[()]') - self.assertEndsWith(repr(A[float]), 'A[float]') - self.assertEndsWith(repr(B[float]), 'B[float]') - self.assertEndsWith(repr(A[float, str]), 'A[float, str]') - self.assertEndsWith(repr(B[float, str]), 'B[float, str]') + self.assertEndswith(repr(A[()]), 'A[()]') + self.assertEndswith(repr(B[()]), 'B[()]') + self.assertEndswith(repr(A[float]), 'A[float]') + self.assertEndswith(repr(B[float]), 'B[float]') + self.assertEndswith(repr(A[float, str]), 'A[float, str]') + self.assertEndswith(repr(B[float, str]), 'B[float, str]') - self.assertEndsWith(repr(A[*tuple[int, ...]]), + self.assertEndswith(repr(A[*tuple[int, ...]]), 'A[*tuple[int, ...]]') - self.assertEndsWith(repr(B[Unpack[Tuple[int, ...]]]), + self.assertEndswith(repr(B[Unpack[Tuple[int, ...]]]), 'B[typing.Unpack[typing.Tuple[int, ...]]]') - self.assertEndsWith(repr(A[float, *tuple[int, ...]]), + self.assertEndswith(repr(A[float, *tuple[int, ...]]), 'A[float, *tuple[int, ...]]') - self.assertEndsWith(repr(A[float, Unpack[Tuple[int, ...]]]), + self.assertEndswith(repr(A[float, Unpack[Tuple[int, ...]]]), 'A[float, typing.Unpack[typing.Tuple[int, ...]]]') - self.assertEndsWith(repr(A[*tuple[int, ...], str]), + self.assertEndswith(repr(A[*tuple[int, ...], str]), 'A[*tuple[int, ...], str]') - self.assertEndsWith(repr(B[Unpack[Tuple[int, ...]], str]), + self.assertEndswith(repr(B[Unpack[Tuple[int, ...]], str]), 'B[typing.Unpack[typing.Tuple[int, ...]], str]') - self.assertEndsWith(repr(A[float, *tuple[int, ...], str]), + self.assertEndswith(repr(A[float, *tuple[int, ...], str]), 'A[float, *tuple[int, ...], str]') - self.assertEndsWith(repr(B[float, Unpack[Tuple[int, ...]], str]), + self.assertEndswith(repr(B[float, Unpack[Tuple[int, ...]], str]), 'B[float, typing.Unpack[typing.Tuple[int, ...]], str]') def test_variadic_class_alias_repr_is_correct(self): @@ -1476,64 +1458,64 @@ def test_variadic_class_alias_repr_is_correct(self): class A(Generic[Unpack[Ts]]): pass B = A[*Ts] - self.assertEndsWith(repr(B), 'A[typing.Unpack[Ts]]') - self.assertEndsWith(repr(B[()]), 'A[()]') - self.assertEndsWith(repr(B[float]), 'A[float]') - self.assertEndsWith(repr(B[float, str]), 'A[float, str]') + self.assertEndswith(repr(B), 'A[typing.Unpack[Ts]]') + self.assertEndswith(repr(B[()]), 'A[()]') + self.assertEndswith(repr(B[float]), 'A[float]') + self.assertEndswith(repr(B[float, str]), 'A[float, str]') C = A[Unpack[Ts]] - self.assertEndsWith(repr(C), 'A[typing.Unpack[Ts]]') - self.assertEndsWith(repr(C[()]), 'A[()]') - self.assertEndsWith(repr(C[float]), 'A[float]') - self.assertEndsWith(repr(C[float, str]), 'A[float, str]') + self.assertEndswith(repr(C), 'A[typing.Unpack[Ts]]') + self.assertEndswith(repr(C[()]), 'A[()]') + self.assertEndswith(repr(C[float]), 'A[float]') + self.assertEndswith(repr(C[float, str]), 'A[float, str]') D = A[*Ts, int] - self.assertEndsWith(repr(D), 'A[typing.Unpack[Ts], int]') - self.assertEndsWith(repr(D[()]), 'A[int]') - self.assertEndsWith(repr(D[float]), 'A[float, int]') - self.assertEndsWith(repr(D[float, str]), 'A[float, str, int]') + self.assertEndswith(repr(D), 'A[typing.Unpack[Ts], int]') + self.assertEndswith(repr(D[()]), 'A[int]') + self.assertEndswith(repr(D[float]), 'A[float, int]') + self.assertEndswith(repr(D[float, str]), 'A[float, str, int]') E = A[Unpack[Ts], int] - self.assertEndsWith(repr(E), 'A[typing.Unpack[Ts], int]') - self.assertEndsWith(repr(E[()]), 'A[int]') - self.assertEndsWith(repr(E[float]), 'A[float, int]') - self.assertEndsWith(repr(E[float, str]), 'A[float, str, int]') + self.assertEndswith(repr(E), 'A[typing.Unpack[Ts], int]') + self.assertEndswith(repr(E[()]), 'A[int]') + self.assertEndswith(repr(E[float]), 'A[float, int]') + self.assertEndswith(repr(E[float, str]), 'A[float, str, int]') F = A[int, *Ts] - self.assertEndsWith(repr(F), 'A[int, typing.Unpack[Ts]]') - self.assertEndsWith(repr(F[()]), 'A[int]') - self.assertEndsWith(repr(F[float]), 'A[int, float]') - self.assertEndsWith(repr(F[float, str]), 'A[int, float, str]') + self.assertEndswith(repr(F), 'A[int, typing.Unpack[Ts]]') + self.assertEndswith(repr(F[()]), 'A[int]') + self.assertEndswith(repr(F[float]), 'A[int, float]') + self.assertEndswith(repr(F[float, str]), 'A[int, float, str]') G = A[int, Unpack[Ts]] - self.assertEndsWith(repr(G), 'A[int, typing.Unpack[Ts]]') - self.assertEndsWith(repr(G[()]), 'A[int]') - self.assertEndsWith(repr(G[float]), 'A[int, float]') - self.assertEndsWith(repr(G[float, str]), 'A[int, float, str]') + self.assertEndswith(repr(G), 'A[int, typing.Unpack[Ts]]') + self.assertEndswith(repr(G[()]), 'A[int]') + self.assertEndswith(repr(G[float]), 'A[int, float]') + self.assertEndswith(repr(G[float, str]), 'A[int, float, str]') H = A[int, *Ts, str] - self.assertEndsWith(repr(H), 'A[int, typing.Unpack[Ts], str]') - self.assertEndsWith(repr(H[()]), 'A[int, str]') - self.assertEndsWith(repr(H[float]), 'A[int, float, str]') - self.assertEndsWith(repr(H[float, str]), 'A[int, float, str, str]') + self.assertEndswith(repr(H), 'A[int, typing.Unpack[Ts], str]') + self.assertEndswith(repr(H[()]), 'A[int, str]') + self.assertEndswith(repr(H[float]), 'A[int, float, str]') + self.assertEndswith(repr(H[float, str]), 'A[int, float, str, str]') I = A[int, Unpack[Ts], str] - self.assertEndsWith(repr(I), 'A[int, typing.Unpack[Ts], str]') - self.assertEndsWith(repr(I[()]), 'A[int, str]') - self.assertEndsWith(repr(I[float]), 'A[int, float, str]') - self.assertEndsWith(repr(I[float, str]), 'A[int, float, str, str]') + self.assertEndswith(repr(I), 'A[int, typing.Unpack[Ts], str]') + self.assertEndswith(repr(I[()]), 'A[int, str]') + self.assertEndswith(repr(I[float]), 'A[int, float, str]') + self.assertEndswith(repr(I[float, str]), 'A[int, float, str, str]') J = A[*Ts, *tuple[str, ...]] - self.assertEndsWith(repr(J), 'A[typing.Unpack[Ts], *tuple[str, ...]]') - self.assertEndsWith(repr(J[()]), 'A[*tuple[str, ...]]') - self.assertEndsWith(repr(J[float]), 'A[float, *tuple[str, ...]]') - self.assertEndsWith(repr(J[float, str]), 'A[float, str, *tuple[str, ...]]') + self.assertEndswith(repr(J), 'A[typing.Unpack[Ts], *tuple[str, ...]]') + self.assertEndswith(repr(J[()]), 'A[*tuple[str, ...]]') + self.assertEndswith(repr(J[float]), 'A[float, *tuple[str, ...]]') + self.assertEndswith(repr(J[float, str]), 'A[float, str, *tuple[str, ...]]') K = A[Unpack[Ts], Unpack[Tuple[str, ...]]] - self.assertEndsWith(repr(K), 'A[typing.Unpack[Ts], typing.Unpack[typing.Tuple[str, ...]]]') - self.assertEndsWith(repr(K[()]), 'A[typing.Unpack[typing.Tuple[str, ...]]]') - self.assertEndsWith(repr(K[float]), 'A[float, typing.Unpack[typing.Tuple[str, ...]]]') - self.assertEndsWith(repr(K[float, str]), 'A[float, str, typing.Unpack[typing.Tuple[str, ...]]]') + self.assertEndswith(repr(K), 'A[typing.Unpack[Ts], typing.Unpack[typing.Tuple[str, ...]]]') + self.assertEndswith(repr(K[()]), 'A[typing.Unpack[typing.Tuple[str, ...]]]') + self.assertEndswith(repr(K[float]), 'A[float, typing.Unpack[typing.Tuple[str, ...]]]') + self.assertEndswith(repr(K[float, str]), 'A[float, str, typing.Unpack[typing.Tuple[str, ...]]]') def test_cannot_subclass(self): with self.assertRaisesRegex(TypeError, NOT_A_BASE_TYPE % 'TypeVarTuple'): diff --git a/Lib/test/test_unittest/test_case.py b/Lib/test/test_unittest/test_case.py index b4b2194a09cf9f..3f440f276b768c 100644 --- a/Lib/test/test_unittest/test_case.py +++ b/Lib/test/test_unittest/test_case.py @@ -10,6 +10,7 @@ import inspect import types +from collections import UserString from copy import deepcopy from test import support @@ -54,6 +55,10 @@ def tearDown(self): self.events.append('tearDown') +class List(list): + pass + + class Test_TestCase(unittest.TestCase, TestEquality, TestHashing): ### Set up attributes used by inherited tests @@ -85,7 +90,7 @@ class Test(unittest.TestCase): def runTest(self): raise MyException() def test(self): pass - self.assertEqual(Test().id()[-13:], '.Test.runTest') + self.assertEndswith(Test().id(), '.Test.runTest') # test that TestCase can be instantiated with no args # primarily for use at the interactive interpreter @@ -106,7 +111,7 @@ class Test(unittest.TestCase): def runTest(self): raise MyException() def test(self): pass - self.assertEqual(Test('test').id()[-10:], '.Test.test') + self.assertEndswith(Test('test').id(), '.Test.test') # "class TestCase([methodName])" # ... @@ -697,16 +702,120 @@ def testAssertIsNot(self): self.assertRaises(self.failureException, self.assertIsNot, thing, thing) def testAssertIsInstance(self): - thing = [] + thing = List() self.assertIsInstance(thing, list) - self.assertRaises(self.failureException, self.assertIsInstance, - thing, dict) + self.assertIsInstance(thing, (int, list)) + with self.assertRaises(self.failureException) as cm: + self.assertIsInstance(thing, int) + self.assertEqual(str(cm.exception), + "[] is not an instance of ") + with self.assertRaises(self.failureException) as cm: + self.assertIsInstance(thing, (int, float)) + self.assertEqual(str(cm.exception), + "[] is not an instance of any of (, )") + + with self.assertRaises(self.failureException) as cm: + self.assertIsInstance(thing, int, 'ababahalamaha') + self.assertIn('ababahalamaha', str(cm.exception)) + with self.assertRaises(self.failureException) as cm: + self.assertIsInstance(thing, int, msg='ababahalamaha') + self.assertIn('ababahalamaha', str(cm.exception)) def testAssertNotIsInstance(self): - thing = [] - self.assertNotIsInstance(thing, dict) - self.assertRaises(self.failureException, self.assertNotIsInstance, - thing, list) + thing = List() + self.assertNotIsInstance(thing, int) + self.assertNotIsInstance(thing, (int, float)) + with self.assertRaises(self.failureException) as cm: + self.assertNotIsInstance(thing, list) + self.assertEqual(str(cm.exception), + "[] is an instance of ") + with self.assertRaises(self.failureException) as cm: + self.assertNotIsInstance(thing, (int, list)) + self.assertEqual(str(cm.exception), + "[] is an instance of ") + + with self.assertRaises(self.failureException) as cm: + self.assertNotIsInstance(thing, list, 'ababahalamaha') + self.assertIn('ababahalamaha', str(cm.exception)) + with self.assertRaises(self.failureException) as cm: + self.assertNotIsInstance(thing, list, msg='ababahalamaha') + self.assertIn('ababahalamaha', str(cm.exception)) + + def testAssertIsSubclass(self): + self.assertIsSubclass(List, list) + self.assertIsSubclass(List, (int, list)) + with self.assertRaises(self.failureException) as cm: + self.assertIsSubclass(List, int) + self.assertEqual(str(cm.exception), + f"{List!r} is not a subclass of ") + with self.assertRaises(self.failureException) as cm: + self.assertIsSubclass(List, (int, float)) + self.assertEqual(str(cm.exception), + f"{List!r} is not a subclass of any of (, )") + with self.assertRaises(self.failureException) as cm: + self.assertIsSubclass(1, int) + self.assertEqual(str(cm.exception), "1 is not a class") + + with self.assertRaises(self.failureException) as cm: + self.assertIsSubclass(List, int, 'ababahalamaha') + self.assertIn('ababahalamaha', str(cm.exception)) + with self.assertRaises(self.failureException) as cm: + self.assertIsSubclass(List, int, msg='ababahalamaha') + self.assertIn('ababahalamaha', str(cm.exception)) + + def testAssertNotIsSubclass(self): + self.assertNotIsSubclass(List, int) + self.assertNotIsSubclass(List, (int, float)) + with self.assertRaises(self.failureException) as cm: + self.assertNotIsSubclass(List, list) + self.assertEqual(str(cm.exception), + f"{List!r} is a subclass of ") + with self.assertRaises(self.failureException) as cm: + self.assertNotIsSubclass(List, (int, list)) + self.assertEqual(str(cm.exception), + f"{List!r} is a subclass of ") + with self.assertRaises(self.failureException) as cm: + self.assertNotIsSubclass(1, int) + self.assertEqual(str(cm.exception), "1 is not a class") + + with self.assertRaises(self.failureException) as cm: + self.assertNotIsSubclass(List, list, 'ababahalamaha') + self.assertIn('ababahalamaha', str(cm.exception)) + with self.assertRaises(self.failureException) as cm: + self.assertNotIsSubclass(List, list, msg='ababahalamaha') + self.assertIn('ababahalamaha', str(cm.exception)) + + def testAssertHasAttr(self): + a = List() + a.x = 1 + self.assertHasAttr(a, 'x') + with self.assertRaises(self.failureException) as cm: + self.assertHasAttr(a, 'y') + self.assertEqual(str(cm.exception), + "List instance has no attribute 'y'") + + with self.assertRaises(self.failureException) as cm: + self.assertHasAttr(a, 'y', 'ababahalamaha') + self.assertIn('ababahalamaha', str(cm.exception)) + with self.assertRaises(self.failureException) as cm: + self.assertHasAttr(a, 'y', msg='ababahalamaha') + self.assertIn('ababahalamaha', str(cm.exception)) + + def testAssertNotHasAttr(self): + a = List() + a.x = 1 + self.assertNotHasAttr(a, 'y') + with self.assertRaises(self.failureException) as cm: + self.assertNotHasAttr(a, 'x') + self.assertEqual(str(cm.exception), + "List instance has unexpected attribute 'x'") + + with self.assertRaises(self.failureException) as cm: + self.assertNotHasAttr(a, 'x', 'ababahalamaha') + self.assertIn('ababahalamaha', str(cm.exception)) + with self.assertRaises(self.failureException) as cm: + self.assertNotHasAttr(a, 'x', msg='ababahalamaha') + self.assertIn('ababahalamaha', str(cm.exception)) def testAssertIn(self): animals = {'monkey': 'banana', 'cow': 'grass', 'seal': 'fish'} @@ -1861,6 +1970,186 @@ def testAssertNoLogsYieldsNone(self): pass self.assertIsNone(value) + def testAssertStartswith(self): + self.assertStartswith('ababahalamaha', 'ababa') + self.assertStartswith('ababahalamaha', ('x', 'ababa', 'y')) + self.assertStartswith(UserString('ababahalamaha'), 'ababa') + self.assertStartswith(UserString('ababahalamaha'), ('x', 'ababa', 'y')) + self.assertStartswith(bytearray(b'ababahalamaha'), b'ababa') + self.assertStartswith(bytearray(b'ababahalamaha'), (b'x', b'ababa', b'y')) + self.assertStartswith(b'ababahalamaha', bytearray(b'ababa')) + self.assertStartswith(b'ababahalamaha', + (bytearray(b'x'), bytearray(b'ababa'), bytearray(b'y'))) + + with self.assertRaises(self.failureException) as cm: + self.assertStartswith('ababahalamaha', 'amaha') + self.assertEqual(str(cm.exception), + "'ababahalamaha' doesn't start with 'amaha'") + with self.assertRaises(self.failureException) as cm: + self.assertStartswith('ababahalamaha', ('x', 'y')) + self.assertEqual(str(cm.exception), + "'ababahalamaha' doesn't start with any of ('x', 'y')") + + with self.assertRaises(self.failureException) as cm: + self.assertStartswith(b'ababahalamaha', 'ababa') + self.assertEqual(str(cm.exception), 'Expected str, not bytes') + with self.assertRaises(self.failureException) as cm: + self.assertStartswith(b'ababahalamaha', ('amaha', 'ababa')) + self.assertEqual(str(cm.exception), 'Expected str, not bytes') + with self.assertRaises(self.failureException) as cm: + self.assertStartswith([], 'ababa') + self.assertEqual(str(cm.exception), 'Expected str, not list') + with self.assertRaises(self.failureException) as cm: + self.assertStartswith('ababahalamaha', b'ababa') + self.assertEqual(str(cm.exception), 'Expected bytes, not str') + with self.assertRaises(self.failureException) as cm: + self.assertStartswith('ababahalamaha', (b'amaha', b'ababa')) + self.assertEqual(str(cm.exception), 'Expected bytes, not str') + with self.assertRaises(TypeError): + self.assertStartswith('ababahalamaha', ord('a')) + + with self.assertRaises(self.failureException) as cm: + self.assertStartswith('ababahalamaha', 'amaha', 'abracadabra') + self.assertIn('ababahalamaha', str(cm.exception)) + with self.assertRaises(self.failureException) as cm: + self.assertStartswith('ababahalamaha', 'amaha', msg='abracadabra') + self.assertIn('ababahalamaha', str(cm.exception)) + + def testAssertNotStartswith(self): + self.assertNotStartswith('ababahalamaha', 'amaha') + self.assertNotStartswith('ababahalamaha', ('x', 'amaha', 'y')) + self.assertNotStartswith(UserString('ababahalamaha'), 'amaha') + self.assertNotStartswith(UserString('ababahalamaha'), ('x', 'amaha', 'y')) + self.assertNotStartswith(bytearray(b'ababahalamaha'), b'amaha') + self.assertNotStartswith(bytearray(b'ababahalamaha'), (b'x', b'amaha', b'y')) + self.assertNotStartswith(b'ababahalamaha', bytearray(b'amaha')) + self.assertNotStartswith(b'ababahalamaha', + (bytearray(b'x'), bytearray(b'amaha'), bytearray(b'y'))) + + with self.assertRaises(self.failureException) as cm: + self.assertNotStartswith('ababahalamaha', 'ababa') + self.assertEqual(str(cm.exception), + "'ababahalamaha' starts with 'ababa'") + with self.assertRaises(self.failureException) as cm: + self.assertNotStartswith('ababahalamaha', ('x', 'ababa', 'y')) + self.assertEqual(str(cm.exception), + "'ababahalamaha' starts with 'ababa'") + + with self.assertRaises(self.failureException) as cm: + self.assertNotStartswith(b'ababahalamaha', 'ababa') + self.assertEqual(str(cm.exception), 'Expected str, not bytes') + with self.assertRaises(self.failureException) as cm: + self.assertNotStartswith(b'ababahalamaha', ('amaha', 'ababa')) + self.assertEqual(str(cm.exception), 'Expected str, not bytes') + with self.assertRaises(self.failureException) as cm: + self.assertNotStartswith([], 'ababa') + self.assertEqual(str(cm.exception), 'Expected str, not list') + with self.assertRaises(self.failureException) as cm: + self.assertNotStartswith('ababahalamaha', b'ababa') + self.assertEqual(str(cm.exception), 'Expected bytes, not str') + with self.assertRaises(self.failureException) as cm: + self.assertNotStartswith('ababahalamaha', (b'amaha', b'ababa')) + self.assertEqual(str(cm.exception), 'Expected bytes, not str') + with self.assertRaises(TypeError): + self.assertNotStartswith('ababahalamaha', ord('a')) + + with self.assertRaises(self.failureException) as cm: + self.assertNotStartswith('ababahalamaha', 'ababa', 'abracadabra') + self.assertIn('ababahalamaha', str(cm.exception)) + with self.assertRaises(self.failureException) as cm: + self.assertNotStartswith('ababahalamaha', 'ababa', msg='abracadabra') + self.assertIn('ababahalamaha', str(cm.exception)) + + def testAssertEndswith(self): + self.assertEndswith('ababahalamaha', 'amaha') + self.assertEndswith('ababahalamaha', ('x', 'amaha', 'y')) + self.assertEndswith(UserString('ababahalamaha'), 'amaha') + self.assertEndswith(UserString('ababahalamaha'), ('x', 'amaha', 'y')) + self.assertEndswith(bytearray(b'ababahalamaha'), b'amaha') + self.assertEndswith(bytearray(b'ababahalamaha'), (b'x', b'amaha', b'y')) + self.assertEndswith(b'ababahalamaha', bytearray(b'amaha')) + self.assertEndswith(b'ababahalamaha', + (bytearray(b'x'), bytearray(b'amaha'), bytearray(b'y'))) + + with self.assertRaises(self.failureException) as cm: + self.assertEndswith('ababahalamaha', 'ababa') + self.assertEqual(str(cm.exception), + "'ababahalamaha' doesn't end with 'ababa'") + with self.assertRaises(self.failureException) as cm: + self.assertEndswith('ababahalamaha', ('x', 'y')) + self.assertEqual(str(cm.exception), + "'ababahalamaha' doesn't end with any of ('x', 'y')") + + with self.assertRaises(self.failureException) as cm: + self.assertEndswith(b'ababahalamaha', 'amaha') + self.assertEqual(str(cm.exception), 'Expected str, not bytes') + with self.assertRaises(self.failureException) as cm: + self.assertEndswith(b'ababahalamaha', ('ababa', 'amaha')) + self.assertEqual(str(cm.exception), 'Expected str, not bytes') + with self.assertRaises(self.failureException) as cm: + self.assertEndswith([], 'amaha') + self.assertEqual(str(cm.exception), 'Expected str, not list') + with self.assertRaises(self.failureException) as cm: + self.assertEndswith('ababahalamaha', b'amaha') + self.assertEqual(str(cm.exception), 'Expected bytes, not str') + with self.assertRaises(self.failureException) as cm: + self.assertEndswith('ababahalamaha', (b'ababa', b'amaha')) + self.assertEqual(str(cm.exception), 'Expected bytes, not str') + with self.assertRaises(TypeError): + self.assertEndswith('ababahalamaha', ord('a')) + + with self.assertRaises(self.failureException) as cm: + self.assertEndswith('ababahalamaha', 'ababa', 'abracadabra') + self.assertIn('ababahalamaha', str(cm.exception)) + with self.assertRaises(self.failureException) as cm: + self.assertEndswith('ababahalamaha', 'ababa', msg='abracadabra') + self.assertIn('ababahalamaha', str(cm.exception)) + + def testAssertNotEndswith(self): + self.assertNotEndswith('ababahalamaha', 'ababa') + self.assertNotEndswith('ababahalamaha', ('x', 'ababa', 'y')) + self.assertNotEndswith(UserString('ababahalamaha'), 'ababa') + self.assertNotEndswith(UserString('ababahalamaha'), ('x', 'ababa', 'y')) + self.assertNotEndswith(bytearray(b'ababahalamaha'), b'ababa') + self.assertNotEndswith(bytearray(b'ababahalamaha'), (b'x', b'ababa', b'y')) + self.assertNotEndswith(b'ababahalamaha', bytearray(b'ababa')) + self.assertNotEndswith(b'ababahalamaha', + (bytearray(b'x'), bytearray(b'ababa'), bytearray(b'y'))) + + with self.assertRaises(self.failureException) as cm: + self.assertNotEndswith('ababahalamaha', 'amaha') + self.assertEqual(str(cm.exception), + "'ababahalamaha' ends with 'amaha'") + with self.assertRaises(self.failureException) as cm: + self.assertNotEndswith('ababahalamaha', ('x', 'amaha', 'y')) + self.assertEqual(str(cm.exception), + "'ababahalamaha' ends with 'amaha'") + + with self.assertRaises(self.failureException) as cm: + self.assertNotEndswith(b'ababahalamaha', 'amaha') + self.assertEqual(str(cm.exception), 'Expected str, not bytes') + with self.assertRaises(self.failureException) as cm: + self.assertNotEndswith(b'ababahalamaha', ('ababa', 'amaha')) + self.assertEqual(str(cm.exception), 'Expected str, not bytes') + with self.assertRaises(self.failureException) as cm: + self.assertNotEndswith([], 'amaha') + self.assertEqual(str(cm.exception), 'Expected str, not list') + with self.assertRaises(self.failureException) as cm: + self.assertNotEndswith('ababahalamaha', b'amaha') + self.assertEqual(str(cm.exception), 'Expected bytes, not str') + with self.assertRaises(self.failureException) as cm: + self.assertNotEndswith('ababahalamaha', (b'ababa', b'amaha')) + self.assertEqual(str(cm.exception), 'Expected bytes, not str') + with self.assertRaises(TypeError): + self.assertNotEndswith('ababahalamaha', ord('a')) + + with self.assertRaises(self.failureException) as cm: + self.assertNotEndswith('ababahalamaha', 'amaha', 'abracadabra') + self.assertIn('ababahalamaha', str(cm.exception)) + with self.assertRaises(self.failureException) as cm: + self.assertNotEndswith('ababahalamaha', 'amaha', msg='abracadabra') + self.assertIn('ababahalamaha', str(cm.exception)) + def testDeprecatedFailMethods(self): """Test that the deprecated fail* methods get removed in 3.12""" deprecated_names = [ diff --git a/Lib/test/test_unittest/test_loader.py b/Lib/test/test_unittest/test_loader.py index 83dd25ca54623f..3b0b72fdd275ac 100644 --- a/Lib/test/test_unittest/test_loader.py +++ b/Lib/test/test_unittest/test_loader.py @@ -76,7 +76,7 @@ def runTest(self): loader = unittest.TestLoader() # This has to be false for the test to succeed - self.assertFalse('runTest'.startswith(loader.testMethodPrefix)) + self.assertNotStartswith('runTest', loader.testMethodPrefix) suite = loader.loadTestsFromTestCase(Foo) self.assertIsInstance(suite, loader.suiteClass) diff --git a/Lib/test/test_unittest/test_program.py b/Lib/test/test_unittest/test_program.py index 0b46f338ac77e1..93463ccbfb18b2 100644 --- a/Lib/test/test_unittest/test_program.py +++ b/Lib/test/test_unittest/test_program.py @@ -128,14 +128,14 @@ def test_NonExit(self): argv=["foobar"], testRunner=unittest.TextTestRunner(stream=stream), testLoader=self.TestLoader(self.FooBar)) - self.assertTrue(hasattr(program, 'result')) + self.assertHasAttr(program, 'result') out = stream.getvalue() self.assertIn('\nFAIL: testFail ', out) self.assertIn('\nERROR: testError ', out) self.assertIn('\nUNEXPECTED SUCCESS: testUnexpectedSuccess ', out) expected = ('\n\nFAILED (failures=1, errors=1, skipped=1, ' 'expected failures=1, unexpected successes=1)\n') - self.assertTrue(out.endswith(expected)) + self.assertEndswith(out, expected) @force_not_colorized def test_Exit(self): @@ -153,7 +153,7 @@ def test_Exit(self): self.assertIn('\nUNEXPECTED SUCCESS: testUnexpectedSuccess ', out) expected = ('\n\nFAILED (failures=1, errors=1, skipped=1, ' 'expected failures=1, unexpected successes=1)\n') - self.assertTrue(out.endswith(expected)) + self.assertEndswith(out, expected) @force_not_colorized def test_ExitAsDefault(self): @@ -169,7 +169,7 @@ def test_ExitAsDefault(self): self.assertIn('\nUNEXPECTED SUCCESS: testUnexpectedSuccess ', out) expected = ('\n\nFAILED (failures=1, errors=1, skipped=1, ' 'expected failures=1, unexpected successes=1)\n') - self.assertTrue(out.endswith(expected)) + self.assertEndswith(out, expected) @force_not_colorized def test_ExitSkippedSuite(self): @@ -182,7 +182,7 @@ def test_ExitSkippedSuite(self): self.assertEqual(cm.exception.code, 0) out = stream.getvalue() expected = '\n\nOK (skipped=1)\n' - self.assertTrue(out.endswith(expected)) + self.assertEndswith(out, expected) @force_not_colorized def test_ExitEmptySuite(self): diff --git a/Lib/test/test_unittest/test_result.py b/Lib/test/test_unittest/test_result.py index 746b9fa2677717..904349ae710fa9 100644 --- a/Lib/test/test_unittest/test_result.py +++ b/Lib/test/test_unittest/test_result.py @@ -460,7 +460,7 @@ def test(result): self.assertTrue(result.failfast) result = runner.run(test) stream.flush() - self.assertTrue(stream.getvalue().endswith('\n\nOK\n')) + self.assertEndswith(stream.getvalue(), '\n\nOK\n') class Test_TextTestResult(unittest.TestCase): diff --git a/Lib/test/test_unittest/testmock/testasync.py b/Lib/test/test_unittest/testmock/testasync.py index afc9d1f11da1e2..0791675b5401ca 100644 --- a/Lib/test/test_unittest/testmock/testasync.py +++ b/Lib/test/test_unittest/testmock/testasync.py @@ -586,16 +586,16 @@ def test_sync_magic_methods_return_magic_mocks(self): def test_magicmock_has_async_magic_methods(self): m_mock = MagicMock() - self.assertTrue(hasattr(m_mock, "__aenter__")) - self.assertTrue(hasattr(m_mock, "__aexit__")) - self.assertTrue(hasattr(m_mock, "__anext__")) + self.assertHasAttr(m_mock, "__aenter__") + self.assertHasAttr(m_mock, "__aexit__") + self.assertHasAttr(m_mock, "__anext__") def test_asyncmock_has_sync_magic_methods(self): a_mock = AsyncMock() - self.assertTrue(hasattr(a_mock, "__enter__")) - self.assertTrue(hasattr(a_mock, "__exit__")) - self.assertTrue(hasattr(a_mock, "__next__")) - self.assertTrue(hasattr(a_mock, "__len__")) + self.assertHasAttr(a_mock, "__enter__") + self.assertHasAttr(a_mock, "__exit__") + self.assertHasAttr(a_mock, "__next__") + self.assertHasAttr(a_mock, "__len__") def test_magic_methods_are_async_functions(self): m_mock = MagicMock() diff --git a/Lib/test/test_unittest/testmock/testcallable.py b/Lib/test/test_unittest/testmock/testcallable.py index ca88511f63959d..03cb983e447c70 100644 --- a/Lib/test/test_unittest/testmock/testcallable.py +++ b/Lib/test/test_unittest/testmock/testcallable.py @@ -23,21 +23,21 @@ def assertNotCallable(self, mock): def test_non_callable(self): for mock in NonCallableMagicMock(), NonCallableMock(): self.assertRaises(TypeError, mock) - self.assertFalse(hasattr(mock, '__call__')) + self.assertNotHasAttr(mock, '__call__') self.assertIn(mock.__class__.__name__, repr(mock)) def test_hierarchy(self): - self.assertTrue(issubclass(MagicMock, Mock)) - self.assertTrue(issubclass(NonCallableMagicMock, NonCallableMock)) + self.assertIsSubclass(MagicMock, Mock) + self.assertIsSubclass(NonCallableMagicMock, NonCallableMock) def test_attributes(self): one = NonCallableMock() - self.assertTrue(issubclass(type(one.one), Mock)) + self.assertIsSubclass(type(one.one), Mock) two = NonCallableMagicMock() - self.assertTrue(issubclass(type(two.two), MagicMock)) + self.assertIsSubclass(type(two.two), MagicMock) def test_subclasses(self): @@ -45,13 +45,13 @@ class MockSub(Mock): pass one = MockSub() - self.assertTrue(issubclass(type(one.one), MockSub)) + self.assertIsSubclass(type(one.one), MockSub) class MagicSub(MagicMock): pass two = MagicSub() - self.assertTrue(issubclass(type(two.two), MagicSub)) + self.assertIsSubclass(type(two.two), MagicSub) def test_patch_spec(self): diff --git a/Lib/test/test_unittest/testmock/testhelpers.py b/Lib/test/test_unittest/testmock/testhelpers.py index f260769eb8c35e..8d0f3ebc5cba88 100644 --- a/Lib/test/test_unittest/testmock/testhelpers.py +++ b/Lib/test/test_unittest/testmock/testhelpers.py @@ -951,7 +951,7 @@ def __getattr__(self, attribute): proxy = Foo() autospec = create_autospec(proxy) - self.assertFalse(hasattr(autospec, '__name__')) + self.assertNotHasAttr(autospec, '__name__') def test_autospec_signature_staticmethod(self): diff --git a/Lib/test/test_unittest/testmock/testmagicmethods.py b/Lib/test/test_unittest/testmock/testmagicmethods.py index 2a8aa11b3284f6..acdbd699d18134 100644 --- a/Lib/test/test_unittest/testmock/testmagicmethods.py +++ b/Lib/test/test_unittest/testmock/testmagicmethods.py @@ -10,13 +10,13 @@ class TestMockingMagicMethods(unittest.TestCase): def test_deleting_magic_methods(self): mock = Mock() - self.assertFalse(hasattr(mock, '__getitem__')) + self.assertNotHasAttr(mock, '__getitem__') mock.__getitem__ = Mock() - self.assertTrue(hasattr(mock, '__getitem__')) + self.assertHasAttr(mock, '__getitem__') del mock.__getitem__ - self.assertFalse(hasattr(mock, '__getitem__')) + self.assertNotHasAttr(mock, '__getitem__') def test_magicmock_del(self): @@ -252,12 +252,12 @@ def test_magicmock(self): self.assertEqual(list(mock), [1, 2, 3]) getattr(mock, '__bool__').return_value = False - self.assertFalse(hasattr(mock, '__nonzero__')) + self.assertNotHasAttr(mock, '__nonzero__') self.assertFalse(bool(mock)) for entry in _magics: - self.assertTrue(hasattr(mock, entry)) - self.assertFalse(hasattr(mock, '__imaginary__')) + self.assertHasAttr(mock, entry) + self.assertNotHasAttr(mock, '__imaginary__') def test_magic_mock_equality(self): diff --git a/Lib/test/test_unittest/testmock/testmock.py b/Lib/test/test_unittest/testmock/testmock.py index e1b108f81e513c..5d1bf4258afacd 100644 --- a/Lib/test/test_unittest/testmock/testmock.py +++ b/Lib/test/test_unittest/testmock/testmock.py @@ -2215,13 +2215,13 @@ def test_attach_mock_patch_autospec_signature(self): def test_attribute_deletion(self): for mock in (Mock(), MagicMock(), NonCallableMagicMock(), NonCallableMock()): - self.assertTrue(hasattr(mock, 'm')) + self.assertHasAttr(mock, 'm') del mock.m - self.assertFalse(hasattr(mock, 'm')) + self.assertNotHasAttr(mock, 'm') del mock.f - self.assertFalse(hasattr(mock, 'f')) + self.assertNotHasAttr(mock, 'f') self.assertRaises(AttributeError, getattr, mock, 'f') @@ -2230,18 +2230,18 @@ def test_mock_does_not_raise_on_repeated_attribute_deletion(self): for mock in (Mock(), MagicMock(), NonCallableMagicMock(), NonCallableMock()): mock.foo = 3 - self.assertTrue(hasattr(mock, 'foo')) + self.assertHasAttr(mock, 'foo') self.assertEqual(mock.foo, 3) del mock.foo - self.assertFalse(hasattr(mock, 'foo')) + self.assertNotHasAttr(mock, 'foo') mock.foo = 4 - self.assertTrue(hasattr(mock, 'foo')) + self.assertHasAttr(mock, 'foo') self.assertEqual(mock.foo, 4) del mock.foo - self.assertFalse(hasattr(mock, 'foo')) + self.assertNotHasAttr(mock, 'foo') def test_mock_raises_when_deleting_nonexistent_attribute(self): @@ -2259,7 +2259,7 @@ def test_reset_mock_does_not_raise_on_attr_deletion(self): mock.child = True del mock.child mock.reset_mock() - self.assertFalse(hasattr(mock, 'child')) + self.assertNotHasAttr(mock, 'child') def test_class_assignable(self): diff --git a/Lib/test/test_unittest/testmock/testpatch.py b/Lib/test/test_unittest/testmock/testpatch.py index 037c021e6eafcf..7c5fc3deed2ca2 100644 --- a/Lib/test/test_unittest/testmock/testpatch.py +++ b/Lib/test/test_unittest/testmock/testpatch.py @@ -366,7 +366,7 @@ def test(): self.assertEqual(SomeClass.frooble, sentinel.Frooble) test() - self.assertFalse(hasattr(SomeClass, 'frooble')) + self.assertNotHasAttr(SomeClass, 'frooble') def test_patch_wont_create_by_default(self): @@ -383,7 +383,7 @@ def test_patchobject_wont_create_by_default(self): @patch.object(SomeClass, 'ord', sentinel.Frooble) def test(): pass test() - self.assertFalse(hasattr(SomeClass, 'ord')) + self.assertNotHasAttr(SomeClass, 'ord') def test_patch_builtins_without_create(self): @@ -1477,7 +1477,7 @@ def test_patch_multiple_create(self): finally: patcher.stop() - self.assertFalse(hasattr(Foo, 'blam')) + self.assertNotHasAttr(Foo, 'blam') def test_patch_multiple_spec_set(self): diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py index 0b09010c69d4ea..19115b0d08f87b 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -111,10 +111,6 @@ def get_text_file_contents(self, *args, encoding='utf-8'): result = f.read() return result - def assertEndsWith(self, string, tail): - if not string.endswith(tail): - self.fail(f"String {string!r} does not end with {tail!r}") - class BasicTest(BaseTest): """Test venv module functionality.""" @@ -517,7 +513,7 @@ def test_special_chars_bash(self): out, err = check_output([bash, test_script]) lines = out.splitlines() self.assertTrue(env_name.encode() in lines[0]) - self.assertEndsWith(lines[1], env_name.encode()) + self.assertEndswith(lines[1], env_name.encode()) # gh-124651: test quoted strings @unittest.skipIf(os.name == 'nt', 'contains invalid characters on Windows') @@ -543,7 +539,7 @@ def test_special_chars_csh(self): out, err = check_output([csh, test_script]) lines = out.splitlines() self.assertTrue(env_name.encode() in lines[0]) - self.assertEndsWith(lines[1], env_name.encode()) + self.assertEndswith(lines[1], env_name.encode()) # gh-124651: test quoted strings on Windows @unittest.skipUnless(os.name == 'nt', 'only relevant on Windows') @@ -567,7 +563,7 @@ def test_special_chars_windows(self): out, err = check_output([test_batch]) lines = out.splitlines() self.assertTrue(env_name.encode() in lines[0]) - self.assertEndsWith(lines[1], env_name.encode()) + self.assertEndswith(lines[1], env_name.encode()) @unittest.skipUnless(os.name == 'nt', 'only relevant on Windows') def test_unicode_in_batch_file(self): diff --git a/Lib/unittest/case.py b/Lib/unittest/case.py index 55c79d353539ca..3df95d47429d40 100644 --- a/Lib/unittest/case.py +++ b/Lib/unittest/case.py @@ -1321,15 +1321,77 @@ def assertIsInstance(self, obj, cls, msg=None): """Same as self.assertTrue(isinstance(obj, cls)), with a nicer default message.""" if not isinstance(obj, cls): - standardMsg = '%s is not an instance of %r' % (safe_repr(obj), cls) + if isinstance(cls, tuple): + standardMsg = '%s is not an instance of any of %r' % (safe_repr(obj), cls) + else: + standardMsg = '%s is not an instance of %r' % (safe_repr(obj), cls) self.fail(self._formatMessage(msg, standardMsg)) def assertNotIsInstance(self, obj, cls, msg=None): """Included for symmetry with assertIsInstance.""" if isinstance(obj, cls): + if isinstance(cls, tuple): + for x in cls: + if isinstance(obj, x): + cls = x + break standardMsg = '%s is an instance of %r' % (safe_repr(obj), cls) self.fail(self._formatMessage(msg, standardMsg)) + def assertIsSubclass(self, cls, superclass, msg=None): + try: + r = issubclass(cls, superclass) + except TypeError: + if not isinstance(cls, type): + self.fail(self._formatMessage(msg, + '%r is not a class' % (cls,))) + raise + if not r: + if isinstance(superclass, tuple): + standardMsg = '%r is not a subclass of any of %r' % (cls, superclass) + else: + standardMsg = '%r is not a subclass of %r' % (cls, superclass) + self.fail(self._formatMessage(msg, standardMsg)) + + def assertNotIsSubclass(self, cls, superclass, msg=None): + try: + r = issubclass(cls, superclass) + except TypeError: + if not isinstance(cls, type): + self.fail(self._formatMessage(msg, + '%r is not a class' % (cls,))) + raise + if r: + if isinstance(superclass, tuple): + for x in superclass: + if issubclass(cls, x): + superclass = x + break + self.fail(self._formatMessage(msg, + '%r is a subclass of %r' % (cls, superclass))) + + def assertHasAttr(self, obj, name, msg=None): + if not hasattr(obj, name): + if isinstance(obj, types.ModuleType): + self.fail(self._formatMessage(msg, + 'module %r has no attribute %r' % + (obj.__name__, name))) + else: + self.fail(self._formatMessage(msg, + '%s instance has no attribute %r' % + (type(obj).__name__, name))) + + def assertNotHasAttr(self, obj, name, msg=None): + if hasattr(obj, name): + if isinstance(obj, types.ModuleType): + self.fail(self._formatMessage(msg, + 'module %r has unexpected attribute %r' % + (obj.__name__, name))) + else: + self.fail(self._formatMessage(msg, + '%s instance has unexpected attribute %r' % + (type(obj).__name__, name))) + def assertRaisesRegex(self, expected_exception, expected_regex, *args, **kwargs): """Asserts that the message in a raised exception matches a regex. @@ -1391,6 +1453,84 @@ def assertNotRegex(self, text, unexpected_regex, msg=None): msg = self._formatMessage(msg, standardMsg) raise self.failureException(msg) + def _tail_type_check(self, s, tails, msg): + if not isinstance(tails, tuple): + tails = (tails,) + for tail in tails: + if isinstance(tail, str): + if not isinstance(s, str): + self.fail(self._formatMessage(msg, + 'Expected str, not %s' % (type(s).__name__,))) + elif isinstance(tail, (bytes, bytearray)): + if not isinstance(s, (bytes, bytearray)): + self.fail(self._formatMessage(msg, + 'Expected bytes, not %s' % (type(s).__name__,))) + + def assertStartswith(self, s, prefix, msg=None): + try: + r = s.startswith(prefix) + except (AttributeError, TypeError): + self._tail_type_check(s, prefix, msg) + raise + if not r: + a = safe_repr(s, short=True) + b = safe_repr(prefix) + if isinstance(prefix, tuple): + self.fail(self._formatMessage(msg, + "%s doesn't start with any of %s" % (a, b))) + else: + self.fail(self._formatMessage(msg, + "%s doesn't start with %s" % (a, b))) + + def assertNotStartswith(self, s, prefix, msg=None): + try: + r = s.startswith(prefix) + except (AttributeError, TypeError): + self._tail_type_check(s, prefix, msg) + raise + if r: + if isinstance(prefix, tuple): + for x in prefix: + if s.startswith(x): + prefix = x + break + a = safe_repr(s, short=True) + b = safe_repr(prefix) + self.fail(self._formatMessage(msg, + "%s starts with %s" % (a, b))) + + def assertEndswith(self, s, suffix, msg=None): + try: + r = s.endswith(suffix) + except (AttributeError, TypeError): + self._tail_type_check(s, suffix, msg) + raise + if not r: + a = safe_repr(s, short=True) + b = safe_repr(suffix) + if isinstance(suffix, tuple): + self.fail(self._formatMessage(msg, + "%s doesn't end with any of %s" % (a, b))) + else: + self.fail(self._formatMessage(msg, + "%s doesn't end with %s" % (a, b))) + + def assertNotEndswith(self, s, suffix, msg=None): + try: + r = s.endswith(suffix) + except (AttributeError, TypeError): + self._tail_type_check(s, suffix, msg) + raise + if r: + if isinstance(suffix, tuple): + for x in suffix: + if s.endswith(x): + suffix = x + break + a = safe_repr(s, short=True) + b = safe_repr(suffix) + self.fail(self._formatMessage(msg, + "%s ends with %s" % (a, b))) class FunctionTestCase(TestCase): diff --git a/Misc/NEWS.d/next/Library/2025-01-10-15-06-45.gh-issue-71339.EKnpzw.rst b/Misc/NEWS.d/next/Library/2025-01-10-15-06-45.gh-issue-71339.EKnpzw.rst new file mode 100644 index 00000000000000..5f33a30bd5eae1 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-01-10-15-06-45.gh-issue-71339.EKnpzw.rst @@ -0,0 +1,9 @@ +Add new assertion methods for :mod:`unittest`: +:meth:`~unittest.TestCase.assertHasAttr`, +:meth:`~unittest.TestCase.assertNotHasAttr`, +:meth:`~unittest.TestCase.assertIsSubclass`, +:meth:`~unittest.TestCase.assertNotIsSubclass` +:meth:`~unittest.TestCase.assertStartsWith`, +:meth:`~unittest.TestCase.assertNotStartsWith`, +:meth:`~unittest.TestCase.assertEndsWith` and +:meth:`~unittest.TestCase.assertNotEndsWith`. From b825e23d94e08e82af5dc9091bf86eee0329e809 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 10 Jan 2025 15:26:22 +0200 Subject: [PATCH 2/7] Fix references: "with" instead of "With". --- Doc/whatsnew/3.14.rst | 8 ++++---- .../Library/2025-01-10-15-06-45.gh-issue-71339.EKnpzw.rst | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 29f520ed05c030..5f2f8f63c916cc 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -679,10 +679,10 @@ unittest - :meth:`~unittest.TestCase.assertIsSubclass` and :meth:`~unittest.TestCase.assertNotIsSubclass` check whether the object is a subclass of a particular class, or of one of a tuple of classes. - - :meth:`~unittest.TestCase.assertStartsWith`, - :meth:`~unittest.TestCase.assertNotStartsWith`, - :meth:`~unittest.TestCase.assertEndsWith` and - :meth:`~unittest.TestCase.assertNotEndsWith` check whether the unicode + - :meth:`~unittest.TestCase.assertStartswith`, + :meth:`~unittest.TestCase.assertNotStartswith`, + :meth:`~unittest.TestCase.assertEndswith` and + :meth:`~unittest.TestCase.assertNotEndswith` check whether the unicode or byte string starts or ends with particular string(s). (Contributed by Serhiy Storchaka in :gh:`71339`.) diff --git a/Misc/NEWS.d/next/Library/2025-01-10-15-06-45.gh-issue-71339.EKnpzw.rst b/Misc/NEWS.d/next/Library/2025-01-10-15-06-45.gh-issue-71339.EKnpzw.rst index 5f33a30bd5eae1..1d10e4a4dfe3bc 100644 --- a/Misc/NEWS.d/next/Library/2025-01-10-15-06-45.gh-issue-71339.EKnpzw.rst +++ b/Misc/NEWS.d/next/Library/2025-01-10-15-06-45.gh-issue-71339.EKnpzw.rst @@ -3,7 +3,7 @@ Add new assertion methods for :mod:`unittest`: :meth:`~unittest.TestCase.assertNotHasAttr`, :meth:`~unittest.TestCase.assertIsSubclass`, :meth:`~unittest.TestCase.assertNotIsSubclass` -:meth:`~unittest.TestCase.assertStartsWith`, -:meth:`~unittest.TestCase.assertNotStartsWith`, -:meth:`~unittest.TestCase.assertEndsWith` and -:meth:`~unittest.TestCase.assertNotEndsWith`. +:meth:`~unittest.TestCase.assertStartswith`, +:meth:`~unittest.TestCase.assertNotStartswith`, +:meth:`~unittest.TestCase.assertEndswith` and +:meth:`~unittest.TestCase.assertNotEndswith`. From a54e69396f78c116e538f83c99e9d470d8bfbeb3 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 11 Jan 2025 16:20:57 +0200 Subject: [PATCH 3/7] Spell "With" instead of "with". --- Doc/library/unittest.rst | 24 +-- Doc/whatsnew/3.14.rst | 8 +- Lib/test/test_gdb/test_misc.py | 6 +- .../resources/test_functional.py | 4 +- Lib/test/test_typing.py | 110 ++++++------- Lib/test/test_unittest/test_case.py | 148 +++++++++--------- Lib/test/test_unittest/test_loader.py | 2 +- Lib/test/test_unittest/test_program.py | 8 +- Lib/test/test_unittest/test_result.py | 2 +- Lib/test/test_venv.py | 6 +- Lib/unittest/case.py | 8 +- ...5-01-10-15-06-45.gh-issue-71339.EKnpzw.rst | 8 +- 12 files changed, 167 insertions(+), 167 deletions(-) diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst index f1f3f887de81ef..d885f8b576de0f 100644 --- a/Doc/library/unittest.rst +++ b/Doc/library/unittest.rst @@ -1224,17 +1224,17 @@ Test cases | ` | elements in the same number, | | | | regardless of their order. | | +---------------------------------------+--------------------------------+--------------+ - | :meth:`assertStartswith(a, b) | ``a.startswith(b)`` | 3.14 | - | ` | | | + | :meth:`assertStartsWith(a, b) | ``a.startswith(b)`` | 3.14 | + | ` | | | +---------------------------------------+--------------------------------+--------------+ - | :meth:`assertNotStartswith(a, b) | ``not a.startswith(b)`` | 3.14 | - | ` | | | + | :meth:`assertNotStartsWith(a, b) | ``not a.startswith(b)`` | 3.14 | + | ` | | | +---------------------------------------+--------------------------------+--------------+ - | :meth:`assertEndswith(a, b) | ``a.endswith(b)`` | 3.14 | - | ` | | | + | :meth:`assertEndsWith(a, b) | ``a.endswith(b)`` | 3.14 | + | ` | | | +---------------------------------------+--------------------------------+--------------+ - | :meth:`assertNotEndswith(a, b) | ``not a.endswith(b)`` | 3.14 | - | ` | | | + | :meth:`assertNotEndsWith(a, b) | ``not a.endswith(b)`` | 3.14 | + | ` | | | +---------------------------------------+--------------------------------+--------------+ | :meth:`assertHasAttr(a, b) | ``hastattr(a, b)`` | 3.14 | | ` | | | @@ -1310,8 +1310,8 @@ Test cases .. versionadded:: 3.2 - .. method:: assertStartswith(s, prefix, msg=None) - .. method:: assertNotStartswith(s, prefix, msg=None) + .. method:: assertStartsWith(s, prefix, msg=None) + .. method:: assertNotStartsWith(s, prefix, msg=None) Test that the unicode or byte string *s* starts (or does not start) with a *prefix*. @@ -1319,8 +1319,8 @@ Test cases .. versionadded:: next - .. method:: assertEndswith(s, suffix, msg=None) - .. method:: assertNotEndswith(s, suffix, msg=None) + .. method:: assertEndsWith(s, suffix, msg=None) + .. method:: assertNotEndsWith(s, suffix, msg=None) Test that the unicode or byte string *s* ends (or does not end) with a *suffix*. diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 5f2f8f63c916cc..29f520ed05c030 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -679,10 +679,10 @@ unittest - :meth:`~unittest.TestCase.assertIsSubclass` and :meth:`~unittest.TestCase.assertNotIsSubclass` check whether the object is a subclass of a particular class, or of one of a tuple of classes. - - :meth:`~unittest.TestCase.assertStartswith`, - :meth:`~unittest.TestCase.assertNotStartswith`, - :meth:`~unittest.TestCase.assertEndswith` and - :meth:`~unittest.TestCase.assertNotEndswith` check whether the unicode + - :meth:`~unittest.TestCase.assertStartsWith`, + :meth:`~unittest.TestCase.assertNotStartsWith`, + :meth:`~unittest.TestCase.assertEndsWith` and + :meth:`~unittest.TestCase.assertNotEndsWith` check whether the unicode or byte string starts or ends with particular string(s). (Contributed by Serhiy Storchaka in :gh:`71339`.) diff --git a/Lib/test/test_gdb/test_misc.py b/Lib/test/test_gdb/test_misc.py index a66ab8d574b747..1047f4867c1d03 100644 --- a/Lib/test/test_gdb/test_misc.py +++ b/Lib/test/test_gdb/test_misc.py @@ -28,7 +28,7 @@ def gdb_has_frame_select(): "Python was compiled with optimizations") class PyListTests(DebuggerTests): def assertListing(self, expected, actual): - self.assertEndswith(actual, expected) + self.assertEndsWith(actual, expected) def test_basic_command(self): 'Verify that the "py-list" command works' @@ -103,7 +103,7 @@ def test_down_at_bottom(self): 'Verify handling of "py-down" at the bottom of the stack' bt = self.get_stack_trace(script=SAMPLE_SCRIPT, cmds_after_breakpoint=['py-down']) - self.assertEndswith(bt, + self.assertEndsWith(bt, 'Unable to find a newer python frame\n') @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") @@ -111,7 +111,7 @@ def test_up_at_top(self): 'Verify handling of "py-up" at the top of the stack' bt = self.get_stack_trace(script=SAMPLE_SCRIPT, cmds_after_breakpoint=['py-up'] * 5) - self.assertEndswith(bt, + self.assertEndsWith(bt, 'Unable to find an older python frame\n') @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") diff --git a/Lib/test/test_importlib/resources/test_functional.py b/Lib/test/test_importlib/resources/test_functional.py index 8070fce20bb3c9..e8d25fa4d9faf0 100644 --- a/Lib/test/test_importlib/resources/test_functional.py +++ b/Lib/test/test_importlib/resources/test_functional.py @@ -83,7 +83,7 @@ def test_read_text(self): ), '\x00\x01\x02\x03', ) - self.assertEndswith( # ignore the BOM + self.assertEndsWith( # ignore the BOM resources.read_text( self.anchor01, 'utf-16.file', @@ -135,7 +135,7 @@ def test_open_text(self): 'utf-16.file', errors='backslashreplace', ) as f: - self.assertEndswith( # ignore the BOM + self.assertEndsWith( # ignore the BOM f.read(), 'Hello, UTF-16 world!\n'.encode('utf-16-le').decode( errors='backslashreplace', diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 931ce8ed09fc29..c98e6f820e8cf7 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -1426,31 +1426,31 @@ def test_variadic_class_repr_is_correct(self): class A(Generic[*Ts]): pass class B(Generic[Unpack[Ts]]): pass - self.assertEndswith(repr(A[()]), 'A[()]') - self.assertEndswith(repr(B[()]), 'B[()]') - self.assertEndswith(repr(A[float]), 'A[float]') - self.assertEndswith(repr(B[float]), 'B[float]') - self.assertEndswith(repr(A[float, str]), 'A[float, str]') - self.assertEndswith(repr(B[float, str]), 'B[float, str]') - - self.assertEndswith(repr(A[*tuple[int, ...]]), + self.assertEndsWith(repr(A[()]), 'A[()]') + self.assertEndsWith(repr(B[()]), 'B[()]') + self.assertEndsWith(repr(A[float]), 'A[float]') + self.assertEndsWith(repr(B[float]), 'B[float]') + self.assertEndsWith(repr(A[float, str]), 'A[float, str]') + self.assertEndsWith(repr(B[float, str]), 'B[float, str]') + + self.assertEndsWith(repr(A[*tuple[int, ...]]), 'A[*tuple[int, ...]]') - self.assertEndswith(repr(B[Unpack[Tuple[int, ...]]]), + self.assertEndsWith(repr(B[Unpack[Tuple[int, ...]]]), 'B[typing.Unpack[typing.Tuple[int, ...]]]') - self.assertEndswith(repr(A[float, *tuple[int, ...]]), + self.assertEndsWith(repr(A[float, *tuple[int, ...]]), 'A[float, *tuple[int, ...]]') - self.assertEndswith(repr(A[float, Unpack[Tuple[int, ...]]]), + self.assertEndsWith(repr(A[float, Unpack[Tuple[int, ...]]]), 'A[float, typing.Unpack[typing.Tuple[int, ...]]]') - self.assertEndswith(repr(A[*tuple[int, ...], str]), + self.assertEndsWith(repr(A[*tuple[int, ...], str]), 'A[*tuple[int, ...], str]') - self.assertEndswith(repr(B[Unpack[Tuple[int, ...]], str]), + self.assertEndsWith(repr(B[Unpack[Tuple[int, ...]], str]), 'B[typing.Unpack[typing.Tuple[int, ...]], str]') - self.assertEndswith(repr(A[float, *tuple[int, ...], str]), + self.assertEndsWith(repr(A[float, *tuple[int, ...], str]), 'A[float, *tuple[int, ...], str]') - self.assertEndswith(repr(B[float, Unpack[Tuple[int, ...]], str]), + self.assertEndsWith(repr(B[float, Unpack[Tuple[int, ...]], str]), 'B[float, typing.Unpack[typing.Tuple[int, ...]], str]') def test_variadic_class_alias_repr_is_correct(self): @@ -1458,64 +1458,64 @@ def test_variadic_class_alias_repr_is_correct(self): class A(Generic[Unpack[Ts]]): pass B = A[*Ts] - self.assertEndswith(repr(B), 'A[typing.Unpack[Ts]]') - self.assertEndswith(repr(B[()]), 'A[()]') - self.assertEndswith(repr(B[float]), 'A[float]') - self.assertEndswith(repr(B[float, str]), 'A[float, str]') + self.assertEndsWith(repr(B), 'A[typing.Unpack[Ts]]') + self.assertEndsWith(repr(B[()]), 'A[()]') + self.assertEndsWith(repr(B[float]), 'A[float]') + self.assertEndsWith(repr(B[float, str]), 'A[float, str]') C = A[Unpack[Ts]] - self.assertEndswith(repr(C), 'A[typing.Unpack[Ts]]') - self.assertEndswith(repr(C[()]), 'A[()]') - self.assertEndswith(repr(C[float]), 'A[float]') - self.assertEndswith(repr(C[float, str]), 'A[float, str]') + self.assertEndsWith(repr(C), 'A[typing.Unpack[Ts]]') + self.assertEndsWith(repr(C[()]), 'A[()]') + self.assertEndsWith(repr(C[float]), 'A[float]') + self.assertEndsWith(repr(C[float, str]), 'A[float, str]') D = A[*Ts, int] - self.assertEndswith(repr(D), 'A[typing.Unpack[Ts], int]') - self.assertEndswith(repr(D[()]), 'A[int]') - self.assertEndswith(repr(D[float]), 'A[float, int]') - self.assertEndswith(repr(D[float, str]), 'A[float, str, int]') + self.assertEndsWith(repr(D), 'A[typing.Unpack[Ts], int]') + self.assertEndsWith(repr(D[()]), 'A[int]') + self.assertEndsWith(repr(D[float]), 'A[float, int]') + self.assertEndsWith(repr(D[float, str]), 'A[float, str, int]') E = A[Unpack[Ts], int] - self.assertEndswith(repr(E), 'A[typing.Unpack[Ts], int]') - self.assertEndswith(repr(E[()]), 'A[int]') - self.assertEndswith(repr(E[float]), 'A[float, int]') - self.assertEndswith(repr(E[float, str]), 'A[float, str, int]') + self.assertEndsWith(repr(E), 'A[typing.Unpack[Ts], int]') + self.assertEndsWith(repr(E[()]), 'A[int]') + self.assertEndsWith(repr(E[float]), 'A[float, int]') + self.assertEndsWith(repr(E[float, str]), 'A[float, str, int]') F = A[int, *Ts] - self.assertEndswith(repr(F), 'A[int, typing.Unpack[Ts]]') - self.assertEndswith(repr(F[()]), 'A[int]') - self.assertEndswith(repr(F[float]), 'A[int, float]') - self.assertEndswith(repr(F[float, str]), 'A[int, float, str]') + self.assertEndsWith(repr(F), 'A[int, typing.Unpack[Ts]]') + self.assertEndsWith(repr(F[()]), 'A[int]') + self.assertEndsWith(repr(F[float]), 'A[int, float]') + self.assertEndsWith(repr(F[float, str]), 'A[int, float, str]') G = A[int, Unpack[Ts]] - self.assertEndswith(repr(G), 'A[int, typing.Unpack[Ts]]') - self.assertEndswith(repr(G[()]), 'A[int]') - self.assertEndswith(repr(G[float]), 'A[int, float]') - self.assertEndswith(repr(G[float, str]), 'A[int, float, str]') + self.assertEndsWith(repr(G), 'A[int, typing.Unpack[Ts]]') + self.assertEndsWith(repr(G[()]), 'A[int]') + self.assertEndsWith(repr(G[float]), 'A[int, float]') + self.assertEndsWith(repr(G[float, str]), 'A[int, float, str]') H = A[int, *Ts, str] - self.assertEndswith(repr(H), 'A[int, typing.Unpack[Ts], str]') - self.assertEndswith(repr(H[()]), 'A[int, str]') - self.assertEndswith(repr(H[float]), 'A[int, float, str]') - self.assertEndswith(repr(H[float, str]), 'A[int, float, str, str]') + self.assertEndsWith(repr(H), 'A[int, typing.Unpack[Ts], str]') + self.assertEndsWith(repr(H[()]), 'A[int, str]') + self.assertEndsWith(repr(H[float]), 'A[int, float, str]') + self.assertEndsWith(repr(H[float, str]), 'A[int, float, str, str]') I = A[int, Unpack[Ts], str] - self.assertEndswith(repr(I), 'A[int, typing.Unpack[Ts], str]') - self.assertEndswith(repr(I[()]), 'A[int, str]') - self.assertEndswith(repr(I[float]), 'A[int, float, str]') - self.assertEndswith(repr(I[float, str]), 'A[int, float, str, str]') + self.assertEndsWith(repr(I), 'A[int, typing.Unpack[Ts], str]') + self.assertEndsWith(repr(I[()]), 'A[int, str]') + self.assertEndsWith(repr(I[float]), 'A[int, float, str]') + self.assertEndsWith(repr(I[float, str]), 'A[int, float, str, str]') J = A[*Ts, *tuple[str, ...]] - self.assertEndswith(repr(J), 'A[typing.Unpack[Ts], *tuple[str, ...]]') - self.assertEndswith(repr(J[()]), 'A[*tuple[str, ...]]') - self.assertEndswith(repr(J[float]), 'A[float, *tuple[str, ...]]') - self.assertEndswith(repr(J[float, str]), 'A[float, str, *tuple[str, ...]]') + self.assertEndsWith(repr(J), 'A[typing.Unpack[Ts], *tuple[str, ...]]') + self.assertEndsWith(repr(J[()]), 'A[*tuple[str, ...]]') + self.assertEndsWith(repr(J[float]), 'A[float, *tuple[str, ...]]') + self.assertEndsWith(repr(J[float, str]), 'A[float, str, *tuple[str, ...]]') K = A[Unpack[Ts], Unpack[Tuple[str, ...]]] - self.assertEndswith(repr(K), 'A[typing.Unpack[Ts], typing.Unpack[typing.Tuple[str, ...]]]') - self.assertEndswith(repr(K[()]), 'A[typing.Unpack[typing.Tuple[str, ...]]]') - self.assertEndswith(repr(K[float]), 'A[float, typing.Unpack[typing.Tuple[str, ...]]]') - self.assertEndswith(repr(K[float, str]), 'A[float, str, typing.Unpack[typing.Tuple[str, ...]]]') + self.assertEndsWith(repr(K), 'A[typing.Unpack[Ts], typing.Unpack[typing.Tuple[str, ...]]]') + self.assertEndsWith(repr(K[()]), 'A[typing.Unpack[typing.Tuple[str, ...]]]') + self.assertEndsWith(repr(K[float]), 'A[float, typing.Unpack[typing.Tuple[str, ...]]]') + self.assertEndsWith(repr(K[float, str]), 'A[float, str, typing.Unpack[typing.Tuple[str, ...]]]') def test_cannot_subclass(self): with self.assertRaisesRegex(TypeError, NOT_A_BASE_TYPE % 'TypeVarTuple'): diff --git a/Lib/test/test_unittest/test_case.py b/Lib/test/test_unittest/test_case.py index 3f440f276b768c..5693679191c337 100644 --- a/Lib/test/test_unittest/test_case.py +++ b/Lib/test/test_unittest/test_case.py @@ -90,7 +90,7 @@ class Test(unittest.TestCase): def runTest(self): raise MyException() def test(self): pass - self.assertEndswith(Test().id(), '.Test.runTest') + self.assertEndsWith(Test().id(), '.Test.runTest') # test that TestCase can be instantiated with no args # primarily for use at the interactive interpreter @@ -111,7 +111,7 @@ class Test(unittest.TestCase): def runTest(self): raise MyException() def test(self): pass - self.assertEndswith(Test('test').id(), '.Test.test') + self.assertEndsWith(Test('test').id(), '.Test.test') # "class TestCase([methodName])" # ... @@ -1971,183 +1971,183 @@ def testAssertNoLogsYieldsNone(self): self.assertIsNone(value) def testAssertStartswith(self): - self.assertStartswith('ababahalamaha', 'ababa') - self.assertStartswith('ababahalamaha', ('x', 'ababa', 'y')) - self.assertStartswith(UserString('ababahalamaha'), 'ababa') - self.assertStartswith(UserString('ababahalamaha'), ('x', 'ababa', 'y')) - self.assertStartswith(bytearray(b'ababahalamaha'), b'ababa') - self.assertStartswith(bytearray(b'ababahalamaha'), (b'x', b'ababa', b'y')) - self.assertStartswith(b'ababahalamaha', bytearray(b'ababa')) - self.assertStartswith(b'ababahalamaha', + self.assertStartsWith('ababahalamaha', 'ababa') + self.assertStartsWith('ababahalamaha', ('x', 'ababa', 'y')) + self.assertStartsWith(UserString('ababahalamaha'), 'ababa') + self.assertStartsWith(UserString('ababahalamaha'), ('x', 'ababa', 'y')) + self.assertStartsWith(bytearray(b'ababahalamaha'), b'ababa') + self.assertStartsWith(bytearray(b'ababahalamaha'), (b'x', b'ababa', b'y')) + self.assertStartsWith(b'ababahalamaha', bytearray(b'ababa')) + self.assertStartsWith(b'ababahalamaha', (bytearray(b'x'), bytearray(b'ababa'), bytearray(b'y'))) with self.assertRaises(self.failureException) as cm: - self.assertStartswith('ababahalamaha', 'amaha') + self.assertStartsWith('ababahalamaha', 'amaha') self.assertEqual(str(cm.exception), "'ababahalamaha' doesn't start with 'amaha'") with self.assertRaises(self.failureException) as cm: - self.assertStartswith('ababahalamaha', ('x', 'y')) + self.assertStartsWith('ababahalamaha', ('x', 'y')) self.assertEqual(str(cm.exception), "'ababahalamaha' doesn't start with any of ('x', 'y')") with self.assertRaises(self.failureException) as cm: - self.assertStartswith(b'ababahalamaha', 'ababa') + self.assertStartsWith(b'ababahalamaha', 'ababa') self.assertEqual(str(cm.exception), 'Expected str, not bytes') with self.assertRaises(self.failureException) as cm: - self.assertStartswith(b'ababahalamaha', ('amaha', 'ababa')) + self.assertStartsWith(b'ababahalamaha', ('amaha', 'ababa')) self.assertEqual(str(cm.exception), 'Expected str, not bytes') with self.assertRaises(self.failureException) as cm: - self.assertStartswith([], 'ababa') + self.assertStartsWith([], 'ababa') self.assertEqual(str(cm.exception), 'Expected str, not list') with self.assertRaises(self.failureException) as cm: - self.assertStartswith('ababahalamaha', b'ababa') + self.assertStartsWith('ababahalamaha', b'ababa') self.assertEqual(str(cm.exception), 'Expected bytes, not str') with self.assertRaises(self.failureException) as cm: - self.assertStartswith('ababahalamaha', (b'amaha', b'ababa')) + self.assertStartsWith('ababahalamaha', (b'amaha', b'ababa')) self.assertEqual(str(cm.exception), 'Expected bytes, not str') with self.assertRaises(TypeError): - self.assertStartswith('ababahalamaha', ord('a')) + self.assertStartsWith('ababahalamaha', ord('a')) with self.assertRaises(self.failureException) as cm: - self.assertStartswith('ababahalamaha', 'amaha', 'abracadabra') + self.assertStartsWith('ababahalamaha', 'amaha', 'abracadabra') self.assertIn('ababahalamaha', str(cm.exception)) with self.assertRaises(self.failureException) as cm: - self.assertStartswith('ababahalamaha', 'amaha', msg='abracadabra') + self.assertStartsWith('ababahalamaha', 'amaha', msg='abracadabra') self.assertIn('ababahalamaha', str(cm.exception)) def testAssertNotStartswith(self): - self.assertNotStartswith('ababahalamaha', 'amaha') - self.assertNotStartswith('ababahalamaha', ('x', 'amaha', 'y')) - self.assertNotStartswith(UserString('ababahalamaha'), 'amaha') - self.assertNotStartswith(UserString('ababahalamaha'), ('x', 'amaha', 'y')) - self.assertNotStartswith(bytearray(b'ababahalamaha'), b'amaha') - self.assertNotStartswith(bytearray(b'ababahalamaha'), (b'x', b'amaha', b'y')) - self.assertNotStartswith(b'ababahalamaha', bytearray(b'amaha')) - self.assertNotStartswith(b'ababahalamaha', + self.assertNotStartsWith('ababahalamaha', 'amaha') + self.assertNotStartsWith('ababahalamaha', ('x', 'amaha', 'y')) + self.assertNotStartsWith(UserString('ababahalamaha'), 'amaha') + self.assertNotStartsWith(UserString('ababahalamaha'), ('x', 'amaha', 'y')) + self.assertNotStartsWith(bytearray(b'ababahalamaha'), b'amaha') + self.assertNotStartsWith(bytearray(b'ababahalamaha'), (b'x', b'amaha', b'y')) + self.assertNotStartsWith(b'ababahalamaha', bytearray(b'amaha')) + self.assertNotStartsWith(b'ababahalamaha', (bytearray(b'x'), bytearray(b'amaha'), bytearray(b'y'))) with self.assertRaises(self.failureException) as cm: - self.assertNotStartswith('ababahalamaha', 'ababa') + self.assertNotStartsWith('ababahalamaha', 'ababa') self.assertEqual(str(cm.exception), "'ababahalamaha' starts with 'ababa'") with self.assertRaises(self.failureException) as cm: - self.assertNotStartswith('ababahalamaha', ('x', 'ababa', 'y')) + self.assertNotStartsWith('ababahalamaha', ('x', 'ababa', 'y')) self.assertEqual(str(cm.exception), "'ababahalamaha' starts with 'ababa'") with self.assertRaises(self.failureException) as cm: - self.assertNotStartswith(b'ababahalamaha', 'ababa') + self.assertNotStartsWith(b'ababahalamaha', 'ababa') self.assertEqual(str(cm.exception), 'Expected str, not bytes') with self.assertRaises(self.failureException) as cm: - self.assertNotStartswith(b'ababahalamaha', ('amaha', 'ababa')) + self.assertNotStartsWith(b'ababahalamaha', ('amaha', 'ababa')) self.assertEqual(str(cm.exception), 'Expected str, not bytes') with self.assertRaises(self.failureException) as cm: - self.assertNotStartswith([], 'ababa') + self.assertNotStartsWith([], 'ababa') self.assertEqual(str(cm.exception), 'Expected str, not list') with self.assertRaises(self.failureException) as cm: - self.assertNotStartswith('ababahalamaha', b'ababa') + self.assertNotStartsWith('ababahalamaha', b'ababa') self.assertEqual(str(cm.exception), 'Expected bytes, not str') with self.assertRaises(self.failureException) as cm: - self.assertNotStartswith('ababahalamaha', (b'amaha', b'ababa')) + self.assertNotStartsWith('ababahalamaha', (b'amaha', b'ababa')) self.assertEqual(str(cm.exception), 'Expected bytes, not str') with self.assertRaises(TypeError): - self.assertNotStartswith('ababahalamaha', ord('a')) + self.assertNotStartsWith('ababahalamaha', ord('a')) with self.assertRaises(self.failureException) as cm: - self.assertNotStartswith('ababahalamaha', 'ababa', 'abracadabra') + self.assertNotStartsWith('ababahalamaha', 'ababa', 'abracadabra') self.assertIn('ababahalamaha', str(cm.exception)) with self.assertRaises(self.failureException) as cm: - self.assertNotStartswith('ababahalamaha', 'ababa', msg='abracadabra') + self.assertNotStartsWith('ababahalamaha', 'ababa', msg='abracadabra') self.assertIn('ababahalamaha', str(cm.exception)) def testAssertEndswith(self): - self.assertEndswith('ababahalamaha', 'amaha') - self.assertEndswith('ababahalamaha', ('x', 'amaha', 'y')) - self.assertEndswith(UserString('ababahalamaha'), 'amaha') - self.assertEndswith(UserString('ababahalamaha'), ('x', 'amaha', 'y')) - self.assertEndswith(bytearray(b'ababahalamaha'), b'amaha') - self.assertEndswith(bytearray(b'ababahalamaha'), (b'x', b'amaha', b'y')) - self.assertEndswith(b'ababahalamaha', bytearray(b'amaha')) - self.assertEndswith(b'ababahalamaha', + self.assertEndsWith('ababahalamaha', 'amaha') + self.assertEndsWith('ababahalamaha', ('x', 'amaha', 'y')) + self.assertEndsWith(UserString('ababahalamaha'), 'amaha') + self.assertEndsWith(UserString('ababahalamaha'), ('x', 'amaha', 'y')) + self.assertEndsWith(bytearray(b'ababahalamaha'), b'amaha') + self.assertEndsWith(bytearray(b'ababahalamaha'), (b'x', b'amaha', b'y')) + self.assertEndsWith(b'ababahalamaha', bytearray(b'amaha')) + self.assertEndsWith(b'ababahalamaha', (bytearray(b'x'), bytearray(b'amaha'), bytearray(b'y'))) with self.assertRaises(self.failureException) as cm: - self.assertEndswith('ababahalamaha', 'ababa') + self.assertEndsWith('ababahalamaha', 'ababa') self.assertEqual(str(cm.exception), "'ababahalamaha' doesn't end with 'ababa'") with self.assertRaises(self.failureException) as cm: - self.assertEndswith('ababahalamaha', ('x', 'y')) + self.assertEndsWith('ababahalamaha', ('x', 'y')) self.assertEqual(str(cm.exception), "'ababahalamaha' doesn't end with any of ('x', 'y')") with self.assertRaises(self.failureException) as cm: - self.assertEndswith(b'ababahalamaha', 'amaha') + self.assertEndsWith(b'ababahalamaha', 'amaha') self.assertEqual(str(cm.exception), 'Expected str, not bytes') with self.assertRaises(self.failureException) as cm: - self.assertEndswith(b'ababahalamaha', ('ababa', 'amaha')) + self.assertEndsWith(b'ababahalamaha', ('ababa', 'amaha')) self.assertEqual(str(cm.exception), 'Expected str, not bytes') with self.assertRaises(self.failureException) as cm: - self.assertEndswith([], 'amaha') + self.assertEndsWith([], 'amaha') self.assertEqual(str(cm.exception), 'Expected str, not list') with self.assertRaises(self.failureException) as cm: - self.assertEndswith('ababahalamaha', b'amaha') + self.assertEndsWith('ababahalamaha', b'amaha') self.assertEqual(str(cm.exception), 'Expected bytes, not str') with self.assertRaises(self.failureException) as cm: - self.assertEndswith('ababahalamaha', (b'ababa', b'amaha')) + self.assertEndsWith('ababahalamaha', (b'ababa', b'amaha')) self.assertEqual(str(cm.exception), 'Expected bytes, not str') with self.assertRaises(TypeError): - self.assertEndswith('ababahalamaha', ord('a')) + self.assertEndsWith('ababahalamaha', ord('a')) with self.assertRaises(self.failureException) as cm: - self.assertEndswith('ababahalamaha', 'ababa', 'abracadabra') + self.assertEndsWith('ababahalamaha', 'ababa', 'abracadabra') self.assertIn('ababahalamaha', str(cm.exception)) with self.assertRaises(self.failureException) as cm: - self.assertEndswith('ababahalamaha', 'ababa', msg='abracadabra') + self.assertEndsWith('ababahalamaha', 'ababa', msg='abracadabra') self.assertIn('ababahalamaha', str(cm.exception)) def testAssertNotEndswith(self): - self.assertNotEndswith('ababahalamaha', 'ababa') - self.assertNotEndswith('ababahalamaha', ('x', 'ababa', 'y')) - self.assertNotEndswith(UserString('ababahalamaha'), 'ababa') - self.assertNotEndswith(UserString('ababahalamaha'), ('x', 'ababa', 'y')) - self.assertNotEndswith(bytearray(b'ababahalamaha'), b'ababa') - self.assertNotEndswith(bytearray(b'ababahalamaha'), (b'x', b'ababa', b'y')) - self.assertNotEndswith(b'ababahalamaha', bytearray(b'ababa')) - self.assertNotEndswith(b'ababahalamaha', + self.assertNotEndsWith('ababahalamaha', 'ababa') + self.assertNotEndsWith('ababahalamaha', ('x', 'ababa', 'y')) + self.assertNotEndsWith(UserString('ababahalamaha'), 'ababa') + self.assertNotEndsWith(UserString('ababahalamaha'), ('x', 'ababa', 'y')) + self.assertNotEndsWith(bytearray(b'ababahalamaha'), b'ababa') + self.assertNotEndsWith(bytearray(b'ababahalamaha'), (b'x', b'ababa', b'y')) + self.assertNotEndsWith(b'ababahalamaha', bytearray(b'ababa')) + self.assertNotEndsWith(b'ababahalamaha', (bytearray(b'x'), bytearray(b'ababa'), bytearray(b'y'))) with self.assertRaises(self.failureException) as cm: - self.assertNotEndswith('ababahalamaha', 'amaha') + self.assertNotEndsWith('ababahalamaha', 'amaha') self.assertEqual(str(cm.exception), "'ababahalamaha' ends with 'amaha'") with self.assertRaises(self.failureException) as cm: - self.assertNotEndswith('ababahalamaha', ('x', 'amaha', 'y')) + self.assertNotEndsWith('ababahalamaha', ('x', 'amaha', 'y')) self.assertEqual(str(cm.exception), "'ababahalamaha' ends with 'amaha'") with self.assertRaises(self.failureException) as cm: - self.assertNotEndswith(b'ababahalamaha', 'amaha') + self.assertNotEndsWith(b'ababahalamaha', 'amaha') self.assertEqual(str(cm.exception), 'Expected str, not bytes') with self.assertRaises(self.failureException) as cm: - self.assertNotEndswith(b'ababahalamaha', ('ababa', 'amaha')) + self.assertNotEndsWith(b'ababahalamaha', ('ababa', 'amaha')) self.assertEqual(str(cm.exception), 'Expected str, not bytes') with self.assertRaises(self.failureException) as cm: - self.assertNotEndswith([], 'amaha') + self.assertNotEndsWith([], 'amaha') self.assertEqual(str(cm.exception), 'Expected str, not list') with self.assertRaises(self.failureException) as cm: - self.assertNotEndswith('ababahalamaha', b'amaha') + self.assertNotEndsWith('ababahalamaha', b'amaha') self.assertEqual(str(cm.exception), 'Expected bytes, not str') with self.assertRaises(self.failureException) as cm: - self.assertNotEndswith('ababahalamaha', (b'ababa', b'amaha')) + self.assertNotEndsWith('ababahalamaha', (b'ababa', b'amaha')) self.assertEqual(str(cm.exception), 'Expected bytes, not str') with self.assertRaises(TypeError): - self.assertNotEndswith('ababahalamaha', ord('a')) + self.assertNotEndsWith('ababahalamaha', ord('a')) with self.assertRaises(self.failureException) as cm: - self.assertNotEndswith('ababahalamaha', 'amaha', 'abracadabra') + self.assertNotEndsWith('ababahalamaha', 'amaha', 'abracadabra') self.assertIn('ababahalamaha', str(cm.exception)) with self.assertRaises(self.failureException) as cm: - self.assertNotEndswith('ababahalamaha', 'amaha', msg='abracadabra') + self.assertNotEndsWith('ababahalamaha', 'amaha', msg='abracadabra') self.assertIn('ababahalamaha', str(cm.exception)) def testDeprecatedFailMethods(self): diff --git a/Lib/test/test_unittest/test_loader.py b/Lib/test/test_unittest/test_loader.py index 3b0b72fdd275ac..cdff6d1a20c8df 100644 --- a/Lib/test/test_unittest/test_loader.py +++ b/Lib/test/test_unittest/test_loader.py @@ -76,7 +76,7 @@ def runTest(self): loader = unittest.TestLoader() # This has to be false for the test to succeed - self.assertNotStartswith('runTest', loader.testMethodPrefix) + self.assertNotStartsWith('runTest', loader.testMethodPrefix) suite = loader.loadTestsFromTestCase(Foo) self.assertIsInstance(suite, loader.suiteClass) diff --git a/Lib/test/test_unittest/test_program.py b/Lib/test/test_unittest/test_program.py index 93463ccbfb18b2..58d0cef9708c95 100644 --- a/Lib/test/test_unittest/test_program.py +++ b/Lib/test/test_unittest/test_program.py @@ -135,7 +135,7 @@ def test_NonExit(self): self.assertIn('\nUNEXPECTED SUCCESS: testUnexpectedSuccess ', out) expected = ('\n\nFAILED (failures=1, errors=1, skipped=1, ' 'expected failures=1, unexpected successes=1)\n') - self.assertEndswith(out, expected) + self.assertEndsWith(out, expected) @force_not_colorized def test_Exit(self): @@ -153,7 +153,7 @@ def test_Exit(self): self.assertIn('\nUNEXPECTED SUCCESS: testUnexpectedSuccess ', out) expected = ('\n\nFAILED (failures=1, errors=1, skipped=1, ' 'expected failures=1, unexpected successes=1)\n') - self.assertEndswith(out, expected) + self.assertEndsWith(out, expected) @force_not_colorized def test_ExitAsDefault(self): @@ -169,7 +169,7 @@ def test_ExitAsDefault(self): self.assertIn('\nUNEXPECTED SUCCESS: testUnexpectedSuccess ', out) expected = ('\n\nFAILED (failures=1, errors=1, skipped=1, ' 'expected failures=1, unexpected successes=1)\n') - self.assertEndswith(out, expected) + self.assertEndsWith(out, expected) @force_not_colorized def test_ExitSkippedSuite(self): @@ -182,7 +182,7 @@ def test_ExitSkippedSuite(self): self.assertEqual(cm.exception.code, 0) out = stream.getvalue() expected = '\n\nOK (skipped=1)\n' - self.assertEndswith(out, expected) + self.assertEndsWith(out, expected) @force_not_colorized def test_ExitEmptySuite(self): diff --git a/Lib/test/test_unittest/test_result.py b/Lib/test/test_unittest/test_result.py index 904349ae710fa9..460c09f20e46e2 100644 --- a/Lib/test/test_unittest/test_result.py +++ b/Lib/test/test_unittest/test_result.py @@ -460,7 +460,7 @@ def test(result): self.assertTrue(result.failfast) result = runner.run(test) stream.flush() - self.assertEndswith(stream.getvalue(), '\n\nOK\n') + self.assertEndsWith(stream.getvalue(), '\n\nOK\n') class Test_TextTestResult(unittest.TestCase): diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py index 19115b0d08f87b..6e23097deaf221 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -513,7 +513,7 @@ def test_special_chars_bash(self): out, err = check_output([bash, test_script]) lines = out.splitlines() self.assertTrue(env_name.encode() in lines[0]) - self.assertEndswith(lines[1], env_name.encode()) + self.assertEndsWith(lines[1], env_name.encode()) # gh-124651: test quoted strings @unittest.skipIf(os.name == 'nt', 'contains invalid characters on Windows') @@ -539,7 +539,7 @@ def test_special_chars_csh(self): out, err = check_output([csh, test_script]) lines = out.splitlines() self.assertTrue(env_name.encode() in lines[0]) - self.assertEndswith(lines[1], env_name.encode()) + self.assertEndsWith(lines[1], env_name.encode()) # gh-124651: test quoted strings on Windows @unittest.skipUnless(os.name == 'nt', 'only relevant on Windows') @@ -563,7 +563,7 @@ def test_special_chars_windows(self): out, err = check_output([test_batch]) lines = out.splitlines() self.assertTrue(env_name.encode() in lines[0]) - self.assertEndswith(lines[1], env_name.encode()) + self.assertEndsWith(lines[1], env_name.encode()) @unittest.skipUnless(os.name == 'nt', 'only relevant on Windows') def test_unicode_in_batch_file(self): diff --git a/Lib/unittest/case.py b/Lib/unittest/case.py index 3df95d47429d40..dfc7830b920b14 100644 --- a/Lib/unittest/case.py +++ b/Lib/unittest/case.py @@ -1466,7 +1466,7 @@ def _tail_type_check(self, s, tails, msg): self.fail(self._formatMessage(msg, 'Expected bytes, not %s' % (type(s).__name__,))) - def assertStartswith(self, s, prefix, msg=None): + def assertStartsWith(self, s, prefix, msg=None): try: r = s.startswith(prefix) except (AttributeError, TypeError): @@ -1482,7 +1482,7 @@ def assertStartswith(self, s, prefix, msg=None): self.fail(self._formatMessage(msg, "%s doesn't start with %s" % (a, b))) - def assertNotStartswith(self, s, prefix, msg=None): + def assertNotStartsWith(self, s, prefix, msg=None): try: r = s.startswith(prefix) except (AttributeError, TypeError): @@ -1499,7 +1499,7 @@ def assertNotStartswith(self, s, prefix, msg=None): self.fail(self._formatMessage(msg, "%s starts with %s" % (a, b))) - def assertEndswith(self, s, suffix, msg=None): + def assertEndsWith(self, s, suffix, msg=None): try: r = s.endswith(suffix) except (AttributeError, TypeError): @@ -1515,7 +1515,7 @@ def assertEndswith(self, s, suffix, msg=None): self.fail(self._formatMessage(msg, "%s doesn't end with %s" % (a, b))) - def assertNotEndswith(self, s, suffix, msg=None): + def assertNotEndsWith(self, s, suffix, msg=None): try: r = s.endswith(suffix) except (AttributeError, TypeError): diff --git a/Misc/NEWS.d/next/Library/2025-01-10-15-06-45.gh-issue-71339.EKnpzw.rst b/Misc/NEWS.d/next/Library/2025-01-10-15-06-45.gh-issue-71339.EKnpzw.rst index 1d10e4a4dfe3bc..5f33a30bd5eae1 100644 --- a/Misc/NEWS.d/next/Library/2025-01-10-15-06-45.gh-issue-71339.EKnpzw.rst +++ b/Misc/NEWS.d/next/Library/2025-01-10-15-06-45.gh-issue-71339.EKnpzw.rst @@ -3,7 +3,7 @@ Add new assertion methods for :mod:`unittest`: :meth:`~unittest.TestCase.assertNotHasAttr`, :meth:`~unittest.TestCase.assertIsSubclass`, :meth:`~unittest.TestCase.assertNotIsSubclass` -:meth:`~unittest.TestCase.assertStartswith`, -:meth:`~unittest.TestCase.assertNotStartswith`, -:meth:`~unittest.TestCase.assertEndswith` and -:meth:`~unittest.TestCase.assertNotEndswith`. +:meth:`~unittest.TestCase.assertStartsWith`, +:meth:`~unittest.TestCase.assertNotStartsWith`, +:meth:`~unittest.TestCase.assertEndsWith` and +:meth:`~unittest.TestCase.assertNotEndsWith`. From 85509f624f58a33e25f19ab4d809e382ec71efb9 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 12 Jan 2025 01:04:38 +0200 Subject: [PATCH 4/7] Apply suggestions from code review Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- Doc/library/unittest.rst | 4 ++-- Doc/whatsnew/3.14.rst | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst index d885f8b576de0f..42a3100122b283 100644 --- a/Doc/library/unittest.rst +++ b/Doc/library/unittest.rst @@ -1313,7 +1313,7 @@ Test cases .. method:: assertStartsWith(s, prefix, msg=None) .. method:: assertNotStartsWith(s, prefix, msg=None) - Test that the unicode or byte string *s* starts (or does not start) + Test that the Unicode or byte string *s* starts (or does not start) with a *prefix*. *prefix* can also be a tuple of strings to try. @@ -1322,7 +1322,7 @@ Test cases .. method:: assertEndsWith(s, suffix, msg=None) .. method:: assertNotEndsWith(s, suffix, msg=None) - Test that the unicode or byte string *s* ends (or does not end) + Test that the Unicode or byte string *s* ends (or does not end) with a *suffix*. *suffix* can also be a tuple of strings to try. diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 29f520ed05c030..3c82cdb4f68a73 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -682,7 +682,7 @@ unittest - :meth:`~unittest.TestCase.assertStartsWith`, :meth:`~unittest.TestCase.assertNotStartsWith`, :meth:`~unittest.TestCase.assertEndsWith` and - :meth:`~unittest.TestCase.assertNotEndsWith` check whether the unicode + :meth:`~unittest.TestCase.assertNotEndsWith` check whether the Unicode or byte string starts or ends with particular string(s). (Contributed by Serhiy Storchaka in :gh:`71339`.) From bfe98aa3583f91cd87b08578714be11b8437df81 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 13 Jan 2025 12:29:39 +0200 Subject: [PATCH 5/7] Remove variable r. --- Lib/unittest/case.py | 116 +++++++++++++++++++++---------------------- 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/Lib/unittest/case.py b/Lib/unittest/case.py index dfc7830b920b14..7c5673d88f7da2 100644 --- a/Lib/unittest/case.py +++ b/Lib/unittest/case.py @@ -1340,35 +1340,35 @@ def assertNotIsInstance(self, obj, cls, msg=None): def assertIsSubclass(self, cls, superclass, msg=None): try: - r = issubclass(cls, superclass) + if issubclass(cls, superclass): + return except TypeError: if not isinstance(cls, type): self.fail(self._formatMessage(msg, '%r is not a class' % (cls,))) raise - if not r: - if isinstance(superclass, tuple): - standardMsg = '%r is not a subclass of any of %r' % (cls, superclass) - else: - standardMsg = '%r is not a subclass of %r' % (cls, superclass) - self.fail(self._formatMessage(msg, standardMsg)) + if isinstance(superclass, tuple): + standardMsg = '%r is not a subclass of any of %r' % (cls, superclass) + else: + standardMsg = '%r is not a subclass of %r' % (cls, superclass) + self.fail(self._formatMessage(msg, standardMsg)) def assertNotIsSubclass(self, cls, superclass, msg=None): try: - r = issubclass(cls, superclass) + if not issubclass(cls, superclass): + return except TypeError: if not isinstance(cls, type): self.fail(self._formatMessage(msg, '%r is not a class' % (cls,))) raise - if r: - if isinstance(superclass, tuple): - for x in superclass: - if issubclass(cls, x): - superclass = x - break - self.fail(self._formatMessage(msg, - '%r is a subclass of %r' % (cls, superclass))) + if isinstance(superclass, tuple): + for x in superclass: + if issubclass(cls, x): + superclass = x + break + self.fail(self._formatMessage(msg, + '%r is a subclass of %r' % (cls, superclass))) def assertHasAttr(self, obj, name, msg=None): if not hasattr(obj, name): @@ -1468,69 +1468,69 @@ def _tail_type_check(self, s, tails, msg): def assertStartsWith(self, s, prefix, msg=None): try: - r = s.startswith(prefix) + if s.startswith(prefix): + return except (AttributeError, TypeError): self._tail_type_check(s, prefix, msg) raise - if not r: - a = safe_repr(s, short=True) - b = safe_repr(prefix) - if isinstance(prefix, tuple): - self.fail(self._formatMessage(msg, - "%s doesn't start with any of %s" % (a, b))) - else: - self.fail(self._formatMessage(msg, - "%s doesn't start with %s" % (a, b))) + a = safe_repr(s, short=True) + b = safe_repr(prefix) + if isinstance(prefix, tuple): + self.fail(self._formatMessage(msg, + "%s doesn't start with any of %s" % (a, b))) + else: + self.fail(self._formatMessage(msg, + "%s doesn't start with %s" % (a, b))) def assertNotStartsWith(self, s, prefix, msg=None): try: - r = s.startswith(prefix) + if not s.startswith(prefix): + return except (AttributeError, TypeError): self._tail_type_check(s, prefix, msg) raise - if r: - if isinstance(prefix, tuple): - for x in prefix: - if s.startswith(x): - prefix = x - break - a = safe_repr(s, short=True) - b = safe_repr(prefix) - self.fail(self._formatMessage(msg, - "%s starts with %s" % (a, b))) + if isinstance(prefix, tuple): + for x in prefix: + if s.startswith(x): + prefix = x + break + a = safe_repr(s, short=True) + b = safe_repr(prefix) + self.fail(self._formatMessage(msg, + "%s starts with %s" % (a, b))) def assertEndsWith(self, s, suffix, msg=None): try: - r = s.endswith(suffix) + if s.endswith(suffix): + return except (AttributeError, TypeError): self._tail_type_check(s, suffix, msg) raise - if not r: - a = safe_repr(s, short=True) - b = safe_repr(suffix) - if isinstance(suffix, tuple): - self.fail(self._formatMessage(msg, - "%s doesn't end with any of %s" % (a, b))) - else: - self.fail(self._formatMessage(msg, - "%s doesn't end with %s" % (a, b))) + a = safe_repr(s, short=True) + b = safe_repr(suffix) + if isinstance(suffix, tuple): + self.fail(self._formatMessage(msg, + "%s doesn't end with any of %s" % (a, b))) + else: + self.fail(self._formatMessage(msg, + "%s doesn't end with %s" % (a, b))) def assertNotEndsWith(self, s, suffix, msg=None): try: - r = s.endswith(suffix) + if not s.endswith(suffix): + return except (AttributeError, TypeError): self._tail_type_check(s, suffix, msg) raise - if r: - if isinstance(suffix, tuple): - for x in suffix: - if s.endswith(x): - suffix = x - break - a = safe_repr(s, short=True) - b = safe_repr(suffix) - self.fail(self._formatMessage(msg, - "%s ends with %s" % (a, b))) + if isinstance(suffix, tuple): + for x in suffix: + if s.endswith(x): + suffix = x + break + a = safe_repr(s, short=True) + b = safe_repr(suffix) + self.fail(self._formatMessage(msg, + "%s ends with %s" % (a, b))) class FunctionTestCase(TestCase): From 89b1577a10b7c9bd3690fb0e9a4e0414bd4c2e13 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 13 Jan 2025 12:56:06 +0200 Subject: [PATCH 6/7] Use f-strings. --- Lib/unittest/case.py | 62 ++++++++++++++++++-------------------------- 1 file changed, 25 insertions(+), 37 deletions(-) diff --git a/Lib/unittest/case.py b/Lib/unittest/case.py index 7c5673d88f7da2..e9ef551d0b3ded 100644 --- a/Lib/unittest/case.py +++ b/Lib/unittest/case.py @@ -1322,9 +1322,9 @@ def assertIsInstance(self, obj, cls, msg=None): default message.""" if not isinstance(obj, cls): if isinstance(cls, tuple): - standardMsg = '%s is not an instance of any of %r' % (safe_repr(obj), cls) + standardMsg = f'{safe_repr(obj)} is not an instance of any of {cls!r}' else: - standardMsg = '%s is not an instance of %r' % (safe_repr(obj), cls) + standardMsg = f'{safe_repr(obj)} is not an instance of {cls!r}' self.fail(self._formatMessage(msg, standardMsg)) def assertNotIsInstance(self, obj, cls, msg=None): @@ -1335,7 +1335,7 @@ def assertNotIsInstance(self, obj, cls, msg=None): if isinstance(obj, x): cls = x break - standardMsg = '%s is an instance of %r' % (safe_repr(obj), cls) + standardMsg = f'{safe_repr(obj)} is an instance of {cls!r}' self.fail(self._formatMessage(msg, standardMsg)) def assertIsSubclass(self, cls, superclass, msg=None): @@ -1344,13 +1344,12 @@ def assertIsSubclass(self, cls, superclass, msg=None): return except TypeError: if not isinstance(cls, type): - self.fail(self._formatMessage(msg, - '%r is not a class' % (cls,))) + self.fail(self._formatMessage(msg, f'{cls!r} is not a class')) raise if isinstance(superclass, tuple): - standardMsg = '%r is not a subclass of any of %r' % (cls, superclass) + standardMsg = f'{cls!r} is not a subclass of any of {superclass!r}' else: - standardMsg = '%r is not a subclass of %r' % (cls, superclass) + standardMsg = f'{cls!r} is not a subclass of {superclass!r}' self.fail(self._formatMessage(msg, standardMsg)) def assertNotIsSubclass(self, cls, superclass, msg=None): @@ -1359,38 +1358,31 @@ def assertNotIsSubclass(self, cls, superclass, msg=None): return except TypeError: if not isinstance(cls, type): - self.fail(self._formatMessage(msg, - '%r is not a class' % (cls,))) + self.fail(self._formatMessage(msg, f'{cls!r} is not a class')) raise if isinstance(superclass, tuple): for x in superclass: if issubclass(cls, x): superclass = x break - self.fail(self._formatMessage(msg, - '%r is a subclass of %r' % (cls, superclass))) + standardMsg = f'{cls!r} is a subclass of {superclass!r}' + self.fail(self._formatMessage(msg, standardMsg)) def assertHasAttr(self, obj, name, msg=None): if not hasattr(obj, name): if isinstance(obj, types.ModuleType): - self.fail(self._formatMessage(msg, - 'module %r has no attribute %r' % - (obj.__name__, name))) + standardMsg = f'module {obj.__name__!r} has no attribute {name!r}' else: - self.fail(self._formatMessage(msg, - '%s instance has no attribute %r' % - (type(obj).__name__, name))) + standardMsg = f'{type(obj).__name__} instance has no attribute {name!r}' + self.fail(self._formatMessage(msg, standardMsg)) def assertNotHasAttr(self, obj, name, msg=None): if hasattr(obj, name): if isinstance(obj, types.ModuleType): - self.fail(self._formatMessage(msg, - 'module %r has unexpected attribute %r' % - (obj.__name__, name))) + standardMsg = f'module {obj.__name__!r} has unexpected attribute {name!r}' else: - self.fail(self._formatMessage(msg, - '%s instance has unexpected attribute %r' % - (type(obj).__name__, name))) + standardMsg = f'{type(obj).__name__} instance has unexpected attribute {name!r}' + self.fail(self._formatMessage(msg, standardMsg)) def assertRaisesRegex(self, expected_exception, expected_regex, *args, **kwargs): @@ -1460,11 +1452,11 @@ def _tail_type_check(self, s, tails, msg): if isinstance(tail, str): if not isinstance(s, str): self.fail(self._formatMessage(msg, - 'Expected str, not %s' % (type(s).__name__,))) + f'Expected str, not {type(s).__name__}')) elif isinstance(tail, (bytes, bytearray)): if not isinstance(s, (bytes, bytearray)): self.fail(self._formatMessage(msg, - 'Expected bytes, not %s' % (type(s).__name__,))) + f'Expected bytes, not {type(s).__name__}')) def assertStartsWith(self, s, prefix, msg=None): try: @@ -1476,11 +1468,10 @@ def assertStartsWith(self, s, prefix, msg=None): a = safe_repr(s, short=True) b = safe_repr(prefix) if isinstance(prefix, tuple): - self.fail(self._formatMessage(msg, - "%s doesn't start with any of %s" % (a, b))) + standardMsg = f"{a} doesn't start with any of {b}" else: - self.fail(self._formatMessage(msg, - "%s doesn't start with %s" % (a, b))) + standardMsg = f"{a} doesn't start with {b}" + self.fail(self._formatMessage(msg, standardMsg)) def assertNotStartsWith(self, s, prefix, msg=None): try: @@ -1496,8 +1487,7 @@ def assertNotStartsWith(self, s, prefix, msg=None): break a = safe_repr(s, short=True) b = safe_repr(prefix) - self.fail(self._formatMessage(msg, - "%s starts with %s" % (a, b))) + self.fail(self._formatMessage(msg, f"{a} starts with {b}")) def assertEndsWith(self, s, suffix, msg=None): try: @@ -1509,11 +1499,10 @@ def assertEndsWith(self, s, suffix, msg=None): a = safe_repr(s, short=True) b = safe_repr(suffix) if isinstance(suffix, tuple): - self.fail(self._formatMessage(msg, - "%s doesn't end with any of %s" % (a, b))) + standardMsg = f"{a} doesn't end with any of {b}" else: - self.fail(self._formatMessage(msg, - "%s doesn't end with %s" % (a, b))) + standardMsg = f"{a} doesn't end with {b}" + self.fail(self._formatMessage(msg, standardMsg)) def assertNotEndsWith(self, s, suffix, msg=None): try: @@ -1529,8 +1518,7 @@ def assertNotEndsWith(self, s, suffix, msg=None): break a = safe_repr(s, short=True) b = safe_repr(suffix) - self.fail(self._formatMessage(msg, - "%s ends with %s" % (a, b))) + self.fail(self._formatMessage(msg, f"{a} ends with {b}")) class FunctionTestCase(TestCase): From c703dc9a4d04782992af62a3615ac8687e6103f5 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 13 Jan 2025 17:39:34 +0200 Subject: [PATCH 7/7] Double empty lines. --- Doc/library/unittest.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst index 42a3100122b283..0eead59a337ef9 100644 --- a/Doc/library/unittest.rst +++ b/Doc/library/unittest.rst @@ -966,6 +966,7 @@ Test cases .. versionadded:: 3.2 + .. method:: assertIsSubclass(cls, superclass, msg=None) assertNotIsSubclass(cls, superclass, msg=None) @@ -1310,6 +1311,7 @@ Test cases .. versionadded:: 3.2 + .. method:: assertStartsWith(s, prefix, msg=None) .. method:: assertNotStartsWith(s, prefix, msg=None) @@ -1319,6 +1321,7 @@ Test cases .. versionadded:: next + .. method:: assertEndsWith(s, suffix, msg=None) .. method:: assertNotEndsWith(s, suffix, msg=None) @@ -1328,6 +1331,7 @@ Test cases .. versionadded:: next + .. method:: assertHasAttr(obj, name, msg=None) .. method:: assertNotHasAttr(obj, name, msg=None)