diff --git a/Lib/test/support/testcase.py b/Lib/test/support/testcase.py index fad1e4cb3499c07..0d21f2668b30e4c 100644 --- a/Lib/test/support/testcase.py +++ b/Lib/test/support/testcase.py @@ -1,6 +1,59 @@ from math import copysign, isnan +class ExtraAssertions: + + def assertIsSubclass(self, cls, superclass, msg=None): + if issubclass(cls, superclass): + return + standardMsg = f'{cls!r} is not a subclass of {superclass!r}' + self.fail(self._formatMessage(msg, standardMsg)) + + def assertNotIsSubclass(self, cls, superclass, msg=None): + if not issubclass(cls, superclass): + return + 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): + standardMsg = f'module {obj.__name__!r} has no attribute {name!r}' + else: + 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): + standardMsg = f'module {obj.__name__!r} has unexpected attribute {name!r}' + else: + standardMsg = f'{type(obj).__name__} instance has unexpected attribute {name!r}' + self.fail(self._formatMessage(msg, standardMsg)) + + def assertStartsWith(self, s, prefix, msg=None): + if s.startswith(prefix): + return + standardMsg = f"{s!r} doesn't start with {prefix!r}" + self.fail(self._formatMessage(msg, standardMsg)) + + def assertNotStartsWith(self, s, prefix, msg=None): + if not s.startswith(prefix): + return + self.fail(self._formatMessage(msg, f"{s!r} starts with {prefix!r}")) + + def assertEndsWith(self, s, suffix, msg=None): + if s.endswith(suffix): + return + standardMsg = f"{s!r} doesn't end with {suffix!r}" + self.fail(self._formatMessage(msg, standardMsg)) + + def assertNotEndsWith(self, s, suffix, msg=None): + if not s.endswith(suffix): + return + self.fail(self._formatMessage(msg, f"{s!r} ends with {suffix!r}")) + + class ExceptionIsLikeMixin: def assertExceptionIsLike(self, exc, template): """ diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 14bd87eb9c8d843..dd1fa321ecf1712 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -15,6 +15,7 @@ from copy import deepcopy from contextlib import redirect_stdout from test import support +from test.support.testcase import ExtraAssertions try: import _testcapi @@ -403,15 +404,7 @@ def test_wrap_lenfunc_bad_cast(self): self.assertEqual(range(sys.maxsize).__len__(), sys.maxsize) -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)) +class ClassPropertiesAndMethods(unittest.TestCase, ExtraAssertions): def test_python_dicts(self): # Testing Python subclass of dict... diff --git a/Lib/test/test_gdb/util.py b/Lib/test/test_gdb/util.py index 8fe9cfc543395e1..54c6b2de7cc99d8 100644 --- a/Lib/test/test_gdb/util.py +++ b/Lib/test/test_gdb/util.py @@ -7,6 +7,7 @@ import sysconfig import unittest from test import support +from test.support.testcase import ExtraAssertions GDB_PROGRAM = shutil.which('gdb') or 'gdb' @@ -152,7 +153,7 @@ def setup_module(): print() -class DebuggerTests(unittest.TestCase): +class DebuggerTests(unittest.TestCase, ExtraAssertions): """Test that the debugger can debug Python.""" @@ -280,11 +281,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 4317abf3162c529..3fc1ade35bef5ae 100644 --- a/Lib/test/test_importlib/resources/test_functional.py +++ b/Lib/test/test_importlib/resources/test_functional.py @@ -3,6 +3,7 @@ import importlib from test.support import warnings_helper +from test.support.testcase import ExtraAssertions from importlib import resources @@ -28,7 +29,7 @@ def anchor02(self): return importlib.import_module('data02') -class FunctionalAPIBase(util.DiskSetup): +class FunctionalAPIBase(util.DiskSetup, ExtraAssertions): def setUp(self): super().setUp() self.load_fixture('data02') @@ -43,12 +44,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'), diff --git a/Lib/test/test_pyclbr.py b/Lib/test/test_pyclbr.py index d409a2d4a312e63..a65705aaf53abc7 100644 --- a/Lib/test/test_pyclbr.py +++ b/Lib/test/test_pyclbr.py @@ -10,6 +10,7 @@ from unittest import TestCase, main as unittest_main from test.test_importlib import util as test_importlib_util import warnings +from test.support.testcase import ExtraAssertions StaticMethodType = type(staticmethod(lambda: None)) @@ -22,7 +23,7 @@ # is imperfect (as designed), testModule is called with a set of # members to ignore. -class PyclbrTest(TestCase): +class PyclbrTest(TestCase, ExtraAssertions): def assertListEq(self, l1, l2, ignore): ''' succeed iff {l1} - {ignore} == {l2} - {ignore} ''' @@ -31,14 +32,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 +79,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 140ceb747355705..89a32c7a1a0d14f 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -46,6 +46,7 @@ import types from test.support import captured_stderr, cpython_only, infinite_recursion, requires_docstrings, import_helper +from test.support.testcase import ExtraAssertions from test.typinganndata import ann_module695, mod_generics_cache, _typed_dict_helper @@ -54,21 +55,7 @@ CANNOT_SUBCLASS_INSTANCE = 'Cannot subclass an instance of %s' -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) +class BaseTestCase(TestCase, ExtraAssertions): def clear_caches(self): for f in typing._cleanups: @@ -1249,10 +1236,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') diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py index 0b09010c69d4eac..c39c83f9d0a5c3a 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -26,6 +26,7 @@ requires_resource, copy_python_src_ignore) from test.support.os_helper import (can_symlink, EnvironmentVarGuard, rmtree, TESTFN, FakePath) +from test.support.testcase import ExtraAssertions import unittest import venv from unittest.mock import patch, Mock @@ -64,7 +65,7 @@ def check_output(cmd, encoding=None): ) return out, err -class BaseTest(unittest.TestCase): +class BaseTest(unittest.TestCase, ExtraAssertions): """Base class for venv tests.""" maxDiff = 80 * 50 @@ -111,10 +112,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."""