Skip to content
43 changes: 39 additions & 4 deletions Lib/test/support/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -840,11 +840,27 @@ def python_is_optimized():
return final_opt not in ('', '-O0', '-Og')


_header = 'nP'
# From CPython 3.13.5
Py_GIL_DISABLED = bool(sysconfig.get_config_var('Py_GIL_DISABLED'))

# From CPython 3.13.5
def requires_gil_enabled(msg="needs the GIL enabled"):
"""Decorator for skipping tests on the free-threaded build."""
return unittest.skipIf(Py_GIL_DISABLED, msg)

# From CPython 3.13.5
def expected_failure_if_gil_disabled():
"""Expect test failure if the GIL is disabled."""
if Py_GIL_DISABLED:
return unittest.expectedFailure
return lambda test_case: test_case

# From CPython 3.13.5
if Py_GIL_DISABLED:
_header = 'PHBBInP'
else:
_header = 'nP'
_align = '0n'
if hasattr(sys, "getobjects"):
_header = '2P' + _header
_align = '0P'
_vheader = _header + 'n'

def calcobjsize(fmt):
Expand Down Expand Up @@ -2617,6 +2633,10 @@ def exceeds_recursion_limit():
'skipped on s390x')
HAVE_ASAN_FORK_BUG = check_sanitizer(address=True)

# From CPython 3.13.5
Py_TRACE_REFS = hasattr(sys, 'getobjects')


# From Cpython 3.13.5
@contextlib.contextmanager
def no_color():
Expand All @@ -2641,6 +2661,21 @@ def wrapper(*args, **kwargs):
return wrapper


# From Cpython 3.13.5
def force_not_colorized_test_class(cls):
"""Force the terminal not to be colorized for the entire test class."""
original_setUpClass = cls.setUpClass

@classmethod
@functools.wraps(cls.setUpClass)
def new_setUpClass(cls):
cls.enterClassContext(no_color())
original_setUpClass()

cls.setUpClass = new_setUpClass
return cls


# From python 3.12.8
class BrokenIter:
def __init__(self, init_raises=False, next_raises=False, iter_raises=False):
Expand Down
71 changes: 50 additions & 21 deletions Lib/test/test_exception_group.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import collections.abc
import types
import unittest
from test.support import C_RECURSION_LIMIT
from test.support import get_c_recursion_limit

class TestExceptionGroupTypeHierarchy(unittest.TestCase):
def test_exception_group_types(self):
Expand Down Expand Up @@ -300,17 +300,33 @@ def assertMatchesTemplate(self, exc, exc_type, template):
self.assertEqual(type(exc), type(template))
self.assertEqual(exc.args, template.args)

class Predicate:
def __init__(self, func):
self.func = func

def __call__(self, e):
return self.func(e)

def method(self, e):
return self.func(e)

class ExceptionGroupSubgroupTests(ExceptionGroupTestBase):
def setUp(self):
self.eg = create_simple_eg()
self.eg_template = [ValueError(1), TypeError(int), ValueError(2)]

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_basics_subgroup_split__bad_arg_type(self):
class C:
pass

bad_args = ["bad arg",
C,
OSError('instance not type'),
[OSError, TypeError],
(OSError, 42)]
(OSError, 42),
]
for arg in bad_args:
with self.assertRaises(TypeError):
self.eg.subgroup(arg)
Expand Down Expand Up @@ -342,10 +358,14 @@ def test_basics_subgroup_by_type__match(self):
self.assertMatchesTemplate(subeg, ExceptionGroup, template)

def test_basics_subgroup_by_predicate__passthrough(self):
self.assertIs(self.eg, self.eg.subgroup(lambda e: True))
f = lambda e: True
for callable in [f, Predicate(f), Predicate(f).method]:
self.assertIs(self.eg, self.eg.subgroup(callable))

def test_basics_subgroup_by_predicate__no_match(self):
self.assertIsNone(self.eg.subgroup(lambda e: False))
f = lambda e: False
for callable in [f, Predicate(f), Predicate(f).method]:
self.assertIsNone(self.eg.subgroup(callable))

def test_basics_subgroup_by_predicate__match(self):
eg = self.eg
Expand All @@ -356,9 +376,12 @@ def test_basics_subgroup_by_predicate__match(self):
((ValueError, TypeError), self.eg_template)]

for match_type, template in testcases:
subeg = eg.subgroup(lambda e: isinstance(e, match_type))
self.assertEqual(subeg.message, eg.message)
self.assertMatchesTemplate(subeg, ExceptionGroup, template)
f = lambda e: isinstance(e, match_type)
for callable in [f, Predicate(f), Predicate(f).method]:
with self.subTest(callable=callable):
subeg = eg.subgroup(f)
self.assertEqual(subeg.message, eg.message)
self.assertMatchesTemplate(subeg, ExceptionGroup, template)


class ExceptionGroupSplitTests(ExceptionGroupTestBase):
Expand Down Expand Up @@ -405,14 +428,18 @@ def test_basics_split_by_type__match(self):
self.assertIsNone(rest)

def test_basics_split_by_predicate__passthrough(self):
match, rest = self.eg.split(lambda e: True)
self.assertMatchesTemplate(match, ExceptionGroup, self.eg_template)
self.assertIsNone(rest)
f = lambda e: True
for callable in [f, Predicate(f), Predicate(f).method]:
match, rest = self.eg.split(callable)
self.assertMatchesTemplate(match, ExceptionGroup, self.eg_template)
self.assertIsNone(rest)

def test_basics_split_by_predicate__no_match(self):
match, rest = self.eg.split(lambda e: False)
self.assertIsNone(match)
self.assertMatchesTemplate(rest, ExceptionGroup, self.eg_template)
f = lambda e: False
for callable in [f, Predicate(f), Predicate(f).method]:
match, rest = self.eg.split(callable)
self.assertIsNone(match)
self.assertMatchesTemplate(rest, ExceptionGroup, self.eg_template)

def test_basics_split_by_predicate__match(self):
eg = self.eg
Expand All @@ -426,20 +453,22 @@ def test_basics_split_by_predicate__match(self):
]

for match_type, match_template, rest_template in testcases:
match, rest = eg.split(lambda e: isinstance(e, match_type))
self.assertEqual(match.message, eg.message)
self.assertMatchesTemplate(
match, ExceptionGroup, match_template)
if rest_template is not None:
self.assertEqual(rest.message, eg.message)
f = lambda e: isinstance(e, match_type)
for callable in [f, Predicate(f), Predicate(f).method]:
match, rest = eg.split(callable)
self.assertEqual(match.message, eg.message)
self.assertMatchesTemplate(
rest, ExceptionGroup, rest_template)
match, ExceptionGroup, match_template)
if rest_template is not None:
self.assertEqual(rest.message, eg.message)
self.assertMatchesTemplate(
rest, ExceptionGroup, rest_template)


class DeepRecursionInSplitAndSubgroup(unittest.TestCase):
def make_deep_eg(self):
e = TypeError(1)
for i in range(C_RECURSION_LIMIT + 1):
for i in range(get_c_recursion_limit() + 1):
e = ExceptionGroup('eg', [e])
return e

Expand Down
1 change: 0 additions & 1 deletion Lib/test/test_exception_hierarchy.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,6 @@ def test_windows_error(self):
else:
self.assertNotIn('winerror', dir(OSError))

@unittest.skip("TODO: RUSTPYTHON")
def test_posix_error(self):
e = OSError(EEXIST, "File already exists", "foo.txt")
self.assertEqual(e.errno, EEXIST)
Expand Down
Loading
Loading