You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
BaseExceptionGroup should return ExceptionGroup if initialized with non-base exceptions #12972
When initializing a BaseExceptionGroup with non-base exceptions the stdlib (and the backport) will in fact return an ExceptionGroup. The typing in neither of typeshed nor the backport currently supports this.
$ python foo.py
Runtime type is 'ExceptionGroup'
$ mypy foo.py
foo.py:30: note: Revealed type is "builtins.BaseExceptionGroup[builtins.ValueError]"
$ pyright foo.py
foo.py
foo.py:30:13 - information: Type of "x" is "BaseExceptionGroup[ValueError]"
I have vague recollections that trying to do this was hard-to-impossible, but I currently cannot find any related issues.
At a glance it seems like an overloaded __new__ might work, similar to what's done with some of the other BaseExceptionGroup methods. Though given how special __new__ is, I suspect there may be some unique complications
(cc @sobolevn as the author of the current BaseExceptionGroup overloads)
I tried it out myself, and managed to repro the limitations*. Though I personally feel like getting BaseExceptionGroup -> ExceptionGroup is perhaps more important than custom BaseExceptionGroup subclasses; especially as end users can work around typing issues from custom BaseExceptionGroup with explicit type hints and/or adding a __new__ to their subclass.
Though I also hit python/mypy#17251 so we'd need to remove the __init__, and I'm not sure what the downsides of that would be.
* It's only a limitation for mypy, pyright handles this (somewhat minified) repro perfectly. Mypy falls back to defaults and raises some errors on the stub itself.
from __future__ importannotationsfromtypingimportGeneric, TypeVar, overload, Selffromtyping_extensionsimportreveal_type_BaseExceptionT_co=TypeVar("_BaseExceptionT_co", bound=BaseException, covariant=True, default=BaseException)
_ExceptionT_co=TypeVar("_ExceptionT_co", bound=Exception, covariant=True, default=Exception)
classBaseExceptionGroup(Generic[_BaseExceptionT_co]):
@overload# mypy: Self argument missing for a non-static method (or an invalid type for self)def__new__( # type: ignore[misc]cls: ExceptionGroup[_ExceptionT_co], _exception: _ExceptionT_co, /
) ->ExceptionGroup[_ExceptionT_co]: ...
@overloaddef__new__(cls, _exception: _BaseExceptionT_co, /) ->Self: ...
# mypy: "__new__" must return a class instancedef__new__( # type: ignore[misc]cls, _exception: _ExceptionT_co|_BaseExceptionT_co
) ->Self|ExceptionGroup[_ExceptionT_co]:
returnobject.__new__(cls)
classExceptionGroup(BaseExceptionGroup[_ExceptionT_co]):
def__new__(cls, _exception: _ExceptionT_co, /) ->Self:
returnobject.__new__(cls)
classMyBaseExcGroup(BaseExceptionGroup[_BaseExceptionT_co]): ...
classMyExcGroup(ExceptionGroup[_ExceptionT_co]): ...
reveal_type(BaseExceptionGroup(ValueError()))
reveal_type(BaseExceptionGroup(SystemExit()))
reveal_type(MyBaseExcGroup(ValueError())) # mypy reverts to BaseException defaultreveal_type(MyBaseExcGroup(SystemExit()))
reveal_type(ExceptionGroup(ValueError()))
reveal_type(MyExcGroup(ValueError()))
# expected errorsExceptionGroup(SystemExit()) # type: ignore[type-var] # pyright: ignore[reportArgumentType]MyExcGroup(SystemExit()) # type: ignore[type-var] # pyright: ignore[reportArgumentType]
Activity
brianschubert commentedon Nov 8, 2024
Somewhat related: #9922
At a glance it seems like an overloaded
__new__might work, similar to what's done with some of the otherBaseExceptionGroupmethods. Though given how special__new__is, I suspect there may be some unique complications(cc @sobolevn as the author of the current
BaseExceptionGroupoverloads)jakkdl commentedon Jan 28, 2025
Oh, found a comment in the test file noticing it as a limitation
typeshed/stdlib/@tests/test_cases/builtins/check_exception_group-py311.py
Lines 24 to 26 in aa992b9
and found the accompanying discussion #9230 (comment) where @sobolevn lays out the tradeoffs
I tried it out myself, and managed to repro the limitations*. Though I personally feel like getting
BaseExceptionGroup -> ExceptionGroupis perhaps more important than customBaseExceptionGroupsubclasses; especially as end users can work around typing issues from customBaseExceptionGroupwith explicit type hints and/or adding a__new__to their subclass.Though I also hit python/mypy#17251 so we'd need to remove the
__init__, and I'm not sure what the downsides of that would be.* It's only a limitation for mypy, pyright handles this (somewhat minified) repro perfectly. Mypy falls back to defaults and raises some errors on the stub itself.
srittau commentedon Feb 26, 2025
If someone could provide an exploratory PR, we could see the impact of such a change.