PEP-654: allow subclassing of exception groups (#1872)
This commit is contained in:
parent
96c1a37657
commit
e31aab38ed
72
pep-0654.rst
72
pep-0654.rst
|
@ -156,14 +156,13 @@ use the class name. For brevity, we will use ``ExceptionGroup`` in code
|
|||
examples that are relevant to both.
|
||||
|
||||
Since an exception group can be nested, it represents a tree of exceptions,
|
||||
where the leaves are plain exceptions and each internal node represent a time
|
||||
where the leaves are plain exceptions and each internal node represents a time
|
||||
at which the program grouped some unrelated exceptions into a new group and
|
||||
raised them together. The ``ExceptionGroup`` and ``BaseExceptionGroup``
|
||||
classes are final, i.e., they cannot be further subclassed.
|
||||
raised them together.
|
||||
|
||||
The ``BaseExceptionGroup.subgroup(condition)`` method gives us a way to obtain
|
||||
an exception group that has the same metadata (cause, context, traceback) as
|
||||
the original group, and the same nested structure of groups, but
|
||||
an exception group that has the same metadata (message, cause, context,
|
||||
traceback) as the original group, and the same nested structure of groups, but
|
||||
contains only those exceptions for which the condition is true:
|
||||
|
||||
.. code-block::
|
||||
|
@ -267,6 +266,69 @@ as a shorthand for matching that type: ``eg.split(T)`` divides ``eg`` into the
|
|||
subgroup of leaf exceptions that match the type ``T``, and the subgroup of those
|
||||
that do not (using the same check as ``except`` for a match).
|
||||
|
||||
Subclassing Exception Groups
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
It is possible to subclass exception groups, but when doing that it is
|
||||
usually necessary to specify how ``subgroup()`` and ``split()`` should
|
||||
create new instances for the matching or non-matching part of the partition.
|
||||
``BaseExceptionGroup`` exposes an instance method ``derive(self, excs)``
|
||||
which is called whenever ``subgroup`` and ``split`` need to create a new
|
||||
exception group. The parameter ``excs`` is the sequence of exceptions to
|
||||
include in the new group. Since ``derive`` has access to self, it can
|
||||
copy data from it to the new object. For example, if we need an exception
|
||||
group subclass that has an additional error code field, we can do this:
|
||||
|
||||
.. code-block::
|
||||
|
||||
class MyExceptionGroup(ExceptionGroup):
|
||||
def __new__(cls, message, excs, errcode):
|
||||
obj = super().__new__(cls, message, excs)
|
||||
obj.errcode = errcode
|
||||
return obj
|
||||
|
||||
def derive(self, excs):
|
||||
return MyExceptionGroup(self.message, excs, self.errcode)
|
||||
|
||||
|
||||
Note that we override ``__new__`` rather than ``__init__``; this is because
|
||||
``BaseExceptionGroup.__new__`` needs to inspect the constructor arguments, and
|
||||
its signature is different from that of the subclass. Note also that our
|
||||
``derive`` function does not copy the ``__context__``, ``__cause__`` and
|
||||
``__traceback__`` fields, because ``subgroup`` and ``split`` do that for us.
|
||||
|
||||
With the class defined above, we have the following:
|
||||
|
||||
.. code-block::
|
||||
|
||||
>>> eg = MyExceptionGroup("eg", [TypeError(1), ValueError(2)], 42)
|
||||
>>>
|
||||
>>> match, rest = eg.split(ValueError)
|
||||
>>> print(f'match: {match!r}: {match.errcode}')
|
||||
match: MyExceptionGroup('eg', [ValueError(2)], 42): 42
|
||||
>>> print(f'rest: {rest!r}: {rest.errcode}')
|
||||
rest: MyExceptionGroup('eg', [TypeError(1)], 42): 42
|
||||
>>>
|
||||
|
||||
If we do not override ``derive``, then split calls the one defined
|
||||
on ``BaseExceptionGroup``, which returns an instance of ``ExceptionGroup``
|
||||
if all contained exceptions are of type ``Exception``, and
|
||||
``BaseExceptionGroup`` otherwise. For example:
|
||||
|
||||
.. code-block::
|
||||
|
||||
>>> class MyExceptionGroup(BaseExceptionGroup):
|
||||
... pass
|
||||
...
|
||||
>>> eg = MyExceptionGroup("eg", [ValueError(1), KeyboardInterrupt(2)])
|
||||
>>> match, rest = eg.split(ValueError)
|
||||
>>> print(f'match: {match!r}')
|
||||
match: ExceptionGroup('eg', [ValueError(1)])
|
||||
>>> print(f'rest: {rest!r}')
|
||||
rest: BaseExceptionGroup('eg', [KeyboardInterrupt(2)])
|
||||
>>>
|
||||
|
||||
|
||||
The Traceback of an Exception Group
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
|
Loading…
Reference in New Issue