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

ExceptionGroup #5254

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Mar 27, 2025
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
test_exception_group from CPython 3.12.2
  • Loading branch information
youknowone committed Mar 26, 2025
commit 5e0eace8d9a0c83a62324c6470065b0899fcb6df
92 changes: 32 additions & 60 deletions 92 Lib/test/test_exception_group.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import collections.abc
import traceback
import types
import unittest

from test.support import C_RECURSION_LIMIT

class TestExceptionGroupTypeHierarchy(unittest.TestCase):
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_exception_group_types(self):
self.assertTrue(issubclass(ExceptionGroup, Exception))
self.assertTrue(issubclass(ExceptionGroup, BaseExceptionGroup))
Expand Down Expand Up @@ -38,17 +35,13 @@ def test_bad_EG_construction__too_many_args(self):
with self.assertRaisesRegex(TypeError, MSG):
ExceptionGroup('eg', [ValueError('too')], [TypeError('many')])

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_bad_EG_construction__bad_message(self):
MSG = 'argument 1 must be str, not '
with self.assertRaisesRegex(TypeError, MSG):
ExceptionGroup(ValueError(12), SyntaxError('bad syntax'))
with self.assertRaisesRegex(TypeError, MSG):
ExceptionGroup(None, [ValueError(12)])

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_bad_EG_construction__bad_excs_sequence(self):
MSG = r'second argument \(exceptions\) must be a sequence'
with self.assertRaisesRegex(TypeError, MSG):
Expand All @@ -60,8 +53,6 @@ def test_bad_EG_construction__bad_excs_sequence(self):
with self.assertRaisesRegex(ValueError, MSG):
ExceptionGroup("eg", [])

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_bad_EG_construction__nested_non_exceptions(self):
MSG = (r'Item [0-9]+ of second argument \(exceptions\)'
' is not an exception')
Expand All @@ -78,16 +69,12 @@ def test_EG_wraps_Exceptions__creates_EG(self):
type(ExceptionGroup("eg", excs)),
ExceptionGroup)

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_BEG_wraps_Exceptions__creates_EG(self):
excs = [ValueError(1), TypeError(2)]
self.assertIs(
type(BaseExceptionGroup("beg", excs)),
ExceptionGroup)

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_EG_wraps_BaseException__raises_TypeError(self):
MSG= "Cannot nest BaseExceptions in an ExceptionGroup"
with self.assertRaisesRegex(TypeError, MSG):
Expand All @@ -105,8 +92,6 @@ class MyEG(ExceptionGroup):
type(MyEG("eg", [ValueError(12), TypeError(42)])),
MyEG)

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_EG_subclass_does_not_wrap_base_exceptions(self):
class MyEG(ExceptionGroup):
pass
Expand All @@ -115,8 +100,6 @@ class MyEG(ExceptionGroup):
with self.assertRaisesRegex(TypeError, msg):
MyEG("eg", [ValueError(12), KeyboardInterrupt(42)])

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_BEG_and_E_subclass_does_not_wrap_base_exceptions(self):
class MyEG(BaseExceptionGroup, ValueError):
pass
Expand All @@ -125,6 +108,21 @@ class MyEG(BaseExceptionGroup, ValueError):
with self.assertRaisesRegex(TypeError, msg):
MyEG("eg", [ValueError(12), KeyboardInterrupt(42)])

def test_EG_and_specific_subclass_can_wrap_any_nonbase_exception(self):
class MyEG(ExceptionGroup, ValueError):
pass

# The restriction is specific to Exception, not "the other base class"
MyEG("eg", [ValueError(12), Exception()])

def test_BEG_and_specific_subclass_can_wrap_any_nonbase_exception(self):
class MyEG(BaseExceptionGroup, ValueError):
pass

# The restriction is specific to Exception, not "the other base class"
MyEG("eg", [ValueError(12), Exception()])


def test_BEG_subclass_wraps_anything(self):
class MyBEG(BaseExceptionGroup):
pass
Expand All @@ -138,8 +136,6 @@ class MyBEG(BaseExceptionGroup):


