I only mostly stick to runtime evil tricks, good to be here 😁
That all sounds good; just note under the derive examples that when b = BaseExceptionGroup('m', [ValueError()]) b is an instance of ExceptionGroup due to the unusual nature of BaseExceptionGroup.__new__! In fact that should maybe be added as an overload, though it gets tricky thinking through the subclasses...🤔
staticPyObject*BaseExceptionGroup_new(PyTypeObject*type, PyObject*args, PyObject*kwds)
{
struct_Py_exc_state*state=get_exc_state();
PyTypeObject*PyExc_ExceptionGroup=
(PyTypeObject*)state->PyExc_ExceptionGroup;
PyObject*message=NULL;
PyObject*exceptions=NULL;
if (!PyArg_ParseTuple(args,
"UO:BaseExceptionGroup.__new__",
&message,
&exceptions)) {
returnNULL;
}
if (!PySequence_Check(exceptions)) {
PyErr_SetString(
PyExc_TypeError,
"second argument (exceptions) must be a sequence");
returnNULL;
}
exceptions=PySequence_Tuple(exceptions);
if (!exceptions) {
returnNULL;
}
/* We are now holding a ref to the exceptions tuple */Py_ssize_tnumexcs=PyTuple_GET_SIZE(exceptions);
if (numexcs==0) {
PyErr_SetString(
PyExc_ValueError,
"second argument (exceptions) must be a non-empty sequence");
goto error;
}
boolnested_base_exceptions= false;
for (Py_ssize_ti=0; i<numexcs; i++) {
PyObject*exc=PyTuple_GET_ITEM(exceptions, i);
if (!exc) {
goto error;
}
if (!PyExceptionInstance_Check(exc)) {
PyErr_Format(
PyExc_ValueError,
"Item %d of second argument (exceptions) is not an exception",
i);
goto error;
}
intis_nonbase_exception=PyObject_IsInstance(exc, PyExc_Exception);
if (is_nonbase_exception<0) {
goto error;
}
elseif (is_nonbase_exception==0) {
nested_base_exceptions= true;
}
}
PyTypeObject*cls=type;
if (cls==PyExc_ExceptionGroup) {
if (nested_base_exceptions) {
PyErr_SetString(PyExc_TypeError,
"Cannot nest BaseExceptions in an ExceptionGroup");
goto error;
}
}
elseif (cls== (PyTypeObject*)PyExc_BaseExceptionGroup) {
if (!nested_base_exceptions) {
/* All nested exceptions are Exception subclasses, * wrap them in an ExceptionGroup */cls=PyExc_ExceptionGroup;
}
}
else {
/* Do nothing - we don't interfere with subclasses */
}
if (!cls) {
/* Don't crash during interpreter shutdown * (PyExc_ExceptionGroup may have been cleared) */cls= (PyTypeObject*)PyExc_BaseExceptionGroup;
}
PyBaseExceptionGroupObject*self=_PyBaseExceptionGroupObject_cast(BaseException_new(cls, args, kwds));
if (!self) {
goto error;
}
self->msg=Py_NewRef(message);
self->excs=exceptions;
return (PyObject*)self;
error:
Py_DECREF(exceptions);
returnNULL;
}
.. method:: subgroup(condition)
Returns an exception group that contains only the exceptions from the
current group that match *condition*, or ``None`` if the result is empty.
The condition can be either a function that accepts an exception and returns
true for those that should be in the subgroup, or it can be an exception type
or a tuple of exception types, which is used to check for a match using the
same check that is used in an ``except`` clause.
Ok, this is quite tricky. This function is firstly called with ExceptionGroup itself.
Then it is called with exceptions only if the first call returned False. If the first call returns True, it is not called anymore.
Activity
sobolevn commented on Nov 17, 2022
Hi @Zac-HD! Glad to see you in typing-land! 👋
Related PRs:
Yes, looks like Self contract is not always respected. Examples:
split
Self is only preserved when no split happens in the left part (I think it is not really important, we can always use ExceptionGroup).
derive
I cannot find a case, where Self is preserved.
subgroup
So, it can either be None or ExceptionGroup or MyGroup: so we can go with ExceptionGroup.
I would like to work on it, if no one has objections.
Zac-HD commented on Nov 17, 2022
I only mostly stick to runtime evil tricks, good to be here 😁
That all sounds good; just note under the derive examples that when b = BaseExceptionGroup('m', [ValueError()]) b is an instance of ExceptionGroup due to the unusual nature of BaseExceptionGroup.__new__! In fact that should maybe be added as an overload, though it gets tricky thinking through the subclasses...🤔
sobolevn commented on Nov 17, 2022
I will take a look, I've missed this part. Thanks!
sobolevn commented on Nov 17, 2022
For the record:
sobolevn commented on Nov 17, 2022
Looks like I found a bug in ExceptionGroup.subgroup:
Notice that p is called:
While the docs say:
.. method:: subgroup(condition) Returns an exception group that contains only the exceptions from the current group that match *condition*, or ``None`` if the result is empty. The condition can be either a function that accepts an exception and returns true for those that should be in the subgroup, or it can be an exception type or a tuple of exception types, which is used to check for a match using the same check that is used in an ``except`` clause.sobolevn commented on Nov 17, 2022
The same happens with .split:
sobolevn commented on Nov 17, 2022
Ok, this is quite tricky. This function is firstly called with ExceptionGroup itself.
Then it is called with exceptions only if the first call returned False. If the first call returns True, it is not called anymore.
AlexWaygood commented on Nov 23, 2022
Has this been resolved by #9230, or is there more still to do?
sobolevn commented on Nov 23, 2022
I think so! (I cannot close issues, please send help 😆 )
Thanks, @Zac-HD, for the report!
AlexWaygood commented on Nov 23, 2022
Yes, thanks @Zac-HD for the report and thanks @sobolevn for the fix! If there's any further problems, please feel free to report them!
Zac-HD commented on Nov 23, 2022
Happy to help, and I will!