-
Notifications
You must be signed in to change notification settings - Fork 19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Document how to print an exceptiongroup traceback when the monkey patching doesn't work / is not applied. #14
Comments
You would have to patch the library that overrode the exception hook or |
I'm thinking this could be done by subclassing |
Would the linked PR solve the problem for you? |
Sorry for the wall of text. The TLDR is no, I don't think it solves the problem if I understood correctly how to use Here is the long answer Minimal reproducible exampleI've found a minimal reproducible example for the original issue I had originally. You'll need to install the following:
NotesI am sure my original issue wasn't due to trio because it was not in Case 1: Working exampleSo here it is. First the "working" example (i.e. the traceback as it is supposed to be). We define an from attrs import define
from cattrs.preconf.json import make_converter as json_converter
@define
class Test:
a: int
b: int
converter = json_converter()
def main():
s = '{"a": "foo", "b": "bar"}'
print(converter.loads(s, Test))
if __name__ == "__main__":
main() Executing the above script saved in
Case 2: Adding trio firstNow we add trio as the first import in our import trio
from attrs import define
from cattrs.preconf.json import make_converter as json_converter
@define
class Test:
a: int
b: int
converter = json_converter()
def main():
s = '{"a": "foo", "b": "bar"}'
print(converter.loads(s, Test))
if __name__ == "__main__":
main() Executing it:
The nice tree traceback is gone. We only get the top level error Case 3: adding trio lastThird test, putting trio import last. from attrs import define
from cattrs.preconf.json import make_converter as json_converter
import trio
@define
class Test:
a: int
b: int
converter = json_converter()
def main():
s = '{"a": "foo", "b": "bar"}'
print(converter.loads(s, Test))
if __name__ == "__main__":
main() output:
Now this one is worse, because the default handling of Current state of affairsThis kind of Mitigation proposed in PR #21Now we try to see if the mitigation proposed in PR #21 can work: Case 2 modified:We protect the failing line in a import trio
from attrs import define
from cattrs.preconf.json import make_converter as json_converter
from cattrs.errors import ClassValidationError
from exceptiongroup import format_exception
@define
class Test:
a: int
b: int
converter = json_converter()
def main():
s = '{"a": "foo", "b": "bar"}'
try:
print(converter.loads(s, Test))
except ClassValidationError as e:
format_exception(e)
if __name__ == "__main__":
main() output:
Unfortunately, it doesn't produce the expected results. It is even worse and we are like case 3, but inverted. Case 3 modified:As in case 3, trio is imported last, and the rest of the modifications are like from attrs import define
from cattrs.preconf.json import make_converter as json_converter
from cattrs.errors import ClassValidationError
from exceptiongroup import format_exception
import trio
@define
class Test:
a: int
b: int
converter = json_converter()
def main():
s = '{"a": "foo", "b": "bar"}'
try:
print(converter.loads(s, Test))
except ClassValidationError as e:
format_exception(e)
if __name__ == "__main__":
main() output:
This one is the worst of all. We have two times the ConclusionI did not dive in the code really, but as an outsider user, this seems a little bit worrying that once you have two monkeypatching libraries for The other worry, is that once the excepthook broken, there don't seem to be any way to "unbroken" it. At least it's only broken for |
There are two observations to be made here:
Finally, I'll ask: what exactly would you expect me to do in this situation (besides fixing the bug mentioned above)? |
Thanks,
Oooops sorry, here is the amended script that would solve my problem in a meaningful way I think: import trio
import sys
from attrs import define
from cattrs.preconf.json import make_converter as json_converter
from cattrs.errors import ClassValidationError
from exceptiongroup import format_exception
@define
class Test:
a: int
b: int
converter = json_converter()
def main():
s = '{"a": "foo", "b": "bar"}'
try:
print(converter.loads(s, Test))
except ClassValidationError as e:
print("".join(format_exception(e)))
sys.exit(1)
if __name__ == "__main__":
main() And the output would be ideally the same as in case 1:
|
I was able to reproduce the earlier error even without trio. I think it's fixable. |
Confirmed; I committed the fix and an accompanying test to the PR branch. |
I launched my test cases again with you latest fix, it solved one of them, but not all This one works (trio imported last): import sys
from attrs import define
from cattrs.preconf.json import make_converter as json_converter
from cattrs.errors import ClassValidationError
from exceptiongroup import format_exception, catch, ExceptionGroup
import trio
@define
class Test:
a: int
b: int
converter = json_converter()
def err_handler(excgroup: ExceptionGroup) -> None:
for exc in excgroup.exceptions:
print(f"Caught exception: \n{''.join(format_exception(exc))}")
def main():
s = '{"a": "foo", "b": "bar"}'
try:
print(converter.loads(s, Test))
except ClassValidationError as e:
print("".join(format_exception(e)))
sys.exit(1)
if __name__ == "__main__":
main() Output:
This one does not work (trio imported first): import trio
import sys
from attrs import define
from cattrs.preconf.json import make_converter as json_converter
from cattrs.errors import ClassValidationError
from exceptiongroup import format_exception, catch, ExceptionGroup
@define
class Test:
a: int
b: int
converter = json_converter()
def err_handler(excgroup: ExceptionGroup) -> None:
for exc in excgroup.exceptions:
print(f"Caught exception: \n{''.join(format_exception(exc))}")
def main():
s = '{"a": "foo", "b": "bar"}'
try:
print(converter.loads(s, Test))
except ClassValidationError as e:
print("".join(format_exception(e)))
sys.exit(1)
if __name__ == "__main__":
main() will produce this output:
|
For me it was the other way around. The one where trio is imported last crashed with |
The problem stems from I managed to fix this on my end. Give it a try please. |
Yes, sorry about that, you are right, my observations were as you said, but I copy pasted the output with their wrong code inputs.
I confirm it works! 🎉 Many thanks. |
First, thanks for working on this project.
I updated to the latest
cattr 22.1.0
from @Tinche who started to use theexceptiongroup
backport to report various errors occurring while "structuring" objects. All fine.Unfortunately, I think I have an installed package that messes with the monkeypatching of the traceback formatting so when I run my buggy object structuring which fails, I get this console output:
When I run this same code in an virtualenv with less packages, I get the traceback "trees" akin to the ones shown here: python-attrs/cattrs#258 (comment)
I tried for some time to try to force the
exceptiongroup
exception formatting and it resulted in different form attribute errors or the likes deep in theexceptiongroup
code which makes me think there are 2 monkey patching at play.My question: is there anyway to force the display of this exceptiongroup formatting even if someone else elsewhere monkeypatched
traceback.TracebackException
? All my incantations involvingtraceback
stdlib module andexceptiongroup._formatting
failed miserably.The text was updated successfully, but these errors were encountered: