mirror of
https://github.com/correl/SleekXMPP.git
synced 2024-11-27 19:19:54 +00:00
* major stanza improvements
* raise XMPPError in handler to reply with error stanza * started work on pubsub stanzas
This commit is contained in:
parent
805afa4bc1
commit
093644ffbd
11 changed files with 244 additions and 43 deletions
|
@ -18,6 +18,8 @@
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
"""
|
"""
|
||||||
from __future__ import with_statement
|
from __future__ import with_statement
|
||||||
|
|
||||||
|
|
||||||
from xml.etree import cElementTree as ET
|
from xml.etree import cElementTree as ET
|
||||||
from . xmlstream.xmlstream import XMLStream
|
from . xmlstream.xmlstream import XMLStream
|
||||||
from . xmlstream.matcher.xmlmask import MatchXMLMask
|
from . xmlstream.matcher.xmlmask import MatchXMLMask
|
||||||
|
@ -32,6 +34,7 @@ from . stanza.presence import Presence
|
||||||
from . stanza.roster import Roster
|
from . stanza.roster import Roster
|
||||||
from . stanza.nick import Nick
|
from . stanza.nick import Nick
|
||||||
from . stanza.htmlim import HTMLIM
|
from . stanza.htmlim import HTMLIM
|
||||||
|
from . stanza.error import Error
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import threading
|
import threading
|
||||||
|
@ -91,6 +94,8 @@ class basexmpp(object):
|
||||||
"""Register a plugin not in plugins.__init__.__all__ but in the plugins
|
"""Register a plugin not in plugins.__init__.__all__ but in the plugins
|
||||||
directory."""
|
directory."""
|
||||||
# discover relative "path" to the plugins module from the main app, and import it.
|
# discover relative "path" to the plugins module from the main app, and import it.
|
||||||
|
# TODO:
|
||||||
|
# gross, this probably isn't necessary anymore, especially for an installed module
|
||||||
__import__("%s.%s" % (globals()['plugins'].__name__, plugin))
|
__import__("%s.%s" % (globals()['plugins'].__name__, plugin))
|
||||||
# init the plugin class
|
# init the plugin class
|
||||||
self.plugin[plugin] = getattr(getattr(plugins, plugin), plugin)(self, pconfig) # eek
|
self.plugin[plugin] = getattr(getattr(plugins, plugin), plugin)(self, pconfig) # eek
|
||||||
|
|
8
sleekxmpp/exceptions.py
Normal file
8
sleekxmpp/exceptions.py
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
class XMPPError(Exception):
|
||||||
|
def __init__(self, condition='undefined-condition', text=None, etype=None, extension=None, extension_ns=None, extension_args=None):
|
||||||
|
self.condition = condition
|
||||||
|
self.text = text
|
||||||
|
self.etype = etype
|
||||||
|
self.extension = extension
|
||||||
|
self.extension_ns = extension_ns
|
||||||
|
self.extension_args = extension_args
|
177
sleekxmpp/plugins/stanza_pubsub.py
Normal file
177
sleekxmpp/plugins/stanza_pubsub.py
Normal file
|
@ -0,0 +1,177 @@
|
||||||
|
from .. xmlstream.stanzabase import ElementBase, ET
|
||||||
|
from .. stanza.iq import Iq
|
||||||
|
from .. basexmpp import basexmpp
|
||||||
|
from .. xmlstream.xmlstream import XMLStream
|
||||||
|
from . import xep_0004
|
||||||
|
|
||||||
|
|
||||||
|
def stanzaPlugin(stanza, plugin):
|
||||||
|
stanza.plugin_attrib_map[plugin.plugin_attrib] = plugin
|
||||||
|
stanza.plugin_tag_map["{%s}%s" % (plugin.namespace, plugin.name)] = plugin
|
||||||
|
|
||||||
|
class Pubsub(ElementBase):
|
||||||
|
namespace = 'http://jabber.org/protocol/pubsub'
|
||||||
|
name = 'pubsub'
|
||||||
|
plugin_attrib = 'pubsub'
|
||||||
|
interfaces = set((
|
||||||
|
'create',
|
||||||
|
'configure',
|
||||||
|
'subscribe',
|
||||||
|
'options',
|
||||||
|
'default',
|
||||||
|
'items',
|
||||||
|
'publish',
|
||||||
|
'retract',
|
||||||
|
'subscription',
|
||||||
|
'subscriptions',
|
||||||
|
'unsubscribe',
|
||||||
|
))
|
||||||
|
|
||||||
|
stanzaPlugin(Iq, Pubsub)
|
||||||
|
|
||||||
|
class Affiliations(ElementBase):
|
||||||
|
namespace = 'http://jabber.org/protocol/pubsub'
|
||||||
|
name = 'affiliations'
|
||||||
|
plugin_attrib = 'affiliations'
|
||||||
|
interfaces = set(tuple())
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
ElementBase.__init__(self, *args, **kwargs)
|
||||||
|
self.affiliations = []
|
||||||
|
self.idx = 0
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
self.idx = 0
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __next__(self):
|
||||||
|
self.idx += 1
|
||||||
|
if self.idx + 1 > len(self.affilations):
|
||||||
|
self.idx = 0
|
||||||
|
raise StopIteration
|
||||||
|
return self.affiliations[self.idx]
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self.affiliations)
|
||||||
|
|
||||||
|
def append(self, affiliation):
|
||||||
|
if not isinstance(affiliation, Affiliation):
|
||||||
|
raise TypeError
|
||||||
|
self.xml.append(affiliation.xml)
|
||||||
|
return self.affiliations.append(affiliation)
|
||||||
|
|
||||||
|
def pop(self, idx=0):
|
||||||
|
aff = self.affiliations.pop(idx)
|
||||||
|
self.xml.remove(aff.xml)
|
||||||
|
return aff
|
||||||
|
|
||||||
|
def find(self, affilation):
|
||||||
|
return self.affilations.find(affiliation)
|
||||||
|
|
||||||
|
stanzaPlugin(Pubsub, Affiliations)
|
||||||
|
|
||||||
|
class Affiliation(ElementBase):
|
||||||
|
namespace = 'http://jabber.org/protocol/pubsub'
|
||||||
|
name = 'affiliation'
|
||||||
|
plugin_attrib = name
|
||||||
|
interfaces = set(('node', 'affiliation'))
|
||||||
|
|
||||||
|
class Items(ElementBase):
|
||||||
|
namespace = 'http://jabber.org/protocol/pubsub'
|
||||||
|
name = 'items'
|
||||||
|
plugin_attrib = 'items'
|
||||||
|
interfaces = set(tuple())
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
ElementBase.__init__(self, *args, **kwargs)
|
||||||
|
self.items = []
|
||||||
|
self.idx = 0
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
self.idx = 0
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __next__(self):
|
||||||
|
self.idx += 1
|
||||||
|
if self.idx + 1 > len(self.items):
|
||||||
|
self.idx = 0
|
||||||
|
raise StopIteration
|
||||||
|
return self.items[self.idx]
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self.items)
|
||||||
|
|
||||||
|
def append(self, item):
|
||||||
|
if not isinstance(item, Item):
|
||||||
|
raise TypeError
|
||||||
|
self.xml.append(item.xml)
|
||||||
|
return self.items.append(item)
|
||||||
|
|
||||||
|
def pop(self, idx=0):
|
||||||
|
aff = self.items.pop(idx)
|
||||||
|
self.xml.remove(aff.xml)
|
||||||
|
return aff
|
||||||
|
|
||||||
|
def find(self, item):
|
||||||
|
return self.items.find(item)
|
||||||
|
|
||||||
|
stanzaPlugin(Pubsub, Items)
|
||||||
|
|
||||||
|
class Item(ElementBase):
|
||||||
|
namespace = 'http://jabber.org/protocol/pubsub'
|
||||||
|
name = 'affiliation'
|
||||||
|
plugin_attrib = name
|
||||||
|
interfaces = set(('node', 'affiliation'))
|
||||||
|
class DefaultConfig(ElementBase):
|
||||||
|
namespace = 'http://jabber.org/protocol/pubsub'
|
||||||
|
name = 'default'
|
||||||
|
plugin_attrib = 'defaultconfig'
|
||||||
|
interfaces = set(('node', 'type', 'config'))
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
ElementBase.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
|
def getConfig(self):
|
||||||
|
config = self.xml.find('{jabber:x:data}x')
|
||||||
|
form = xep_0004.Form()
|
||||||
|
if config is not None:
|
||||||
|
form.fromXML(config)
|
||||||
|
return form
|
||||||
|
|
||||||
|
def setConfig(self, value):
|
||||||
|
self.xml.append(value.getXML())
|
||||||
|
return self
|
||||||
|
|
||||||
|
def delConfig(self):
|
||||||
|
config = self.xml.find('{jabber:x:data}x')
|
||||||
|
self.xml.remove(config)
|
||||||
|
|
||||||
|
stanzaPlugin(Pubsub, DefaultConfig)
|
||||||
|
|
||||||
|
iq = Iq()
|
||||||
|
aff1 = Affiliation()
|
||||||
|
aff1['node'] = 'testnode'
|
||||||
|
aff1['affiliation'] = 'owner'
|
||||||
|
aff2 = Affiliation()
|
||||||
|
aff2['node'] = 'testnode2'
|
||||||
|
aff2['affiliation'] = 'publisher'
|
||||||
|
iq['pubsub']['affiliations'].append(aff1)
|
||||||
|
iq['pubsub']['affiliations'].append(aff2)
|
||||||
|
print(iq)
|
||||||
|
iq['pubsub']['affiliations'].pop(0)
|
||||||
|
print(iq)
|
||||||
|
|
||||||
|
iq = Iq()
|
||||||
|
iq['pubsub']['defaultconfig']
|
||||||
|
print(iq)
|
||||||
|
|
||||||
|
class OwnerAffiliations(Affiliations):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class OwnerAffiation(Affiliation):
|
||||||
|
namespace = 'http://jabber.org/protocol/pubsub#owner'
|
||||||
|
interfaces = set(('node', 'affiliation', 'jid'))
|
||||||
|
|
||||||
|
class PubSubOwner(ElementBase):
|
||||||
|
namespace = 'http://jabber.org/protocol/pubsub#owner'
|
||||||
|
nick = 'pubsubowner'
|
|
@ -48,7 +48,7 @@ class xep_0004(base.base_plugin):
|
||||||
return object
|
return object
|
||||||
|
|
||||||
def buildForm(self, xml):
|
def buildForm(self, xml):
|
||||||
form = Form(xml.attrib['type'])
|
form = Form(ftype=xml.attrib['type'])
|
||||||
form.fromXML(xml)
|
form.fromXML(xml)
|
||||||
return form
|
return form
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
from __future__ import with_statement
|
from __future__ import with_statement
|
||||||
from . import base
|
from . import base
|
||||||
import logging
|
import logging
|
||||||
from xml.etree import cElementTree as ET
|
#from xml.etree import cElementTree as ET
|
||||||
|
from .. xmlstream.stanzabase import ElementBase, ET
|
||||||
|
|
||||||
class xep_0060(base.base_plugin):
|
class xep_0060(base.base_plugin):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -3,8 +3,9 @@ from xml.etree import cElementTree as ET
|
||||||
from . error import Error
|
from . error import Error
|
||||||
from .. xmlstream.handler.waiter import Waiter
|
from .. xmlstream.handler.waiter import Waiter
|
||||||
from .. xmlstream.matcher.id import MatcherId
|
from .. xmlstream.matcher.id import MatcherId
|
||||||
|
from . rootstanza import RootStanza
|
||||||
|
|
||||||
class Iq(StanzaBase):
|
class Iq(RootStanza):
|
||||||
interfaces = set(('type', 'to', 'from', 'id','query'))
|
interfaces = set(('type', 'to', 'from', 'id','query'))
|
||||||
types = set(('get', 'result', 'set', 'error'))
|
types = set(('get', 'result', 'set', 'error'))
|
||||||
name = 'iq'
|
name = 'iq'
|
||||||
|
@ -13,13 +14,10 @@ class Iq(StanzaBase):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
StanzaBase.__init__(self, *args, **kwargs)
|
StanzaBase.__init__(self, *args, **kwargs)
|
||||||
if self['id'] == '':
|
if self['id'] == '':
|
||||||
self['id'] = self.stream.getNewId()
|
if self.stream is not None:
|
||||||
|
self['id'] = self.stream.getNewId()
|
||||||
def exception(self, text):
|
else:
|
||||||
self.reply()
|
self['id'] = '0'
|
||||||
self['error']['condition'] = 'undefined-condition'
|
|
||||||
self['error']['text'] = text
|
|
||||||
self.send()
|
|
||||||
|
|
||||||
def unhandled(self):
|
def unhandled(self):
|
||||||
self.reply()
|
self.reply()
|
||||||
|
@ -84,7 +82,3 @@ class Iq(StanzaBase):
|
||||||
return waitfor.wait(timeout)
|
return waitfor.wait(timeout)
|
||||||
else:
|
else:
|
||||||
return StanzaBase.send(self)
|
return StanzaBase.send(self)
|
||||||
|
|
||||||
|
|
||||||
Iq.plugin_attrib_map['error'] = Error
|
|
||||||
Iq.plugin_tag_map["{%s}%s" % (Error.namespace, Error.name)] = Error
|
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
from .. xmlstream.stanzabase import StanzaBase
|
from .. xmlstream.stanzabase import StanzaBase
|
||||||
from xml.etree import cElementTree as ET
|
from xml.etree import cElementTree as ET
|
||||||
from . error import Error
|
from . error import Error
|
||||||
|
from . rootstanza import RootStanza
|
||||||
|
|
||||||
class Message(StanzaBase):
|
class Message(RootStanza):
|
||||||
interfaces = set(('type', 'to', 'from', 'id', 'body', 'subject'))
|
interfaces = set(('type', 'to', 'from', 'id', 'body', 'subject'))
|
||||||
types = set((None, 'normal', 'chat', 'headline', 'error', 'groupchat'))
|
types = set((None, 'normal', 'chat', 'headline', 'error', 'groupchat'))
|
||||||
sub_interfaces = set(('body', 'subject'))
|
sub_interfaces = set(('body', 'subject'))
|
||||||
|
@ -27,11 +28,3 @@ class Message(StanzaBase):
|
||||||
self['body'] = body
|
self['body'] = body
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def exception(self, text):
|
|
||||||
self.reply()
|
|
||||||
self['error']['condition'] = 'undefined-condition'
|
|
||||||
self['error']['text'] = text
|
|
||||||
self.send()
|
|
||||||
|
|
||||||
Message.plugin_attrib_map['error'] = Error
|
|
||||||
Message.plugin_tag_map["{%s}%s" % (Error.namespace, Error.name)] = Error
|
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
from .. xmlstream.stanzabase import StanzaBase
|
from .. xmlstream.stanzabase import StanzaBase
|
||||||
from xml.etree import cElementTree as ET
|
from xml.etree import cElementTree as ET
|
||||||
from . error import Error
|
from . error import Error
|
||||||
|
from . rootstanza import RootStanza
|
||||||
|
|
||||||
class Presence(StanzaBase):
|
class Presence(RootStanza):
|
||||||
interfaces = set(('type', 'to', 'from', 'id', 'status', 'priority'))
|
interfaces = set(('type', 'to', 'from', 'id', 'status', 'priority'))
|
||||||
types = set(('available', 'unavailable', 'error', 'probe', 'subscribe', 'subscribed', 'unsubscribe', 'unsubscribed'))
|
types = set(('available', 'unavailable', 'error', 'probe', 'subscribe', 'subscribed', 'unsubscribe', 'unsubscribed'))
|
||||||
showtypes = set(('dnd', 'ffc', 'xa', 'away'))
|
showtypes = set(('dnd', 'ffc', 'xa', 'away'))
|
||||||
|
@ -52,12 +53,3 @@ class Presence(StanzaBase):
|
||||||
elif self['type'] == 'subscribe':
|
elif self['type'] == 'subscribe':
|
||||||
self['type'] = 'subscribed'
|
self['type'] = 'subscribed'
|
||||||
return StanzaBase.reply(self)
|
return StanzaBase.reply(self)
|
||||||
|
|
||||||
def exception(self, text):
|
|
||||||
self.reply()
|
|
||||||
self['error']['condition'] = 'undefined-condition'
|
|
||||||
self['error']['text'] = text
|
|
||||||
self.send()
|
|
||||||
|
|
||||||
Presence.plugin_attrib_map['error'] = Error
|
|
||||||
Presence.plugin_tag_map["{%s}%s" % (Error.namespace, Error.name)] = Error
|
|
||||||
|
|
25
sleekxmpp/stanza/rootstanza.py
Normal file
25
sleekxmpp/stanza/rootstanza.py
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
from .. xmlstream.stanzabase import StanzaBase
|
||||||
|
from xml.etree import cElementTree as ET
|
||||||
|
from . error import Error
|
||||||
|
from .. exceptions import XMPPError
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
class RootStanza(StanzaBase):
|
||||||
|
|
||||||
|
def exception(self, e): #called when a handler raises an exception
|
||||||
|
self.reply()
|
||||||
|
if isinstance(e, XMPPError): # we raised this deliberately
|
||||||
|
self['error']['condition'] = e.condition
|
||||||
|
self['error']['text'] = e.text
|
||||||
|
if e.extension is not None: # extended error tag
|
||||||
|
extxml = ET.Element("{%s}%s" % (e.extension_ns, e.extension), e.extension_args)
|
||||||
|
self['error'].xml.append(extxml)
|
||||||
|
self['error']['type'] = e.etype
|
||||||
|
else: # we probably didn't raise this on purpose, so send back a traceback
|
||||||
|
self['error']['condition'] = 'undefined-condition'
|
||||||
|
self['error']['text'] = traceback.format_tb(e.__traceback__)
|
||||||
|
self.send()
|
||||||
|
|
||||||
|
# all jabber:client root stanzas should have the error plugin
|
||||||
|
RootStanza.plugin_attrib_map['error'] = Error
|
||||||
|
RootStanza.plugin_tag_map["{%s}%s" % (Error.namespace, Error.name)] = Error
|
|
@ -1,5 +1,6 @@
|
||||||
from xml.etree import cElementTree as ET
|
from xml.etree import cElementTree as ET
|
||||||
import logging
|
import logging
|
||||||
|
import traceback
|
||||||
|
|
||||||
class JID(object):
|
class JID(object):
|
||||||
def __init__(self, jid):
|
def __init__(self, jid):
|
||||||
|
@ -31,6 +32,7 @@ class ElementBase(object):
|
||||||
plugin_tag_map = {}
|
plugin_tag_map = {}
|
||||||
|
|
||||||
def __init__(self, xml=None, parent=None):
|
def __init__(self, xml=None, parent=None):
|
||||||
|
self.attrib = self # backwards compatibility hack
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.xml = xml
|
self.xml = xml
|
||||||
self.plugins = {}
|
self.plugins = {}
|
||||||
|
@ -42,6 +44,9 @@ class ElementBase(object):
|
||||||
def match(self, xml):
|
def match(self, xml):
|
||||||
return xml.tag == self.tag
|
return xml.tag == self.tag
|
||||||
|
|
||||||
|
def find(self, xpath): # for backwards compatiblity, expose elementtree interface
|
||||||
|
return self.xml.find(xpath)
|
||||||
|
|
||||||
def setup(self, xml=None):
|
def setup(self, xml=None):
|
||||||
if self.xml is None:
|
if self.xml is None:
|
||||||
self.xml = xml
|
self.xml = xml
|
||||||
|
@ -183,9 +188,8 @@ class StanzaBase(ElementBase):
|
||||||
types = set(('get', 'set', 'error', None, 'unavailable', 'normal', 'chat'))
|
types = set(('get', 'set', 'error', None, 'unavailable', 'normal', 'chat'))
|
||||||
sub_interfaces = tuple()
|
sub_interfaces = tuple()
|
||||||
|
|
||||||
def __init__(self, stream, xml=None, stype=None, sto=None, sfrom=None, sid=None):
|
def __init__(self, stream=None, xml=None, stype=None, sto=None, sfrom=None, sid=None):
|
||||||
self.stream = stream
|
self.stream = stream
|
||||||
self.namespace = stream.default_ns
|
|
||||||
ElementBase.__init__(self, xml)
|
ElementBase.__init__(self, xml)
|
||||||
if stype is not None:
|
if stype is not None:
|
||||||
self['type'] = stype
|
self['type'] = stype
|
||||||
|
@ -193,7 +197,9 @@ class StanzaBase(ElementBase):
|
||||||
self['to'] = sto
|
self['to'] = sto
|
||||||
if sfrom is not None:
|
if sfrom is not None:
|
||||||
self['from'] = sfrom
|
self['from'] = sfrom
|
||||||
self.tag = "{%s}%s" % (self.stream.default_ns, self.name)
|
if stream is not None:
|
||||||
|
self.namespace = stream.default_ns
|
||||||
|
self.tag = "{%s}%s" % (self.namespace, self.name)
|
||||||
|
|
||||||
def setType(self, value):
|
def setType(self, value):
|
||||||
if value in self.types:
|
if value in self.types:
|
||||||
|
@ -240,8 +246,8 @@ class StanzaBase(ElementBase):
|
||||||
def unhandled(self):
|
def unhandled(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def exception(self, text):
|
def exception(self, e):
|
||||||
logging.error(text)
|
logging.error(traceback.format_tb(e))
|
||||||
|
|
||||||
def send(self):
|
def send(self):
|
||||||
self.stream.sendRaw(str(self))
|
self.stream.sendRaw(str(self))
|
||||||
|
@ -257,13 +263,13 @@ class StanzaBase(ElementBase):
|
||||||
else:
|
else:
|
||||||
ixmlns = ''
|
ixmlns = ''
|
||||||
nsbuffer = ''
|
nsbuffer = ''
|
||||||
if xmlns != ixmlns and ixmlns != '' and ixmlns != self.stream.default_ns:
|
if xmlns != ixmlns and ixmlns != '' and ixmlns != self.namespace:
|
||||||
if ixmlns in self.stream.namespace_map:
|
if self.stream is not None and ixmlns in self.stream.namespace_map:
|
||||||
if self.stream.namespace_map[ixmlns] != '':
|
if self.stream.namespace_map[ixmlns] != '':
|
||||||
itag = "%s:%s" % (self.stream.namespace_map[ixmlns], itag)
|
itag = "%s:%s" % (self.stream.namespace_map[ixmlns], itag)
|
||||||
else:
|
else:
|
||||||
nsbuffer = """ xmlns="%s\"""" % ixmlns
|
nsbuffer = """ xmlns="%s\"""" % ixmlns
|
||||||
if ixmlns not in (xmlns, self.namespace):
|
if ixmlns not in ('', xmlns, self.namespace):
|
||||||
nsbuffer = """ xmlns="%s\"""" % ixmlns
|
nsbuffer = """ xmlns="%s\"""" % ixmlns
|
||||||
newoutput.append("<%s" % itag)
|
newoutput.append("<%s" % itag)
|
||||||
newoutput.append(nsbuffer)
|
newoutput.append(nsbuffer)
|
||||||
|
|
|
@ -281,8 +281,8 @@ class XMLStream(object):
|
||||||
if etype == 'stanza':
|
if etype == 'stanza':
|
||||||
try:
|
try:
|
||||||
handler.run(args[0])
|
handler.run(args[0])
|
||||||
except:
|
except Exception as e:
|
||||||
args[0].exception(traceback.format_exc())
|
args[0].exception(e)
|
||||||
elif etype == 'sched':
|
elif etype == 'sched':
|
||||||
try:
|
try:
|
||||||
handler.run(*args)
|
handler.run(*args)
|
||||||
|
|
Loading…
Reference in a new issue