mirror of
https://github.com/correl/SleekXMPP.git
synced 2024-11-27 19:19:54 +00:00
XMPPError exceptions can keep a stanza's contents.
This allows exceptions to include the original content of a stanza in the error response by including the parameter clear=False when raising the exception.
This commit is contained in:
parent
c4b1212c44
commit
0d32638379
7 changed files with 71 additions and 12 deletions
|
@ -21,7 +21,8 @@ class XMPPError(Exception):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, condition='undefined-condition', text=None, etype=None,
|
def __init__(self, condition='undefined-condition', text=None, etype=None,
|
||||||
extension=None, extension_ns=None, extension_args=None):
|
extension=None, extension_ns=None, extension_args=None,
|
||||||
|
clear=True):
|
||||||
"""
|
"""
|
||||||
Create a new XMPPError exception.
|
Create a new XMPPError exception.
|
||||||
|
|
||||||
|
@ -37,6 +38,9 @@ class XMPPError(Exception):
|
||||||
extension_args -- Content and attributes for the extension
|
extension_args -- Content and attributes for the extension
|
||||||
element. Same as the additional arguments to
|
element. Same as the additional arguments to
|
||||||
the ET.Element constructor.
|
the ET.Element constructor.
|
||||||
|
clear -- Indicates if the stanza's contents should be
|
||||||
|
removed before replying with an error.
|
||||||
|
Defaults to True.
|
||||||
"""
|
"""
|
||||||
if extension_args is None:
|
if extension_args is None:
|
||||||
extension_args = {}
|
extension_args = {}
|
||||||
|
@ -44,6 +48,7 @@ class XMPPError(Exception):
|
||||||
self.condition = condition
|
self.condition = condition
|
||||||
self.text = text
|
self.text = text
|
||||||
self.etype = etype
|
self.etype = etype
|
||||||
|
self.clear = clear
|
||||||
self.extension = extension
|
self.extension = extension
|
||||||
self.extension_ns = extension_ns
|
self.extension_ns = extension_ns
|
||||||
self.extension_args = extension_args
|
self.extension_args = extension_args
|
||||||
|
|
|
@ -144,7 +144,7 @@ class Iq(RootStanza):
|
||||||
self.xml.remove(child)
|
self.xml.remove(child)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def reply(self):
|
def reply(self, clear=True):
|
||||||
"""
|
"""
|
||||||
Send a reply <iq> stanza.
|
Send a reply <iq> stanza.
|
||||||
|
|
||||||
|
@ -152,9 +152,13 @@ class Iq(RootStanza):
|
||||||
|
|
||||||
Sets the 'type' to 'result' in addition to the default
|
Sets the 'type' to 'result' in addition to the default
|
||||||
StanzaBase.reply behavior.
|
StanzaBase.reply behavior.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
clear -- Indicates if existing content should be
|
||||||
|
removed before replying. Defaults to True.
|
||||||
"""
|
"""
|
||||||
self['type'] = 'result'
|
self['type'] = 'result'
|
||||||
StanzaBase.reply(self)
|
StanzaBase.reply(self, clear)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def send(self, block=True, timeout=None, callback=None):
|
def send(self, block=True, timeout=None, callback=None):
|
||||||
|
|
|
@ -104,7 +104,7 @@ class Message(RootStanza):
|
||||||
self['type'] = 'normal'
|
self['type'] = 'normal'
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def reply(self, body=None):
|
def reply(self, body=None, clear=True):
|
||||||
"""
|
"""
|
||||||
Create a message reply.
|
Create a message reply.
|
||||||
|
|
||||||
|
@ -114,7 +114,9 @@ class Message(RootStanza):
|
||||||
adds a message body if one is given.
|
adds a message body if one is given.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
body -- Optional text content for the message.
|
body -- Optional text content for the message.
|
||||||
|
clear -- Indicates if existing content should be removed
|
||||||
|
before replying. Defaults to True.
|
||||||
"""
|
"""
|
||||||
StanzaBase.reply(self)
|
StanzaBase.reply(self)
|
||||||
if self['type'] == 'groupchat':
|
if self['type'] == 'groupchat':
|
||||||
|
|
|
@ -173,14 +173,18 @@ class Presence(RootStanza):
|
||||||
# The priority is not a number: we consider it 0 as a default
|
# The priority is not a number: we consider it 0 as a default
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def reply(self):
|
def reply(self, clear=True):
|
||||||
"""
|
"""
|
||||||
Set the appropriate presence reply type.
|
Set the appropriate presence reply type.
|
||||||
|
|
||||||
Overrides StanzaBase.reply.
|
Overrides StanzaBase.reply.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
clear -- Indicates if the stanza contents should be removed
|
||||||
|
before replying. Defaults to True.
|
||||||
"""
|
"""
|
||||||
if self['type'] == 'unsubscribe':
|
if self['type'] == 'unsubscribe':
|
||||||
self['type'] = 'unsubscribed'
|
self['type'] = 'unsubscribed'
|
||||||
elif self['type'] == 'subscribe':
|
elif self['type'] == 'subscribe':
|
||||||
self['type'] = 'subscribed'
|
self['type'] = 'subscribed'
|
||||||
return StanzaBase.reply(self)
|
return StanzaBase.reply(self, clear)
|
||||||
|
|
|
@ -43,8 +43,8 @@ class RootStanza(StanzaBase):
|
||||||
Arguments:
|
Arguments:
|
||||||
e -- Exception object
|
e -- Exception object
|
||||||
"""
|
"""
|
||||||
self.reply()
|
|
||||||
if isinstance(e, XMPPError):
|
if isinstance(e, XMPPError):
|
||||||
|
self.reply(clear=e.clear)
|
||||||
# We raised this deliberately
|
# We raised this deliberately
|
||||||
self['error']['condition'] = e.condition
|
self['error']['condition'] = e.condition
|
||||||
self['error']['text'] = e.text
|
self['error']['text'] = e.text
|
||||||
|
@ -56,6 +56,7 @@ class RootStanza(StanzaBase):
|
||||||
self['error']['type'] = e.etype
|
self['error']['type'] = e.etype
|
||||||
self.send()
|
self.send()
|
||||||
else:
|
else:
|
||||||
|
self.reply()
|
||||||
# We probably didn't raise this on purpose, so send an error stanza
|
# We probably didn't raise this on purpose, so send an error stanza
|
||||||
self['error']['condition'] = 'undefined-condition'
|
self['error']['condition'] = 'undefined-condition'
|
||||||
self['error']['text'] = "SleekXMPP got into trouble."
|
self['error']['text'] = "SleekXMPP got into trouble."
|
||||||
|
|
|
@ -1161,12 +1161,17 @@ class StanzaBase(ElementBase):
|
||||||
self.clear()
|
self.clear()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def reply(self):
|
def reply(self, clear=True):
|
||||||
"""
|
"""
|
||||||
Reset the stanza and swap its 'from' and 'to' attributes to prepare
|
Swap the 'from' and 'to' attributes to prepare the stanza for
|
||||||
for sending a reply stanza.
|
sending a reply. If clear=True, then also remove the stanza's
|
||||||
|
contents to make room for the reply content.
|
||||||
|
|
||||||
For client streams, the 'from' attribute is removed.
|
For client streams, the 'from' attribute is removed.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
clear -- Indicates if the stanza's contents should be
|
||||||
|
removed. Defaults to True
|
||||||
"""
|
"""
|
||||||
# if it's a component, use from
|
# if it's a component, use from
|
||||||
if self.stream and hasattr(self.stream, "is_component") and \
|
if self.stream and hasattr(self.stream, "is_component") and \
|
||||||
|
@ -1175,7 +1180,8 @@ class StanzaBase(ElementBase):
|
||||||
else:
|
else:
|
||||||
self['to'] = self['from']
|
self['to'] = self['from']
|
||||||
del self['from']
|
del self['from']
|
||||||
self.clear()
|
if clear:
|
||||||
|
self.clear()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def error(self):
|
def error(self):
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import sys
|
import sys
|
||||||
import sleekxmpp
|
import sleekxmpp
|
||||||
|
from sleekxmpp.xmlstream.matcher import MatchXPath
|
||||||
|
from sleekxmpp.xmlstream.handler import Callback
|
||||||
from sleekxmpp.exceptions import XMPPError
|
from sleekxmpp.exceptions import XMPPError
|
||||||
from sleekxmpp.test import *
|
from sleekxmpp.test import *
|
||||||
|
|
||||||
|
@ -46,6 +48,41 @@ class TestStreamExceptions(SleekTest):
|
||||||
</message>
|
</message>
|
||||||
""", use_values=False)
|
""", use_values=False)
|
||||||
|
|
||||||
|
def testIqErrorException(self):
|
||||||
|
"""Test using error exceptions with Iq stanzas."""
|
||||||
|
|
||||||
|
def handle_iq(iq):
|
||||||
|
raise XMPPError(condition='feature-not-implemented',
|
||||||
|
text="We don't do things that way here.",
|
||||||
|
etype='cancel',
|
||||||
|
clear=False)
|
||||||
|
|
||||||
|
self.stream_start()
|
||||||
|
self.xmpp.register_handler(
|
||||||
|
Callback(
|
||||||
|
'Test Iq',
|
||||||
|
MatchXPath('{%s}iq/{test}query' % self.xmpp.default_ns),
|
||||||
|
handle_iq))
|
||||||
|
|
||||||
|
self.recv("""
|
||||||
|
<iq type="get" id="0">
|
||||||
|
<query xmlns="test" />
|
||||||
|
</iq>
|
||||||
|
""")
|
||||||
|
|
||||||
|
self.send("""
|
||||||
|
<iq type="error" id="0">
|
||||||
|
<query xmlns="test" />
|
||||||
|
<error type="cancel">
|
||||||
|
<feature-not-implemented
|
||||||
|
xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" />
|
||||||
|
<text xmlns="urn:ietf:params:xml:ns:xmpp-stanzas">
|
||||||
|
We don't do things that way here.
|
||||||
|
</text>
|
||||||
|
</error>
|
||||||
|
</iq>
|
||||||
|
""", use_values=False)
|
||||||
|
|
||||||
def testThreadedXMPPErrorException(self):
|
def testThreadedXMPPErrorException(self):
|
||||||
"""Test raising an XMPPError exception in a threaded handler."""
|
"""Test raising an XMPPError exception in a threaded handler."""
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue