Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit c6a566e

Browse filesBrowse files
[3.13] gh-71339: Add additional assertion methods in test.support (GH-128707) (GH-128815)
Add a mix-in class ExtraAssertions containing the following methods: * assertHasAttr() and assertNotHasAttr() * assertIsSubclass() and assertNotIsSubclass() * assertStartsWith() and assertNotStartsWith() * assertEndsWith() and assertNotEndsWith() (cherry picked from commit 06cad77)
1 parent 59b919b commit c6a566e
Copy full SHA for c6a566e

File tree

Expand file treeCollapse file tree

7 files changed

+70
-56
lines changed
Filter options
Expand file treeCollapse file tree

7 files changed

+70
-56
lines changed

‎Lib/test/support/testcase.py

Copy file name to clipboardExpand all lines: Lib/test/support/testcase.py
+57Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,63 @@
11
from math import copysign, isnan
22

33

4+
class ExtraAssertions:
5+
6+
def assertIsSubclass(self, cls, superclass, msg=None):
7+
if issubclass(cls, superclass):
8+
return
9+
standardMsg = f'{cls!r} is not a subclass of {superclass!r}'
10+
self.fail(self._formatMessage(msg, standardMsg))
11+
12+
def assertNotIsSubclass(self, cls, superclass, msg=None):
13+
if not issubclass(cls, superclass):
14+
return
15+
standardMsg = f'{cls!r} is a subclass of {superclass!r}'
16+
self.fail(self._formatMessage(msg, standardMsg))
17+
18+
def assertHasAttr(self, obj, name, msg=None):
19+
if not hasattr(obj, name):
20+
if isinstance(obj, types.ModuleType):
21+
standardMsg = f'module {obj.__name__!r} has no attribute {name!r}'
22+
elif isinstance(obj, type):
23+
standardMsg = f'type object {obj.__name__!r} has no attribute {name!r}'
24+
else:
25+
standardMsg = f'{type(obj).__name__!r} object has no attribute {name!r}'
26+
self.fail(self._formatMessage(msg, standardMsg))
27+
28+
def assertNotHasAttr(self, obj, name, msg=None):
29+
if hasattr(obj, name):
30+
if isinstance(obj, types.ModuleType):
31+
standardMsg = f'module {obj.__name__!r} has unexpected attribute {name!r}'
32+
elif isinstance(obj, type):
33+
standardMsg = f'type object {obj.__name__!r} has unexpected attribute {name!r}'
34+
else:
35+
standardMsg = f'{type(obj).__name__!r} object has unexpected attribute {name!r}'
36+
self.fail(self._formatMessage(msg, standardMsg))
37+
38+
def assertStartsWith(self, s, prefix, msg=None):
39+
if s.startswith(prefix):
40+
return
41+
standardMsg = f"{s!r} doesn't start with {prefix!r}"
42+
self.fail(self._formatMessage(msg, standardMsg))
43+
44+
def assertNotStartsWith(self, s, prefix, msg=None):
45+
if not s.startswith(prefix):
46+
return
47+
self.fail(self._formatMessage(msg, f"{s!r} starts with {prefix!r}"))
48+
49+
def assertEndsWith(self, s, suffix, msg=None):
50+
if s.endswith(suffix):
51+
return
52+
standardMsg = f"{s!r} doesn't end with {suffix!r}"
53+
self.fail(self._formatMessage(msg, standardMsg))
54+
55+
def assertNotEndsWith(self, s, suffix, msg=None):
56+
if not s.endswith(suffix):
57+
return
58+
self.fail(self._formatMessage(msg, f"{s!r} ends with {suffix!r}"))
59+
60+
461
class ExceptionIsLikeMixin:
562
def assertExceptionIsLike(self, exc, template):
663
"""

‎Lib/test/test_descr.py

Copy file name to clipboardExpand all lines: Lib/test/test_descr.py
+2-9Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from copy import deepcopy
1616
from contextlib import redirect_stdout
1717
from test import support
18+
from test.support.testcase import ExtraAssertions
1819

1920
try:
2021
import _testcapi
@@ -403,15 +404,7 @@ def test_wrap_lenfunc_bad_cast(self):
403404
self.assertEqual(range(sys.maxsize).__len__(), sys.maxsize)
404405

405406

406-
class ClassPropertiesAndMethods(unittest.TestCase):
407-
408-
def assertHasAttr(self, obj, name):
409-
self.assertTrue(hasattr(obj, name),
410-
'%r has no attribute %r' % (obj, name))
411-
412-
def assertNotHasAttr(self, obj, name):
413-
self.assertFalse(hasattr(obj, name),
414-
'%r has unexpected attribute %r' % (obj, name))
407+
class ClassPropertiesAndMethods(unittest.TestCase, ExtraAssertions):
415408

416409
def test_python_dicts(self):
417410
# Testing Python subclass of dict...

‎Lib/test/test_gdb/util.py

Copy file name to clipboardExpand all lines: Lib/test/test_gdb/util.py
+2-6Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import sysconfig
88
import unittest
99
from test import support
10+
from test.support.testcase import ExtraAssertions
1011

1112

1213
GDB_PROGRAM = shutil.which('gdb') or 'gdb'
@@ -152,7 +153,7 @@ def setup_module():
152153
print()
153154

154155

155-
class DebuggerTests(unittest.TestCase):
156+
class DebuggerTests(unittest.TestCase, ExtraAssertions):
156157

157158
"""Test that the debugger can debug Python."""
158159

@@ -280,11 +281,6 @@ def get_stack_trace(self, source=None, script=None,
280281

281282
return out
282283

283-
def assertEndsWith(self, actual, exp_end):
284-
'''Ensure that the given "actual" string ends with "exp_end"'''
285-
self.assertTrue(actual.endswith(exp_end),
286-
msg='%r did not end with %r' % (actual, exp_end))
287-
288284
def assertMultilineMatches(self, actual, pattern):
289285
m = re.match(pattern, actual, re.DOTALL)
290286
if not m:

‎Lib/test/test_importlib/resources/test_functional.py

Copy file name to clipboardExpand all lines: Lib/test/test_importlib/resources/test_functional.py
+2-7Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import importlib
44

55
from test.support import warnings_helper
6+
from test.support.testcase import ExtraAssertions
67

78
from importlib import resources
89

@@ -28,7 +29,7 @@ def anchor02(self):
2829
return importlib.import_module('data02')
2930

3031

31-
class FunctionalAPIBase(util.DiskSetup):
32+
class FunctionalAPIBase(util.DiskSetup, ExtraAssertions):
3233
def setUp(self):
3334
super().setUp()
3435
self.load_fixture('data02')
@@ -43,12 +44,6 @@ def _gen_resourcetxt_path_parts(self):
4344
with self.subTest(path_parts=path_parts):
4445
yield path_parts
4546

46-
def assertEndsWith(self, string, suffix):
47-
"""Assert that `string` ends with `suffix`.
48-
49-
Used to ignore an architecture-specific UTF-16 byte-order mark."""
50-
self.assertEqual(string[-len(suffix) :], suffix)
51-
5247
def test_read_text(self):
5348
self.assertEqual(
5449
resources.read_text(self.anchor01, 'utf-8.file'),

‎Lib/test/test_pyclbr.py

Copy file name to clipboardExpand all lines: Lib/test/test_pyclbr.py
+3-10Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from unittest import TestCase, main as unittest_main
1111
from test.test_importlib import util as test_importlib_util
1212
import warnings
13+
from test.support.testcase import ExtraAssertions
1314

1415

1516
StaticMethodType = type(staticmethod(lambda: None))
@@ -22,7 +23,7 @@
2223
# is imperfect (as designed), testModule is called with a set of
2324
# members to ignore.
2425

25-
class PyclbrTest(TestCase):
26+
class PyclbrTest(TestCase, ExtraAssertions):
2627

2728
def assertListEq(self, l1, l2, ignore):
2829
''' succeed iff {l1} - {ignore} == {l2} - {ignore} '''
@@ -31,14 +32,6 @@ def assertListEq(self, l1, l2, ignore):
3132
print("l1=%r\nl2=%r\nignore=%r" % (l1, l2, ignore), file=sys.stderr)
3233
self.fail("%r missing" % missing.pop())
3334

34-
def assertHasattr(self, obj, attr, ignore):
35-
''' succeed iff hasattr(obj,attr) or attr in ignore. '''
36-
if attr in ignore: return
37-
if not hasattr(obj, attr): print("???", attr)
38-
self.assertTrue(hasattr(obj, attr),
39-
'expected hasattr(%r, %r)' % (obj, attr))
40-
41-
4235
def assertHaskey(self, obj, key, ignore):
4336
''' succeed iff key in obj or key in ignore. '''
4437
if key in ignore: return
@@ -86,7 +79,7 @@ def ismethod(oclass, obj, name):
8679
for name, value in dict.items():
8780
if name in ignore:
8881
continue
89-
self.assertHasattr(module, name, ignore)
82+
self.assertHasAttr(module, name, ignore)
9083
py_item = getattr(module, name)
9184
if isinstance(value, pyclbr.Function):
9285
self.assertIsInstance(py_item, (FunctionType, BuiltinFunctionType))

‎Lib/test/test_typing.py

Copy file name to clipboardExpand all lines: Lib/test/test_typing.py
+2-19Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import types
4747

4848
from test.support import captured_stderr, cpython_only, infinite_recursion, requires_docstrings, import_helper
49+
from test.support.testcase import ExtraAssertions
4950
from test.typinganndata import ann_module695, mod_generics_cache, _typed_dict_helper
5051

5152

@@ -54,21 +55,7 @@
5455
CANNOT_SUBCLASS_INSTANCE = 'Cannot subclass an instance of %s'
5556

5657

57-
class BaseTestCase(TestCase):
58-
59-
def assertIsSubclass(self, cls, class_or_tuple, msg=None):
60-
if not issubclass(cls, class_or_tuple):
61-
message = '%r is not a subclass of %r' % (cls, class_or_tuple)
62-
if msg is not None:
63-
message += ' : %s' % msg
64-
raise self.failureException(message)
65-
66-
def assertNotIsSubclass(self, cls, class_or_tuple, msg=None):
67-
if issubclass(cls, class_or_tuple):
68-
message = '%r is a subclass of %r' % (cls, class_or_tuple)
69-
if msg is not None:
70-
message += ' : %s' % msg
71-
raise self.failureException(message)
58+
class BaseTestCase(TestCase, ExtraAssertions):
7259

7360
def clear_caches(self):
7461
for f in typing._cleanups:
@@ -1249,10 +1236,6 @@ class Gen[*Ts]: ...
12491236

12501237
class TypeVarTupleTests(BaseTestCase):
12511238

1252-
def assertEndsWith(self, string, tail):
1253-
if not string.endswith(tail):
1254-
self.fail(f"String {string!r} does not end with {tail!r}")
1255-
12561239
def test_name(self):
12571240
Ts = TypeVarTuple('Ts')
12581241
self.assertEqual(Ts.__name__, 'Ts')

‎Lib/test/test_venv.py

Copy file name to clipboardExpand all lines: Lib/test/test_venv.py
+2-5Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
requires_resource, copy_python_src_ignore)
2727
from test.support.os_helper import (can_symlink, EnvironmentVarGuard, rmtree,
2828
TESTFN, FakePath)
29+
from test.support.testcase import ExtraAssertions
2930
import unittest
3031
import venv
3132
from unittest.mock import patch, Mock
@@ -64,7 +65,7 @@ def check_output(cmd, encoding=None):
6465
)
6566
return out, err
6667

67-
class BaseTest(unittest.TestCase):
68+
class BaseTest(unittest.TestCase, ExtraAssertions):
6869
"""Base class for venv tests."""
6970
maxDiff = 80 * 50
7071

@@ -111,10 +112,6 @@ def get_text_file_contents(self, *args, encoding='utf-8'):
111112
result = f.read()
112113
return result
113114

114-
def assertEndsWith(self, string, tail):
115-
if not string.endswith(tail):
116-
self.fail(f"String {string!r} does not end with {tail!r}")
117-
118115
class BasicTest(BaseTest):
119116
"""Test venv module functionality."""
120117

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.