Repository navigation
Support Exception-like Objects (TracebackException) For exc.__cause__ and exc.__context__ #111921
Description
Activity
- addedtype-featureA feature request or enhancementA feature request or enhancementinterpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)3.13only security fixesonly security fixes
on Nov 9, 2023 Can we just make
traceback.TracebackExceptionbe anException?I suppose we could. That would certainly be simpler.
ericsnowcurrently commented
on Nov 18, 2023 MemberAuthorMore actionsHaving thought about it, the thing I don't like about subclassing
Exceptionis that then people could start raisingTracebackException, which seems like a strange thing. Things get weird when__cause__,__traceback__, etc. might end up getting overwritten. It would be less of an issue ifTracebackExceptionwere a read-only snapshot. Regardless, maybe mutability and raising isn't such a big deal.Another option: support a new attr (something like
__remote_cause__) for thetracebackmodule to use in cases like subinterpreters where the exception we want to display simply does not exist. The value would be an object that matchesTracebackException's API:.format().format_exception_only().print()
There might be a third option:
Instead of making TracebackException extend Exception, define an exception type that wraps a TracebackException. Then if the wrapper gets raised, it doesn't trample on the data in the wrapped TracebackException.
ericsnowcurrently commented
on Nov 20, 2023 MemberAuthorMore actionsI'm glad you brought this up because I had the same idea. Last week I actually spent some time trying out a dedicated exception that wraps
TracebackException, which I named_DummyException. My conclusion is that it would work but there's something that feels slightly off about it. Mainly, this would require a newTracebackException.__new__()to return the wrapped object, which would mostly mean throwing away the dummy exception's info (e.g. traceback, cause). For my use case that isn't a big deal, but if folks started using the dummy directly then it could easily get confusing. I'm apprehensive about opening that door......which is why I thought of
__remove_cause__. That feels like a very natural, clean, and clear solution. However, it has its own problem: adding a new "dunder" attribute to exceptions, even if only optional, feels like serious overkill.ericsnowcurrently commented
on Nov 20, 2023 MemberAuthorMore actionsJust to summarize, here are the options we've discussed so far:
- support
TracebackException-like non-exception objects in__cause__ - make
TracebackExceptionan actual exception type - wrap the
TracebackExceptionobject with a separate exception type (e.g._DummyException) - like (1), but in a new
__remote_cause__
Each of (1)-(3) has awkward potential corner cases and some awkward conceptual weirdness. I'd strongly favor (4) if it didn't feel so heavy-handed.
- support
ericsnowcurrently commented
on Nov 20, 2023 MemberAuthorMore actionsAnother idea:
Ditch the generalized solution and deal with it via the specific exception (like we do with
ExceptionGroup). That could involve using__notes__or the exception's__repr__().I think
__repr__is better than__notes__, because notes are supposed to be for where the exception needs to be enriched with more information after it was created, and that's not what's happening here.Reacted by Eric Snowericsnowcurrently commented
on Nov 28, 2023 MemberAuthorMore actionsI'm closing this since I don't need it or any of its variants any more.
Feature or enhancement
Currently
__cause__and__context__are expected to be exception objects (i.e. instances ofBaseException). 1 We don't always enforce this expectation, but we definitely do in some places.Proposal
I'd like to allow them to be non-exception objects, as long as the objects look like exceptions. My own interest is that I be able to set either attribute to
traceback.TracebackExceptionobjects specifically. However, I expect that supporting them would in practice mean supporting any object that matches the appropriate drop-in-replacement interface. [^2][^2] See gh-111917.
Note that I am not suggesting that we be able to use such drop-in-replacements anywhere the runtime expects exception objects. You couldn't start raising
TracebackExceptionobjects nor catch them I don't have any motivating case for any of that. I'm only suggesting support in__cause__and__context__.drop-in-replacement for exception objects
The idea of drop-in-replacement (proxy or not) for an exception object isn't mine alone. The implication of the
traceback.TracebackExceptiondocs is that it may be used as a representative proxy of an exception object. Furthermore, there has already been some consideration for treatingTracebackExceptionas a drop-in-replacement in actual technical situations, e.g. when pickling: #73652 (comment).What would need to happen?
How are
__cause__and__context__used (or might be used)?sys.excepthook()What would have to change?
TracebackExceptionappropriately (it is used for the defaultsys.excepthook()2); the diff is actually relatively smallOther Notes
Prior discussion about supporting
traceback.StackSummarywhere we currently support__traceback__: #74764 (comment)Footnotes
This is just like how
__traceback__is currently expected to be aTracebackTypeinstance. ↩Since gh-110721: Use the traceback module for PyErr_Display() and fallback to the C implementation #110702 landed, we can accommodate
TracebackExceptionlike this much more easily. ↩