From 5bdcd9ef9d74d7921f5579086e6c6d94c0ac7deb Mon Sep 17 00:00:00 2001 From: Lance Stout Date: Mon, 25 Oct 2010 15:09:56 -0400 Subject: Made exceptions work. Raising an XMPPError exception from an event handler now works, even if from a threaded handler. Added stream tests to verify. We should start using XMPPError, it really makes things simple! --- sleekxmpp/exceptions.py | 3 ++ sleekxmpp/xmlstream/xmlstream.py | 31 +++++++++-- tests/test_stream_exceptions.py | 110 +++++++++++++++++++++++++++++++++++++++ tests/test_stream_presence.py | 11 ++-- 4 files changed, 145 insertions(+), 10 deletions(-) create mode 100644 tests/test_stream_exceptions.py diff --git a/sleekxmpp/exceptions.py b/sleekxmpp/exceptions.py index 980c6b6e..d3988b4a 100644 --- a/sleekxmpp/exceptions.py +++ b/sleekxmpp/exceptions.py @@ -38,6 +38,9 @@ class XMPPError(Exception): element. Same as the additional arguments to the ET.Element constructor. """ + if extension_args is None: + extension_args = {} + self.condition = condition self.text = text self.etype = etype diff --git a/sleekxmpp/xmlstream/xmlstream.py b/sleekxmpp/xmlstream/xmlstream.py index e11497f2..ace93cc3 100644 --- a/sleekxmpp/xmlstream/xmlstream.py +++ b/sleekxmpp/xmlstream/xmlstream.py @@ -786,6 +786,23 @@ class XMLStream(object): if unhandled: stanza.unhandled() + def _threaded_event_wrapper(self, func, args): + """ + Capture exceptions for event handlers that run + in individual threads. + + Arguments: + func -- The event handler to execute. + args -- Arguments to the event handler. + """ + try: + func(*args) + except Exception as e: + error_msg = 'Error processing event handler: %s' + logging.exception(error_msg % str(func)) + if hasattr(args[0], 'exception'): + args[0].exception(e) + def _event_runner(self): """ Process the event queue and execute handlers. @@ -825,14 +842,18 @@ class XMLStream(object): func, threaded, disposable = handler try: if threaded: - x = threading.Thread(name="Event_%s" % str(func), - target=func, - args=args) + x = threading.Thread( + name="Event_%s" % str(func), + target=self._threaded_event_wrapper, + args=(func, args)) x.start() else: func(*args) - except: - logging.exception('Error processing event handler: %s') + except Exception as e: + error_msg = 'Error processing event handler: %s' + logging.exception(error_msg % str(func)) + if hasattr(args[0], 'exception'): + args[0].exception(e) elif etype == 'quit': logging.debug("Quitting event runner thread") return False diff --git a/tests/test_stream_exceptions.py b/tests/test_stream_exceptions.py new file mode 100644 index 00000000..f788a3a4 --- /dev/null +++ b/tests/test_stream_exceptions.py @@ -0,0 +1,110 @@ +import sys +import sleekxmpp +from sleekxmpp.exceptions import XMPPError +from sleekxmpp.test import * + + +class TestStreamExceptions(SleekTest): + """ + Test handling roster updates. + """ + + def tearDown(self): + self.stream_close() + + def testXMPPErrorException(self): + """Test raising an XMPPError exception.""" + + def message(msg): + raise XMPPError(condition='feature-not-implemented', + text="We don't do things that way here.", + etype='cancel', + extension='foo', + extension_ns='foo:error', + extension_args={'test': 'true'}) + + self.stream_start() + self.xmpp.add_event_handler('message', message) + + self.stream_recv(""" + + This is going to cause an error. + + """) + + self.stream_send_message(""" + + + + + We don't do things that way here. + + + + + """, use_values=False) + + def testThreadedXMPPErrorException(self): + """Test raising an XMPPError exception in a threaded handler.""" + + def message(msg): + raise XMPPError(condition='feature-not-implemented', + text="We don't do things that way here.", + etype='cancel') + + self.stream_start() + self.xmpp.add_event_handler('message', message, + threaded=True) + + self.stream_recv(""" + + This is going to cause an error. + + """) + + self.stream_send_message(""" + + + + + We don't do things that way here. + + + + """) + + def testUnknownException(self): + """Test raising an generic exception in a threaded handler.""" + + def message(msg): + raise ValueError("Did something wrong") + + self.stream_start() + self.xmpp.add_event_handler('message', message) + + self.stream_recv(""" + + This is going to cause an error. + + """) + + if sys.version_info < (3, 0): + self.stream_send_message(""" + + + + + SleekXMPP got into trouble. + + + + """) + else: + # Unfortunately, tracebacks do not make for very portable tests. + pass + + +suite = unittest.TestLoader().loadTestsFromTestCase(TestStreamExceptions) diff --git a/tests/test_stream_presence.py b/tests/test_stream_presence.py index c5d5cec2..ca67f1df 100644 --- a/tests/test_stream_presence.py +++ b/tests/test_stream_presence.py @@ -42,6 +42,7 @@ class TestStreamPresence(SleekTest): def testGotOffline(self): """Test that got_offline is triggered properly.""" events = [] + def got_offline(presence): events.append('got_offline') @@ -124,7 +125,7 @@ class TestStreamPresence(SleekTest): self.stream_start(jid='tester@localhost') - self.xmpp.add_event_handler('changed_subscription', + self.xmpp.add_event_handler('changed_subscription', changed_subscription) self.xmpp.add_event_handler('presence_subscribe', presence_subscribe) @@ -147,7 +148,7 @@ class TestStreamPresence(SleekTest): """) expected = set(('presence_subscribe', 'changed_subscription')) - self.assertEqual(events, expected, + self.assertEqual(events, expected, "Incorrect events triggered: %s" % events) def testNoAutoAuthorize(self): @@ -160,10 +161,10 @@ class TestStreamPresence(SleekTest): def changed_subscription(p): events.add('changed_subscription') - + self.stream_start(jid='tester@localhost') - self.xmpp.add_event_handler('changed_subscription', + self.xmpp.add_event_handler('changed_subscription', changed_subscription) self.xmpp.add_event_handler('presence_subscribe', presence_subscribe) @@ -180,7 +181,7 @@ class TestStreamPresence(SleekTest): """) expected = set(('presence_subscribe', 'changed_subscription')) - self.assertEqual(events, expected, + self.assertEqual(events, expected, "Incorrect events triggered: %s" % events) -- cgit v1.2.3