Merge pull request #5254 from youknowone/exception-group

ExceptionGroup
This commit is contained in:
Jeong, YunWon authored and GitHub committed 2025-03-27 14:51:12 +09:00
commit c7ca173c90
11 files changed
+406 -70

No files matched your search

-2
View File
@@ -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()
-2
View File
@@ -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",
+32 -60
View File
@@ -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):
+7 -3
View File
@@ -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)
+1
View File
@@ -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
View File
@@ -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;
}
+2
View File
@@ -0,0 +1,2 @@
export NODE_OPTIONS=--openssl-legacy-provider
export PATH=$PATH:`pwd`/../../geckodriver
+1 -1
View File
@@ -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",
+8 -1
View File
@@ -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({
+6
View File
@@ -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