mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
Merge pull request #5254 from youknowone/exception-group
ExceptionGroup
This commit is contained in:
11 files changed
+406
-70
No files matched your search
Vendored
-2
@@ -18,8 +18,6 @@ class ExceptionClassTests(unittest.TestCase):
|
||||
"%s missing %s attribute" %
|
||||
(ins.__class__.__name__, attr))
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_inheritance(self):
|
||||
# Make sure the inheritance hierarchy matches the documentation
|
||||
exc_set = set()
|
||||
|
||||
Vendored
-2
@@ -1271,8 +1271,6 @@ class TestSuppress(ExceptionIsLikeMixin, unittest.TestCase):
|
||||
1/0
|
||||
self.assertTrue(outer_continued)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_exception_groups(self):
|
||||
eg_ve = lambda: ExceptionGroup(
|
||||
"EG with ValueErrors only",
|
||||
|
||||
Vendored
+32
-60
@@ -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))
|
||||
@@ -38,8 +35,6 @@ class BadConstructorArgs(unittest.TestCase):
|
||||
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):
|
||||
@@ -47,8 +42,6 @@ class BadConstructorArgs(unittest.TestCase):
|
||||
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):
|
||||
@@ -60,8 +53,6 @@ class BadConstructorArgs(unittest.TestCase):
|
||||
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')
|
||||
@@ -78,16 +69,12 @@ class InstanceCreation(unittest.TestCase):
|
||||
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):
|
||||
@@ -105,8 +92,6 @@ class InstanceCreation(unittest.TestCase):
|
||||
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
|
||||
@@ -115,8 +100,6 @@ class InstanceCreation(unittest.TestCase):
|
||||
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
|
||||
@@ -125,6 +108,21 @@ class InstanceCreation(unittest.TestCase):
|
||||
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
|
||||
@@ -138,8 +136,6 @@ class InstanceCreation(unittest.TestCase):
|
||||
|
||||
|
||||
class StrAndReprTests(unittest.TestCase):
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_ExceptionGroup(self):
|
||||
eg = BaseExceptionGroup(
|
||||
'flat', [ValueError(1), TypeError(2)])
|
||||
@@ -160,8 +156,6 @@ class StrAndReprTests(unittest.TestCase):
|
||||
"ExceptionGroup('flat', "
|
||||
"[ValueError(1), TypeError(2)]), TypeError(2)])")
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_BaseExceptionGroup(self):
|
||||
eg = BaseExceptionGroup(
|
||||
'flat', [ValueError(1), KeyboardInterrupt(2)])
|
||||
@@ -184,8 +178,6 @@ class StrAndReprTests(unittest.TestCase):
|
||||
"BaseExceptionGroup('flat', "
|
||||
"[ValueError(1), KeyboardInterrupt(2)])])")
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_custom_exception(self):
|
||||
class MyEG(ExceptionGroup):
|
||||
pass
|
||||
@@ -270,8 +262,6 @@ class ExceptionGroupFields(unittest.TestCase):
|
||||
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)])
|
||||
|
||||
@@ -287,8 +277,6 @@ class ExceptionGroupFields(unittest.TestCase):
|
||||
|
||||
|
||||
class ExceptionGroupTestBase(unittest.TestCase):
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def assertMatchesTemplate(self, exc, exc_type, template):
|
||||
""" Assert that the exception matches the template
|
||||
|
||||
@@ -318,7 +306,6 @@ class ExceptionGroupSubgroupTests(ExceptionGroupTestBase):
|
||||
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'),
|
||||
@@ -330,8 +317,6 @@ class ExceptionGroupSubgroupTests(ExceptionGroupTestBase):
|
||||
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))
|
||||
@@ -339,11 +324,9 @@ class ExceptionGroupSubgroupTests(ExceptionGroupTestBase):
|
||||
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 = [
|
||||
@@ -358,15 +341,12 @@ class ExceptionGroupSubgroupTests(ExceptionGroupTestBase):
|
||||
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 = [
|
||||
@@ -386,7 +366,6 @@ class ExceptionGroupSplitTests(ExceptionGroupTestBase):
|
||||
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]:
|
||||
@@ -395,14 +374,12 @@ class ExceptionGroupSplitTests(ExceptionGroupTestBase):
|
||||
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
|
||||
@@ -427,19 +404,16 @@ class ExceptionGroupSplitTests(ExceptionGroupTestBase):
|
||||
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
|
||||
@@ -465,19 +439,15 @@ class ExceptionGroupSplitTests(ExceptionGroupTestBase):
|
||||
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):
|
||||
@@ -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):
|
||||
@@ -654,8 +624,6 @@ class ExceptionGroupSplitTestBase(ExceptionGroupTestBase):
|
||||
|
||||
class NestedExceptionGroupSplitTest(ExceptionGroupSplitTestBase):
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_split_by_type(self):
|
||||
class MyExceptionGroup(ExceptionGroup):
|
||||
pass
|
||||
@@ -755,8 +723,6 @@ class NestedExceptionGroupSplitTest(ExceptionGroupSplitTestBase):
|
||||
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:
|
||||
@@ -797,8 +763,6 @@ class NestedExceptionGroupSplitTest(ExceptionGroupSplitTestBase):
|
||||
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)])
|
||||
@@ -830,11 +794,23 @@ class NestedExceptionGroupSplitTest(ExceptionGroupSplitTestBase):
|
||||
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
|
||||
@@ -877,8 +853,6 @@ class NestedExceptionGroupSubclassSplitTest(ExceptionGroupSplitTestBase):
|
||||
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):
|
||||
@@ -921,8 +895,6 @@ class NestedExceptionGroupSubclassSplitTest(ExceptionGroupSplitTestBase):
|
||||
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):
|
||||
|
||||
Vendored
+7
-3
@@ -22,7 +22,6 @@ At large scale, the structure of the module is following:
|
||||
from abc import abstractmethod, ABCMeta
|
||||
import collections
|
||||
import collections.abc
|
||||
import contextlib
|
||||
import functools
|
||||
import operator
|
||||
import re as stdlib_re # Avoid confusion with the re we export.
|
||||
@@ -2138,8 +2137,13 @@ MappingView = _alias(collections.abc.MappingView, 1)
|
||||
KeysView = _alias(collections.abc.KeysView, 1)
|
||||
ItemsView = _alias(collections.abc.ItemsView, 2)
|
||||
ValuesView = _alias(collections.abc.ValuesView, 1)
|
||||
ContextManager = _alias(contextlib.AbstractContextManager, 1, name='ContextManager')
|
||||
AsyncContextManager = _alias(contextlib.AbstractAsyncContextManager, 1, name='AsyncContextManager')
|
||||
try:
|
||||
# XXX: RUSTPYTHON; contextlib support for wasm
|
||||
import contextlib
|
||||
ContextManager = _alias(contextlib.AbstractContextManager, 1, name='ContextManager')
|
||||
AsyncContextManager = _alias(contextlib.AbstractAsyncContextManager, 1, name='AsyncContextManager')
|
||||
except ImportError:
|
||||
pass
|
||||
Dict = _alias(dict, 2, inst=False, name='Dict')
|
||||
DefaultDict = _alias(collections.defaultdict, 2, name='DefaultDict')
|
||||
OrderedDict = _alias(collections.OrderedDict, 2)
|
||||
|
||||
@@ -18,6 +18,7 @@ pub type InitHook = Box<dyn FnOnce(&mut VirtualMachine)>;
|
||||
/// let mut settings = Settings::default();
|
||||
/// settings.debug = 1;
|
||||
/// // You may want to add paths to `rustpython_vm::Settings::path_list` to allow import python libraries.
|
||||
/// settings.path_list.push("Lib".to_owned()); // add standard library directory
|
||||
/// settings.path_list.push("".to_owned()); // add current working directory
|
||||
/// let interpreter = rustpython::InterpreterConfig::new()
|
||||
/// .settings(settings)
|
||||
|
||||
@@ -0,0 +1,330 @@
|
||||
# Copied from https://github.com/agronholm/ExceptionGroup/blob/1.2.1/src/exceptiongroup/_exceptions.py
|
||||
# License: https://github.com/agronholm/exceptiongroup/blob/1.2.1/LICENSE
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable, Sequence
|
||||
from functools import partial
|
||||
from typing import TYPE_CHECKING, Generic, Type, TypeVar, cast, overload
|
||||
|
||||
_BaseExceptionT_co = TypeVar("_BaseExceptionT_co", bound=BaseException, covariant=True)
|
||||
_BaseExceptionT = TypeVar("_BaseExceptionT", bound=BaseException)
|
||||
_ExceptionT_co = TypeVar("_ExceptionT_co", bound=Exception, covariant=True)
|
||||
_ExceptionT = TypeVar("_ExceptionT", bound=Exception)
|
||||
# using typing.Self would require a typing_extensions dependency on py<3.11
|
||||
_ExceptionGroupSelf = TypeVar("_ExceptionGroupSelf", bound="ExceptionGroup")
|
||||
_BaseExceptionGroupSelf = TypeVar("_BaseExceptionGroupSelf", bound="BaseExceptionGroup")
|
||||
|
||||
|
||||
def check_direct_subclass(
|
||||
exc: BaseException, parents: tuple[type[BaseException]]
|
||||
) -> bool:
|
||||
from inspect import getmro # requires rustpython-stdlib
|
||||
|
||||
for cls in getmro(exc.__class__)[:-1]:
|
||||
if cls in parents:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def get_condition_filter(
|
||||
condition: type[_BaseExceptionT]
|
||||
| tuple[type[_BaseExceptionT], ...]
|
||||
| Callable[[_BaseExceptionT_co], bool],
|
||||
) -> Callable[[_BaseExceptionT_co], bool]:
|
||||
from inspect import isclass # requires rustpython-stdlib
|
||||
|
||||
if isclass(condition) and issubclass(
|
||||
cast(Type[BaseException], condition), BaseException
|
||||
):
|
||||
return partial(check_direct_subclass, parents=(condition,))
|
||||
elif isinstance(condition, tuple):
|
||||
if all(isclass(x) and issubclass(x, BaseException) for x in condition):
|
||||
return partial(check_direct_subclass, parents=condition)
|
||||
elif callable(condition):
|
||||
return cast("Callable[[BaseException], bool]", condition)
|
||||
|
||||
raise TypeError("expected a function, exception type or tuple of exception types")
|
||||
|
||||
|
||||
def _derive_and_copy_attributes(self, excs):
|
||||
eg = self.derive(excs)
|
||||
eg.__cause__ = self.__cause__
|
||||
eg.__context__ = self.__context__
|
||||
eg.__traceback__ = self.__traceback__
|
||||
if hasattr(self, "__notes__"):
|
||||
# Create a new list so that add_note() only affects one exceptiongroup
|
||||
eg.__notes__ = list(self.__notes__)
|
||||
return eg
|
||||
|
||||
|
||||
class BaseExceptionGroup(BaseException, Generic[_BaseExceptionT_co]):
|
||||
"""A combination of multiple unrelated exceptions."""
|
||||
|
||||
def __new__(
|
||||
cls: type[_BaseExceptionGroupSelf],
|
||||
__message: str,
|
||||
__exceptions: Sequence[_BaseExceptionT_co],
|
||||
) -> _BaseExceptionGroupSelf:
|
||||
if not isinstance(__message, str):
|
||||
raise TypeError(f"argument 1 must be str, not {type(__message)}")
|
||||
if not isinstance(__exceptions, Sequence):
|
||||
raise TypeError("second argument (exceptions) must be a sequence")
|
||||
if not __exceptions:
|
||||
raise ValueError(
|
||||
"second argument (exceptions) must be a non-empty sequence"
|
||||
)
|
||||
|
||||
for i, exc in enumerate(__exceptions):
|
||||
if not isinstance(exc, BaseException):
|
||||
raise ValueError(
|
||||
f"Item {i} of second argument (exceptions) is not an exception"
|
||||
)
|
||||
|
||||
if cls is BaseExceptionGroup:
|
||||
if all(isinstance(exc, Exception) for exc in __exceptions):
|
||||
cls = ExceptionGroup
|
||||
|
||||
if issubclass(cls, Exception):
|
||||
for exc in __exceptions:
|
||||
if not isinstance(exc, Exception):
|
||||
if cls is ExceptionGroup:
|
||||
raise TypeError(
|
||||
"Cannot nest BaseExceptions in an ExceptionGroup"
|
||||
)
|
||||
else:
|
||||
raise TypeError(
|
||||
f"Cannot nest BaseExceptions in {cls.__name__!r}"
|
||||
)
|
||||
|
||||
instance = super().__new__(cls, __message, __exceptions)
|
||||
instance._message = __message
|
||||
instance._exceptions = __exceptions
|
||||
return instance
|
||||
|
||||
def add_note(self, note: str) -> None:
|
||||
if not isinstance(note, str):
|
||||
raise TypeError(
|
||||
f"Expected a string, got note={note!r} (type {type(note).__name__})"
|
||||
)
|
||||
|
||||
if not hasattr(self, "__notes__"):
|
||||
self.__notes__: list[str] = []
|
||||
|
||||
self.__notes__.append(note)
|
||||
|
||||
@property
|
||||
def message(self) -> str:
|
||||
return self._message
|
||||
|
||||
@property
|
||||
def exceptions(
|
||||
self,
|
||||
) -> tuple[_BaseExceptionT_co | BaseExceptionGroup[_BaseExceptionT_co], ...]:
|
||||
return tuple(self._exceptions)
|
||||
|
||||
@overload
|
||||
def subgroup(
|
||||
self, __condition: type[_ExceptionT] | tuple[type[_ExceptionT], ...]
|
||||
) -> ExceptionGroup[_ExceptionT] | None: ...
|
||||
|
||||
@overload
|
||||
def subgroup(
|
||||
self, __condition: type[_BaseExceptionT] | tuple[type[_BaseExceptionT], ...]
|
||||
) -> BaseExceptionGroup[_BaseExceptionT] | None: ...
|
||||
|
||||
@overload
|
||||
def subgroup(
|
||||
self,
|
||||
__condition: Callable[[_BaseExceptionT_co | _BaseExceptionGroupSelf], bool],
|
||||
) -> BaseExceptionGroup[_BaseExceptionT_co] | None: ...
|
||||
|
||||
def subgroup(
|
||||
self,
|
||||
__condition: type[_BaseExceptionT]
|
||||
| tuple[type[_BaseExceptionT], ...]
|
||||
| Callable[[_BaseExceptionT_co | _BaseExceptionGroupSelf], bool],
|
||||
) -> BaseExceptionGroup[_BaseExceptionT] | None:
|
||||
condition = get_condition_filter(__condition)
|
||||
modified = False
|
||||
if condition(self):
|
||||
return self
|
||||
|
||||
exceptions: list[BaseException] = []
|
||||
for exc in self.exceptions:
|
||||
if isinstance(exc, BaseExceptionGroup):
|
||||
subgroup = exc.subgroup(__condition)
|
||||
if subgroup is not None:
|
||||
exceptions.append(subgroup)
|
||||
|
||||
if subgroup is not exc:
|
||||
modified = True
|
||||
elif condition(exc):
|
||||
exceptions.append(exc)
|
||||
else:
|
||||
modified = True
|
||||
|
||||
if not modified:
|
||||
return self
|
||||
elif exceptions:
|
||||
group = _derive_and_copy_attributes(self, exceptions)
|
||||
return group
|
||||
else:
|
||||
return None
|
||||
|
||||
@overload
|
||||
def split(
|
||||
self, __condition: type[_ExceptionT] | tuple[type[_ExceptionT], ...]
|
||||
) -> tuple[
|
||||
ExceptionGroup[_ExceptionT] | None,
|
||||
BaseExceptionGroup[_BaseExceptionT_co] | None,
|
||||
]: ...
|
||||
|
||||
@overload
|
||||
def split(
|
||||
self, __condition: type[_BaseExceptionT] | tuple[type[_BaseExceptionT], ...]
|
||||
) -> tuple[
|
||||
BaseExceptionGroup[_BaseExceptionT] | None,
|
||||
BaseExceptionGroup[_BaseExceptionT_co] | None,
|
||||
]: ...
|
||||
|
||||
@overload
|
||||
def split(
|
||||
self,
|
||||
__condition: Callable[[_BaseExceptionT_co | _BaseExceptionGroupSelf], bool],
|
||||
) -> tuple[
|
||||
BaseExceptionGroup[_BaseExceptionT_co] | None,
|
||||
BaseExceptionGroup[_BaseExceptionT_co] | None,
|
||||
]: ...
|
||||
|
||||
def split(
|
||||
self,
|
||||
__condition: type[_BaseExceptionT]
|
||||
| tuple[type[_BaseExceptionT], ...]
|
||||
| Callable[[_BaseExceptionT_co], bool],
|
||||
) -> (
|
||||
tuple[
|
||||
ExceptionGroup[_ExceptionT] | None,
|
||||
BaseExceptionGroup[_BaseExceptionT_co] | None,
|
||||
]
|
||||
| tuple[
|
||||
BaseExceptionGroup[_BaseExceptionT] | None,
|
||||
BaseExceptionGroup[_BaseExceptionT_co] | None,
|
||||
]
|
||||
| tuple[
|
||||
BaseExceptionGroup[_BaseExceptionT_co] | None,
|
||||
BaseExceptionGroup[_BaseExceptionT_co] | None,
|
||||
]
|
||||
):
|
||||
condition = get_condition_filter(__condition)
|
||||
if condition(self):
|
||||
return self, None
|
||||
|
||||
matching_exceptions: list[BaseException] = []
|
||||
nonmatching_exceptions: list[BaseException] = []
|
||||
for exc in self.exceptions:
|
||||
if isinstance(exc, BaseExceptionGroup):
|
||||
matching, nonmatching = exc.split(condition)
|
||||
if matching is not None:
|
||||
matching_exceptions.append(matching)
|
||||
|
||||
if nonmatching is not None:
|
||||
nonmatching_exceptions.append(nonmatching)
|
||||
elif condition(exc):
|
||||
matching_exceptions.append(exc)
|
||||
else:
|
||||
nonmatching_exceptions.append(exc)
|
||||
|
||||
matching_group: _BaseExceptionGroupSelf | None = None
|
||||
if matching_exceptions:
|
||||
matching_group = _derive_and_copy_attributes(self, matching_exceptions)
|
||||
|
||||
nonmatching_group: _BaseExceptionGroupSelf | None = None
|
||||
if nonmatching_exceptions:
|
||||
nonmatching_group = _derive_and_copy_attributes(
|
||||
self, nonmatching_exceptions
|
||||
)
|
||||
|
||||
return matching_group, nonmatching_group
|
||||
|
||||
@overload
|
||||
def derive(self, __excs: Sequence[_ExceptionT]) -> ExceptionGroup[_ExceptionT]: ...
|
||||
|
||||
@overload
|
||||
def derive(
|
||||
self, __excs: Sequence[_BaseExceptionT]
|
||||
) -> BaseExceptionGroup[_BaseExceptionT]: ...
|
||||
|
||||
def derive(
|
||||
self, __excs: Sequence[_BaseExceptionT]
|
||||
) -> BaseExceptionGroup[_BaseExceptionT]:
|
||||
return BaseExceptionGroup(self.message, __excs)
|
||||
|
||||
def __str__(self) -> str:
|
||||
suffix = "" if len(self._exceptions) == 1 else "s"
|
||||
return f"{self.message} ({len(self._exceptions)} sub-exception{suffix})"
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"{self.__class__.__name__}({self.message!r}, {self._exceptions!r})"
|
||||
|
||||
|
||||
class ExceptionGroup(BaseExceptionGroup[_ExceptionT_co], Exception):
|
||||
def __new__(
|
||||
cls: type[_ExceptionGroupSelf],
|
||||
__message: str,
|
||||
__exceptions: Sequence[_ExceptionT_co],
|
||||
) -> _ExceptionGroupSelf:
|
||||
return super().__new__(cls, __message, __exceptions)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
||||
@property
|
||||
def exceptions(
|
||||
self,
|
||||
) -> tuple[_ExceptionT_co | ExceptionGroup[_ExceptionT_co], ...]: ...
|
||||
|
||||
@overload # type: ignore[override]
|
||||
def subgroup(
|
||||
self, __condition: type[_ExceptionT] | tuple[type[_ExceptionT], ...]
|
||||
) -> ExceptionGroup[_ExceptionT] | None: ...
|
||||
|
||||
@overload
|
||||
def subgroup(
|
||||
self, __condition: Callable[[_ExceptionT_co | _ExceptionGroupSelf], bool]
|
||||
) -> ExceptionGroup[_ExceptionT_co] | None: ...
|
||||
|
||||
def subgroup(
|
||||
self,
|
||||
__condition: type[_ExceptionT]
|
||||
| tuple[type[_ExceptionT], ...]
|
||||
| Callable[[_ExceptionT_co], bool],
|
||||
) -> ExceptionGroup[_ExceptionT] | None:
|
||||
return super().subgroup(__condition)
|
||||
|
||||
@overload
|
||||
def split(
|
||||
self, __condition: type[_ExceptionT] | tuple[type[_ExceptionT], ...]
|
||||
) -> tuple[
|
||||
ExceptionGroup[_ExceptionT] | None, ExceptionGroup[_ExceptionT_co] | None
|
||||
]: ...
|
||||
|
||||
@overload
|
||||
def split(
|
||||
self, __condition: Callable[[_ExceptionT_co | _ExceptionGroupSelf], bool]
|
||||
) -> tuple[
|
||||
ExceptionGroup[_ExceptionT_co] | None, ExceptionGroup[_ExceptionT_co] | None
|
||||
]: ...
|
||||
|
||||
def split(
|
||||
self: _ExceptionGroupSelf,
|
||||
__condition: type[_ExceptionT]
|
||||
| tuple[type[_ExceptionT], ...]
|
||||
| Callable[[_ExceptionT_co], bool],
|
||||
) -> tuple[
|
||||
ExceptionGroup[_ExceptionT_co] | None, ExceptionGroup[_ExceptionT_co] | None
|
||||
]:
|
||||
return super().split(__condition)
|
||||
|
||||
|
||||
BaseExceptionGroup.__module__ = 'builtins'
|
||||
ExceptionGroup.__module__ = 'builtins'
|
||||
+19
-1
@@ -364,8 +364,11 @@ impl VirtualMachine {
|
||||
}
|
||||
}
|
||||
|
||||
let expect_stdlib =
|
||||
cfg!(feature = "freeze-stdlib") || !self.state.settings.path_list.is_empty();
|
||||
|
||||
#[cfg(feature = "encodings")]
|
||||
if cfg!(feature = "freeze-stdlib") || !self.state.settings.path_list.is_empty() {
|
||||
if expect_stdlib {
|
||||
if let Err(e) = self.import_encodings() {
|
||||
eprintln!(
|
||||
"encodings initialization failed. Only utf-8 encoding will be supported."
|
||||
@@ -382,6 +385,21 @@ impl VirtualMachine {
|
||||
);
|
||||
}
|
||||
|
||||
if expect_stdlib {
|
||||
// enable python-implemented ExceptionGroup when stdlib exists
|
||||
let py_core_init = || -> PyResult<()> {
|
||||
let exception_group = import::import_frozen(self, "_py_exceptiongroup")?;
|
||||
let base_exception_group = exception_group.get_attr("BaseExceptionGroup", self)?;
|
||||
self.builtins
|
||||
.set_attr("BaseExceptionGroup", base_exception_group, self)?;
|
||||
let exception_group = exception_group.get_attr("ExceptionGroup", self)?;
|
||||
self.builtins
|
||||
.set_attr("ExceptionGroup", exception_group, self)?;
|
||||
Ok(())
|
||||
};
|
||||
self.expect_pyresult(py_core_init(), "exceptiongroup initialization failed");
|
||||
}
|
||||
|
||||
self.initialized = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
export NODE_OPTIONS=--openssl-legacy-provider
|
||||
export PATH=$PATH:`pwd`/../../geckodriver
|
||||
@@ -25,7 +25,7 @@
|
||||
"dev": "webpack serve",
|
||||
"build": "webpack",
|
||||
"dist": "webpack --mode production",
|
||||
"test": "webpack --mode production && cd ../tests && pytest"
|
||||
"test": "webpack --mode production && cd ../tests && pytest -v"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -21,7 +21,14 @@ import('rustpython')
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error('Error importing `rustpython`:', e);
|
||||
document.getElementById('error').textContent = e;
|
||||
let errorDetails = e.toString();
|
||||
if (window.__RUSTPYTHON_ERROR) {
|
||||
errorDetails += '\nRustPython Error: ' + window.__RUSTPYTHON_ERROR;
|
||||
}
|
||||
if (window.__RUSTPYTHON_ERROR_STACK) {
|
||||
errorDetails += '\nStack: ' + window.__RUSTPYTHON_ERROR_STACK;
|
||||
}
|
||||
document.getElementById('error').textContent = errorDetails;
|
||||
});
|
||||
|
||||
const fixedHeightEditor = EditorView.theme({
|
||||
|
||||
@@ -102,6 +102,12 @@ def wdriver(request):
|
||||
driver._print_panic()
|
||||
driver.quit()
|
||||
raise
|
||||
except Exception as e:
|
||||
print(f"Error waiting for page to load: {e}")
|
||||
# Check the page source to see what's loaded
|
||||
print("Page source:", driver.page_source[:500])
|
||||
driver.quit()
|
||||
raise
|
||||
|
||||
yield driver
|
||||
|
||||
|
||||
Reference in new issue
Block a user