class StrAndReprTests(unittest.TestCase):
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_ExceptionGroup(self):
eg = BaseExceptionGroup(
'flat', [ValueError(1), TypeError(2)])
Expand All @@ -160,8 +156,6 @@ def test_ExceptionGroup(self):
"ExceptionGroup('flat', "
"[ValueError(1), TypeError(2)]), TypeError(2)])")

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_BaseExceptionGroup(self):
eg = BaseExceptionGroup(
'flat', [ValueError(1), KeyboardInterrupt(2)])
Expand All @@ -184,8 +178,6 @@ def test_BaseExceptionGroup(self):
"BaseExceptionGroup('flat', "
"[ValueError(1), KeyboardInterrupt(2)])])")

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_custom_exception(self):
class MyEG(ExceptionGroup):
pass
Expand Down Expand Up @@ -270,8 +262,6 @@ def test_basics_ExceptionGroup_fields(self):
self.assertIsNone(tb.tb_next)
self.assertEqual(tb.tb_lineno, tb_linenos[1][i])

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_fields_are_readonly(self):
eg = ExceptionGroup('eg', [TypeError(1), OSError(2)])

Expand All @@ -287,8 +277,6 @@ def test_fields_are_readonly(self):


class ExceptionGroupTestBase(unittest.TestCase):
# TODO: RUSTPYTHON
@unittest.expectedFailure
def assertMatchesTemplate(self, exc, exc_type, template):
""" Assert that the exception matches the template

Expand Down Expand Up @@ -318,7 +306,6 @@ def setUp(self):
self.eg = create_simple_eg()
self.eg_template = [ValueError(1), TypeError(int), ValueError(2)]

@unittest.skip("TODO: RUSTPYTHON")
def test_basics_subgroup_split__bad_arg_type(self):
bad_args = ["bad arg",
OSError('instance not type'),
Expand All @@ -330,20 +317,16 @@ def test_basics_subgroup_split__bad_arg_type(self):
with self.assertRaises(TypeError):
self.eg.split(arg)


@unittest.skip("TODO: RUSTPYTHON")
def test_basics_subgroup_by_type__passthrough(self):
eg = self.eg
self.assertIs(eg, eg.subgroup(BaseException))
self.assertIs(eg, eg.subgroup(Exception))
self.assertIs(eg, eg.subgroup(BaseExceptionGroup))
self.assertIs(eg, eg.subgroup(ExceptionGroup))

@unittest.skip("TODO: RUSTPYTHON")
def test_basics_subgroup_by_type__no_match(self):
self.assertIsNone(self.eg.subgroup(OSError))

@unittest.skip("TODO: RUSTPYTHON")
def test_basics_subgroup_by_type__match(self):
eg = self.eg
testcases = [
Expand All @@ -358,15 +341,12 @@ def test_basics_subgroup_by_type__match(self):
self.assertEqual(subeg.message, eg.message)
self.assertMatchesTemplate(subeg, ExceptionGroup, template)

@unittest.skip("TODO: RUSTPYTHON")
def test_basics_subgroup_by_predicate__passthrough(self):
self.assertIs(self.eg, self.eg.subgroup(lambda e: True))

@unittest.skip("TODO: RUSTPYTHON")
def test_basics_subgroup_by_predicate__no_match(self):
self.assertIsNone(self.eg.subgroup(lambda e: False))

@unittest.skip("TODO: RUSTPYTHON")
def test_basics_subgroup_by_predicate__match(self):
eg = self.eg
testcases = [
Expand All @@ -386,7 +366,6 @@ def setUp(self):
self.eg = create_simple_eg()
self.eg_template = [ValueError(1), TypeError(int), ValueError(2)]

@unittest.skip("TODO: RUSTPYTHON")
def test_basics_split_by_type__passthrough(self):
for E in [BaseException, Exception,
BaseExceptionGroup, ExceptionGroup]:
Expand All @@ -395,14 +374,12 @@ def test_basics_split_by_type__passthrough(self):
match, ExceptionGroup, self.eg_template)
self.assertIsNone(rest)

@unittest.skip("TODO: RUSTPYTHON")
def test_basics_split_by_type__no_match(self):
match, rest = self.eg.split(OSError)
self.assertIsNone(match)
self.assertMatchesTemplate(
rest, ExceptionGroup, self.eg_template)

@unittest.skip("TODO: RUSTPYTHON")
def test_basics_split_by_type__match(self):
eg = self.eg
VE = ValueError
Expand All @@ -427,19 +404,16 @@ def test_basics_split_by_type__match(self):
else:
self.assertIsNone(rest)

@unittest.skip("TODO: RUSTPYTHON")
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)

@unittest.skip("TODO: RUSTPYTHON")
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)

@unittest.skip("TODO: RUSTPYTHON")
def test_basics_split_by_predicate__match(self):
eg = self.eg
VE = ValueError
Expand All @@ -465,19 +439,15 @@ def test_basics_split_by_predicate__match(self):
class DeepRecursionInSplitAndSubgroup(unittest.TestCase):
def make_deep_eg(self):
e = TypeError(1)
for i in range(2000):
for i in range(C_RECURSION_LIMIT + 1):
e = ExceptionGroup('eg', [e])
return e

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_deep_split(self):
e = self.make_deep_eg()
with self.assertRaises(RecursionError):
e.split(TypeError)

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_deep_subgroup(self):
e = self.make_deep_eg()
with self.assertRaises(RecursionError):
Expand All @@ -501,7 +471,7 @@ def leaf_generator(exc, tbs=None):

class LeafGeneratorTest(unittest.TestCase):
# The leaf_generator is mentioned in PEP 654 as a suggestion
# on how to iterate over leaf nodes of an EG. It is also
# on how to iterate over leaf nodes of an EG. Is is also
# used below as a test utility. So we test it here.

def test_leaf_generator(self):
Expand Down Expand Up @@ -654,8 +624,6 @@ def tb_linenos(tbs):

class NestedExceptionGroupSplitTest(ExceptionGroupSplitTestBase):

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_split_by_type(self):
class MyExceptionGroup(ExceptionGroup):
pass
Expand Down Expand Up @@ -755,8 +723,6 @@ def level3(i):
self.assertMatchesTemplate(match, ExceptionGroup, [eg_template[0]])
self.assertMatchesTemplate(rest, ExceptionGroup, [eg_template[1]])

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_split_BaseExceptionGroup(self):
def exc(ex):
try:
Expand Down Expand Up @@ -797,8 +763,6 @@ def exc(ex):
self.assertMatchesTemplate(
rest, ExceptionGroup, [ValueError(1)])

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_split_copies_notes(self):
# make sure each exception group after a split has its own __notes__ list
eg = ExceptionGroup("eg", [ValueError(1), TypeError(2)])
Expand Down Expand Up @@ -830,11 +794,23 @@ def test_split_does_not_copy_non_sequence_notes(self):
self.assertFalse(hasattr(match, '__notes__'))
self.assertFalse(hasattr(rest, '__notes__'))

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_drive_invalid_return_value(self):
class MyEg(ExceptionGroup):
def derive(self, excs):
return 42

eg = MyEg('eg', [TypeError(1), ValueError(2)])
msg = "derive must return an instance of BaseExceptionGroup"
with self.assertRaisesRegex(TypeError, msg):
eg.split(TypeError)
with self.assertRaisesRegex(TypeError, msg):
eg.subgroup(TypeError)


class NestedExceptionGroupSubclassSplitTest(ExceptionGroupSplitTestBase):

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_split_ExceptionGroup_subclass_no_derive_no_new_override(self):
class EG(ExceptionGroup):
pass
Expand Down Expand Up @@ -877,8 +853,6 @@ class EG(ExceptionGroup):
self.assertMatchesTemplate(match, ExceptionGroup, [[TypeError(2)]])
self.assertMatchesTemplate(rest, ExceptionGroup, [ValueError(1)])

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_split_BaseExceptionGroup_subclass_no_derive_new_override(self):
class EG(BaseExceptionGroup):
def __new__(cls, message, excs, unused):
Expand Down Expand Up @@ -921,8 +895,6 @@ def __new__(cls, message, excs, unused):
match, BaseExceptionGroup, [KeyboardInterrupt(2)])
self.assertMatchesTemplate(rest, ExceptionGroup, [ValueError(1)])

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_split_ExceptionGroup_subclass_derive_and_new_overrides(self):
class EG(ExceptionGroup):
def __new__(cls, message, excs, code):
Expand Down
Morty Proxy This is a proxified and sanitized view of the page, visit original site.