-
Notifications
You must be signed in to change notification settings - Fork 24
Unhandled Exceptions #4
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
Comments
Thanks for your bug report. I don't use python anymore and therefore won't take care of the issue. Maybe I can move the project to the new pytest organization. |
In your case deferred won't be called, and I there is needed some kind of timeout, like in https://github.com/eugeniy/pytest-tornado Unhandled errors should be reported too, I think. |
Its possibly enough just for pytest-twisted to add an errback handler to every deferred it sees...but I didnt try that yet. |
but this won't help with deferreds that haven't been called. I am not sure if I'm getting it right, but in your case the best solution will be deferLater from twisted.internet import reactor, task
import pytest
@pytest.fixture
def foo():
def blammo():
raise RuntimeError('foo')
d = task.deferLater(reactor, 0.1, blammo)
return pytest.blockon(d)
def test_meaning(foo):
assert foo == 42 and for the pytest-twisted the best solution to have a timeout for deferreds (like trial and pytest-tornado do) |
Well, my example test was meant to illustrate the problem I really encountered which is/was that an unhandled exception wasn't getting logged or printed anywhere. Definitely that's a code problem (i.e. something missing an errback handler) but I filed a ticket in case there's something smart pytest-twisted could do for these cases. So, yes, your fix above is correct -- but in a complex code-base there can easily be hard-to-find Deferreds that are missing appropriate handling (e.g. someone calls a Deferred-returning method as if it was synchronous). |
Yeah, I understand problem now. So there is a possible solution which adds log handler diff --git a/pytest_twisted/plugin.py b/pytest_twisted/plugin.py
index c3f4c73..954ff6b 100644
--- a/pytest_twisted/plugin.py
+++ b/pytest_twisted/plugin.py
@@ -16,6 +16,16 @@ def blockon(d):
if greenlet.getcurrent() is not current:
current.switch(result)
+ def error_observer(eventDict):
+ if eventDict['isError'] and 'failure' in eventDict:
+ if not eventDict.get('why'):
+ failure = eventDict['failure']
+ if greenlet.getcurrent() is not current:
+ current.throw(failure.type, failure.value, failure.tb)
+
+ from twisted.python import log
+ log.addObserver(error_observer)
+
d.addCallbacks(cb, cb)
if not result:
_result = gr_twisted.switch() I'm not really familiar with greenlet, so there might be some edge cases |
Yeah, that looks plausible. I will try against my test-case. |
In case someone wants a copy/paste fixture that asserts on any (noticed) unhandled deferred error, here's what I pieced together from reading here and chatting in #twisted. Noticed as in it banks on https://gist.github.com/altendky/b4929da7f414d8173a4d87fa7d2cd29b import gc
import twisted.logger
class Observer:
def __init__(self):
self.failures = []
def __call__(self, event_dict):
is_error = event_dict.get('isError')
s = 'Unhandled error in Deferred'.casefold()
is_unhandled = s in event_dict.get('log_format', '').casefold()
if is_error and is_unhandled:
self.failures.append(event_dict)
def assert_empty(self):
assert [] == self.failures
@pytest.fixture
def assert_no_unhandled_errbacks():
observer = Observer()
twisted.logger.globalLogPublisher.addObserver(observer)
yield
gc.collect()
twisted.logger.globalLogPublisher.removeObserver(observer)
observer.assert_empty() |
@vtitor, would the above thing be considered useful in a PR with test(s)? @meejah, any chance you've been using it? I just noticed it in my code again and thought 'hey, I should share this...'. I can't say I've been exercising it much though so I was curious if you had any feedback about it 'working' or not in real use. |
Given that it yields nothing, should (entirely untested) def assert_no_unhandled_errbacks(f):
@functools.wraps(f)
def wrapped(*args, **kwargs):
observer = Observer()
twisted.logger.globalLogPublisher.addObserver(observer)
result = f(*args, **kwargs)
gc.collect()
twisted.logger.globalLogPublisher.removeObserver(observer)
observer.assert_empty()
return result
return wrapped |
Hmm, I guess a fixture let's you auto-use... not sure if that's a good approach or not. |
@rschwiebert, that looks to be what I did with it once upon a time. def test_sequence_normal(action_logger, assert_no_unhandled_errbacks): But, it's definitely a 'best effort' sort of situation, not a guaranteed catch. I'll look at #61 now. |
Thanks for the fixture workaround. Using a decorator does not seem to work, but the fixture did. This bug is actually very problematic while developing the test cases, and it requires some serious debugging until you find this ticket and realize where the error is. |
I ran across the following problem: if you have an unhandled exception somewhere in your Twisted stuff (e.g. a missing errback handler), pytest-twisted "hangs" and needs a ctrl-c to stop the test -- at which point you get a traceback into pytest-twisted internals.
Obviously, having a Deferred without an errback is a problem, but if pytest-twisted can do something nicer here, that'd be amazing. (FWIW, _result ends up being None in this case). I don't know enough about greenlets nor pytest to suggest something.
I do have a SSCCE that I detailed in this blog post: https://meejah.ca/pytest-twisted-blockon (and I hereby declare that's Unlicensed if you want any of it).
The text was updated successfully, but these errors were encountered: