Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork32.4k
bpo-36829: Add test.support.catch_unraisable_exception()#13490
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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Conversation
* Copy test_exceptions.test_unraisable() to test_sys.UnraisableHookTest().* test_exceptions.test_unraisable() uses catch_unraisable_exception(); simplify the test. test_sys now checks the exact output.* Use catch_unraisable_exception() in test_coroutines, test_exceptions, test_generators.
I chose to leave test_io unchanged on purpose: I have a local branch which fix alsohttps://bugs.python.org/issue36918 in Lib/_pyio.pyand use this new catch_unraisable_exception() function. Once this PR will be merged, I will write a second PR based on it. |
self.assertEqual(repr(cm.unraisable.object), coro_repr) | ||
self.assertEqual(cm.unraisable.exc_type, ZeroDivisionError) | ||
self.assertIn("was never awaited", stream.getvalue()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
It's a little bit strange to have to catch half of the error message from stderr, and the other half from sys.unraisablehook. My PR#13488 will allow to inject the error message in the unraisable exception to catch both at the same time.
Lib/test/support/__init__.py Outdated
# check the expected unraisable exception | ||
... | ||
finally: |
graingertMay 22, 2019 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
this try/with/finally is somewhat tricky, how about:
try:withsupport.throw_unraisable_exceptions(): ...exceptExceptionase: ...# the exception is now here
eg:
@contextlib.contextmanagerdefthrow_unraisable_exceptions():unraisable=Noneold_hook=sys.unraisablehookdefhook(exc):nonlocalunraisableunraisable=excsys.unraisablehook=hooktry:yieldifunraisableisnotNone:raiseunraisablefinally:unraisable=Nonesys.unraisablehook=old_hook
graingertMay 22, 2019 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
or recommend usage like this:
withsupport.catch_unraisable_exceptions()ascm: ...cm.unraisable# only available hereassertcm.unraisableisNone# now it's gone
by updating__exit__
:
def__exit__(self,*exc_info):self.unraisable=None# clear unraisable heresys.unraisablehook=self._old_hook
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Good idea :-) I implemented your__exit__
idea to avoid the need for try/finally.
throw_unraisable_exceptions() might be useful, but modified tests needs to access the 'obj' attribute of the unraisable hook. Later, they might also want to get access to the 'err_msg' attribute:#13488
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
There's still a sharp edge here:
how about makingcm.unraisable
throw if accessed outside the context instead of being None, eg:
def __exit__(self, *exc_info): del self.unraisable sys.unraisablehook = self._old_hook
graingertMay 22, 2019 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
and if you want to access additional unraisablehook info using throw_unraisable_exceptions:
classUnraisableException(Exception):def__init__(self,unraisable):self.unraisable=unraisablesuper().__init__(self,unraisable)@contextlib.contextmanagerdefthrow_unraisable_exceptions():unraisable=Noneold_hook=sys.unraisablehookdefhook(unraisable_):nonlocalunraisableunraisable=unraisable_sys.unraisablehook=hooktry:yieldifunraisableisnotNone:raiseUnraisableException(unraisable)fromunraisable.exc_valuefinally:sys.unraisablehook=old_hook
then you can use it with:
try:withthrow_unraisable_exceptions(): ...exceptUnraisableExceptionase:print(repr(e.__cause__))err_msg=e.unraisable.err_msg
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
I wrote PR#13554 to implement your "del self.unraisable" idea.
Avoid the need for try/finally: __exit__ clears unraisable to breakthe reference cycle.
Uh oh!
There was an error while loading.Please reload this page.
test_sys.UnraisableHookTest().
simplify the test. test_sys now checks the exact output.
test_exceptions, test_generators.
https://bugs.python.org/issue36829