summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLance Stout <lancestout@gmail.com>2011-02-02 09:13:22 -0500
committerLance Stout <lancestout@gmail.com>2011-02-02 09:13:22 -0500
commitde6170a13de2dbb3cbcfbf4a74b749f20b0cf061 (patch)
treeb60043234119a3d215e0eca65913dd038167b551
parent65931bb384aada922c5287a3ad3cc62d04d6e676 (diff)
parent8dbe6f65462ec9b1a0506a00316415996f4d53d8 (diff)
downloadslixmpp-de6170a13de2dbb3cbcfbf4a74b749f20b0cf061.tar.gz
slixmpp-de6170a13de2dbb3cbcfbf4a74b749f20b0cf061.tar.bz2
slixmpp-de6170a13de2dbb3cbcfbf4a74b749f20b0cf061.tar.xz
slixmpp-de6170a13de2dbb3cbcfbf4a74b749f20b0cf061.zip
Merge branch 'develop' into roster
Conflicts: sleekxmpp/basexmpp.py
-rwxr-xr-xconn_tests/testpubsub.py2
-rw-r--r--sleekxmpp/basexmpp.py10
-rw-r--r--sleekxmpp/clientxmpp.py4
-rw-r--r--sleekxmpp/plugins/xep_0030/disco.py29
-rw-r--r--sleekxmpp/plugins/xep_0030/static.py5
-rw-r--r--sleekxmpp/plugins/xep_0045.py1
-rw-r--r--sleekxmpp/plugins/xep_0078.py2
-rw-r--r--sleekxmpp/plugins/xep_0092/version.py2
-rw-r--r--sleekxmpp/plugins/xep_0199.py2
-rw-r--r--sleekxmpp/plugins/xep_0202.py8
-rw-r--r--sleekxmpp/stanza/__init__.py1
-rw-r--r--sleekxmpp/stanza/iq.py1
-rw-r--r--sleekxmpp/stanza/nick.py2
-rw-r--r--sleekxmpp/stanza/stream_error.py69
-rw-r--r--sleekxmpp/test/sleektest.py23
-rw-r--r--sleekxmpp/thirdparty/__init__.py4
-rw-r--r--sleekxmpp/xmlstream/stanzabase.py119
-rw-r--r--sleekxmpp/xmlstream/xmlstream.py14
-rw-r--r--tests/test_stanza_message.py2
-rw-r--r--tests/test_stanza_presence.py2
-rw-r--r--todo1.0185
21 files changed, 291 insertions, 196 deletions
diff --git a/conn_tests/testpubsub.py b/conn_tests/testpubsub.py
index 24855c90..3aa7200e 100755
--- a/conn_tests/testpubsub.py
+++ b/conn_tests/testpubsub.py
@@ -33,7 +33,7 @@ class testps(sleekxmpp.ClientXMPP):
self.node = "pstestnode_%s"
self.pshost = pshost
if pshost is None:
- self.pshost = self.server
+ self.pshost = self.boundjid.host
self.nodenum = int(nodenum)
self.leafnode = self.nodenum + 1
self.collectnode = self.nodenum + 2
diff --git a/sleekxmpp/basexmpp.py b/sleekxmpp/basexmpp.py
index 699424ca..bd953afe 100644
--- a/sleekxmpp/basexmpp.py
+++ b/sleekxmpp/basexmpp.py
@@ -16,7 +16,7 @@ import sleekxmpp
from sleekxmpp import plugins
import sleekxmpp.roster as roster
-from sleekxmpp.stanza import Message, Presence, Iq, Error
+from sleekxmpp.stanza import Message, Presence, Iq, Error, StreamError
from sleekxmpp.stanza.roster import Roster
from sleekxmpp.stanza.nick import Nick
from sleekxmpp.stanza.htmlim import HTMLIM
@@ -133,6 +133,10 @@ class BaseXMPP(XMLStream):
Callback('Presence',
MatchXPath("{%s}presence" % self.default_ns),
self._handle_presence))
+ self.register_handler(
+ Callback('Stream Error',
+ MatchXPath("{%s}error" % self.stream_ns),
+ self._handle_stream_error))
self.add_event_handler('disconnected',
self._handle_disconnected)
@@ -165,6 +169,7 @@ class BaseXMPP(XMLStream):
self.register_stanza(Message)
self.register_stanza(Iq)
self.register_stanza(Presence)
+ self.register_stanza(StreamError)
# Initialize a few default stanza plugins.
register_stanza_plugin(Iq, Roster)
@@ -606,6 +611,9 @@ class BaseXMPP(XMLStream):
"""When disconnected, reset the roster"""
self.roster = {}
+ def _handle_stream_error(self, error):
+ self.event('stream_error', error)
+
def _handle_message(self, msg):
"""Process incoming message stanzas."""
self.event('message', msg)
diff --git a/sleekxmpp/clientxmpp.py b/sleekxmpp/clientxmpp.py
index c08cd648..2554d299 100644
--- a/sleekxmpp/clientxmpp.py
+++ b/sleekxmpp/clientxmpp.py
@@ -163,11 +163,13 @@ class ClientXMPP(BaseXMPP):
log.debug("Since no address is supplied," + \
"attempting SRV lookup.")
try:
- xmpp_srv = "_xmpp-client._tcp.%s" % self.server
+ xmpp_srv = "_xmpp-client._tcp.%s" % self.boundjid.host
answers = dns.resolver.query(xmpp_srv, dns.rdatatype.SRV)
except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
log.debug("No appropriate SRV record found." + \
" Using JID server name.")
+ except (dns.exception.Timeout,):
+ log.debug("DNS resolution timed out.")
else:
# Pick a random server, weighted by priority.
diff --git a/sleekxmpp/plugins/xep_0030/disco.py b/sleekxmpp/plugins/xep_0030/disco.py
index 6fd4e85f..a976b988 100644
--- a/sleekxmpp/plugins/xep_0030/disco.py
+++ b/sleekxmpp/plugins/xep_0030/disco.py
@@ -177,7 +177,10 @@ class xep_0030(base_plugin):
elif node is None:
self._handlers[htype]['jid'][jid] = handler
elif jid is None:
- jid = self.xmpp.boundjid.full
+ if self.xmpp.is_component:
+ jid = self.xmpp.boundjid.full
+ else:
+ jid = self.xmpp.boundjid.bare
self._handlers[htype]['node'][(jid, node)] = handler
else:
self._handlers[htype]['node'][(jid, node)] = handler
@@ -342,7 +345,7 @@ class xep_0030(base_plugin):
"""
self._run_node_handler('del_items', jid, node, kwargs)
- def add_item(self, jid=None, name='', node=None, subnode='', ijid=None):
+ def add_item(self, jid='', name='', node=None, subnode='', ijid=None):
"""
Add a new item element to the given JID/node combination.
@@ -356,10 +359,12 @@ class xep_0030(base_plugin):
subnode -- Optional node for the item.
ijid -- The JID to modify.
"""
+ if not jid:
+ jid = self.xmpp.boundjid.full
kwargs = {'ijid': jid,
'name': name,
'inode': subnode}
- self._run_node_handler('add_item', jid, node, kwargs)
+ self._run_node_handler('add_item', ijid, node, kwargs)
def del_item(self, jid=None, node=None, **kwargs):
"""
@@ -500,7 +505,10 @@ class xep_0030(base_plugin):
data -- Optional, custom data to pass to the handler.
"""
if jid is None:
- jid = self.xmpp.boundjid.full
+ if self.xmpp.is_component:
+ jid = self.xmpp.boundjid.full
+ else:
+ jid = self.xmpp.boundjid.bare
if node is None:
node = ''
@@ -526,8 +534,12 @@ class xep_0030(base_plugin):
if iq['type'] == 'get':
log.debug("Received disco info query from " + \
"<%s> to <%s>." % (iq['from'], iq['to']))
+ if self.xmpp.is_component:
+ jid = iq['to'].full
+ else:
+ jid = iq['to'].bare
info = self._run_node_handler('get_info',
- iq['to'].full,
+ jid,
iq['disco_info']['node'],
iq)
iq.reply()
@@ -552,8 +564,12 @@ class xep_0030(base_plugin):
if iq['type'] == 'get':
log.debug("Received disco items query from " + \
"<%s> to <%s>." % (iq['from'], iq['to']))
+ if self.xmpp.is_component:
+ jid = iq['to'].full
+ else:
+ jid = iq['to'].bare
items = self._run_node_handler('get_items',
- iq['to'].full,
+ jid,
iq['disco_items']['node'])
iq.reply()
if items:
@@ -590,3 +606,4 @@ class xep_0030(base_plugin):
"Using default disco#info feature.")
info.add_feature(info.namespace)
return info
+
diff --git a/sleekxmpp/plugins/xep_0030/static.py b/sleekxmpp/plugins/xep_0030/static.py
index eff67f02..f957c84c 100644
--- a/sleekxmpp/plugins/xep_0030/static.py
+++ b/sleekxmpp/plugins/xep_0030/static.py
@@ -247,8 +247,8 @@ class StaticDisco(object):
self.add_node(jid, node)
self.nodes[(jid, node)]['items'].add_item(
data.get('ijid', ''),
- node=data.get('inode', None),
- name=data.get('name', None))
+ node=data.get('inode', ''),
+ name=data.get('name', ''))
def del_item(self, jid, node, data):
"""
@@ -262,3 +262,4 @@ class StaticDisco(object):
self.nodes[(jid, node)]['items'].del_item(
data.get('ijid', ''),
node=data.get('inode', None))
+
diff --git a/sleekxmpp/plugins/xep_0045.py b/sleekxmpp/plugins/xep_0045.py
index feec70db..364fbbd9 100644
--- a/sleekxmpp/plugins/xep_0045.py
+++ b/sleekxmpp/plugins/xep_0045.py
@@ -316,6 +316,7 @@ class xep_0045(base.base_plugin):
x = ET.Element('{jabber:x:data}x', type='cancel')
query.append(x)
iq = self.xmpp.makeIqSet(query)
+ iq['to'] = room
iq.send()
def setRoomConfig(self, room, config, ifrom=''):
diff --git a/sleekxmpp/plugins/xep_0078.py b/sleekxmpp/plugins/xep_0078.py
index d2c81b16..bb6a4632 100644
--- a/sleekxmpp/plugins/xep_0078.py
+++ b/sleekxmpp/plugins/xep_0078.py
@@ -36,7 +36,7 @@ class xep_0078(base.base_plugin):
log.debug("Starting jabber:iq:auth Authentication")
auth_request = self.xmpp.makeIqGet()
auth_request_query = ET.Element('{jabber:iq:auth}query')
- auth_request.attrib['to'] = self.xmpp.server
+ auth_request.attrib['to'] = self.xmpp.boundjid.host
username = ET.Element('username')
username.text = self.xmpp.username
auth_request_query.append(username)
diff --git a/sleekxmpp/plugins/xep_0092/version.py b/sleekxmpp/plugins/xep_0092/version.py
index f59f8819..fb3671e4 100644
--- a/sleekxmpp/plugins/xep_0092/version.py
+++ b/sleekxmpp/plugins/xep_0092/version.py
@@ -84,5 +84,5 @@ class xep_0092(base_plugin):
result = iq.send()
if result and result['type'] != 'error':
- return result['software_version']._get_stanza_values()
+ return result['software_version'].values
return False
diff --git a/sleekxmpp/plugins/xep_0199.py b/sleekxmpp/plugins/xep_0199.py
index 2e99ae76..16e79e26 100644
--- a/sleekxmpp/plugins/xep_0199.py
+++ b/sleekxmpp/plugins/xep_0199.py
@@ -33,7 +33,7 @@ class xep_0199(base.base_plugin):
def scheduled_ping(self):
log.debug("pinging...")
- if self.sendPing(self.xmpp.server, self.config.get('timeout', 30)) is False:
+ if self.sendPing(self.xmpp.boundjid.host, self.config.get('timeout', 30)) is False:
log.debug("Did not recieve ping back in time. Requesting Reconnect.")
self.xmpp.reconnect()
diff --git a/sleekxmpp/plugins/xep_0202.py b/sleekxmpp/plugins/xep_0202.py
index fe1191ea..3b31c97a 100644
--- a/sleekxmpp/plugins/xep_0202.py
+++ b/sleekxmpp/plugins/xep_0202.py
@@ -27,10 +27,12 @@ class EntityTime(ElementBase):
interfaces = set(('tzo', 'utc'))
sub_interfaces = set(('tzo', 'utc'))
- #def get_utc(self): # TODO: return a datetime.tzinfo object?
+ #def get_tzo(self):
+ # TODO: Right now it returns a string but maybe it should
+ # return a datetime.tzinfo object or maybe a datetime.timedelta?
#pass
- def set_tzo(self, tzo): # TODO: support datetime.tzinfo objects?
+ def set_tzo(self, tzo):
if isinstance(tzo, tzinfo):
td = datetime.now(tzo).utcoffset() # What if we are faking the time? datetime.now() shouldn't be used here'
seconds = td.seconds + td.days * 24 * 3600
@@ -45,7 +47,7 @@ class EntityTime(ElementBase):
# Returns a datetime object instead the string. Is this a good idea?
value = self._get_sub_text('utc')
if '.' in value:
- return datetime.strptime(value, '%Y-%m-%d.%fT%H:%M:%SZ')
+ return datetime.strptime(value, '%Y-%m-%dT%H:%M:%S.%fZ')
else:
return datetime.strptime(value, '%Y-%m-%dT%H:%M:%SZ')
diff --git a/sleekxmpp/stanza/__init__.py b/sleekxmpp/stanza/__init__.py
index 8302c43d..dbf7b86f 100644
--- a/sleekxmpp/stanza/__init__.py
+++ b/sleekxmpp/stanza/__init__.py
@@ -8,6 +8,7 @@
from sleekxmpp.stanza.error import Error
+from sleekxmpp.stanza.stream_error import StreamError
from sleekxmpp.stanza.iq import Iq
from sleekxmpp.stanza.message import Message
from sleekxmpp.stanza.presence import Presence
diff --git a/sleekxmpp/stanza/iq.py b/sleekxmpp/stanza/iq.py
index 6388346c..c6aa64d0 100644
--- a/sleekxmpp/stanza/iq.py
+++ b/sleekxmpp/stanza/iq.py
@@ -224,4 +224,3 @@ class Iq(RootStanza):
else:
StanzaBase._set_stanza_values(self, values)
return self
-
diff --git a/sleekxmpp/stanza/nick.py b/sleekxmpp/stanza/nick.py
index a9243d1a..dce41d14 100644
--- a/sleekxmpp/stanza/nick.py
+++ b/sleekxmpp/stanza/nick.py
@@ -44,7 +44,7 @@ class Nick(ElementBase):
del_nick -- Remove the <nick> element.
"""
- namespace = 'http://jabber.org/nick/nick'
+ namespace = 'http://jabber.org/protocol/nick'
name = 'nick'
plugin_attrib = name
interfaces = set(('nick',))
diff --git a/sleekxmpp/stanza/stream_error.py b/sleekxmpp/stanza/stream_error.py
new file mode 100644
index 00000000..cf59a7fa
--- /dev/null
+++ b/sleekxmpp/stanza/stream_error.py
@@ -0,0 +1,69 @@
+"""
+ SleekXMPP: The Sleek XMPP Library
+ Copyright (C) 2010 Nathanael C. Fritz
+ This file is part of SleekXMPP.
+
+ See the file LICENSE for copying permission.
+"""
+
+from sleekxmpp.stanza.error import Error
+from sleekxmpp.xmlstream import StanzaBase, ElementBase, ET
+from sleekxmpp.xmlstream import register_stanza_plugin
+
+
+class StreamError(Error, StanzaBase):
+
+ """
+ XMPP stanzas of type 'error' should include an <error> stanza that
+ describes the nature of the error and how it should be handled.
+
+ Use the 'XEP-0086: Error Condition Mappings' plugin to include error
+ codes used in older XMPP versions.
+
+ The stream:error stanza is used to provide more information for
+ error that occur with the underlying XML stream itself, and not
+ a particular stanza.
+
+ Note: The StreamError stanza is mostly the same as the normal
+ Error stanza, but with different namespaces and
+ condition names.
+
+ Example error stanza:
+ <stream:error>
+ <not-well-formed xmlns="urn:ietf:params:xml:ns:xmpp-streams" />
+ <text xmlns="urn:ietf:params:xml:ns:xmpp-streams">
+ XML was not well-formed.
+ </text>
+ </stream:error>
+
+ Stanza Interface:
+ condition -- The name of the condition element.
+ text -- Human readable description of the error.
+
+ Attributes:
+ conditions -- The set of allowable error condition elements.
+ condition_ns -- The namespace for the condition element.
+
+ Methods:
+ setup -- Overrides ElementBase.setup.
+ get_condition -- Retrieve the name of the condition element.
+ set_condition -- Add a condition element.
+ del_condition -- Remove the condition element.
+ get_text -- Retrieve the contents of the <text> element.
+ set_text -- Set the contents of the <text> element.
+ del_text -- Remove the <text> element.
+ """
+
+ namespace = 'http://etherx.jabber.org/streams'
+ interfaces = set(('condition', 'text'))
+ conditions = set((
+ 'bad-format', 'bad-namespace-prefix', 'conflict',
+ 'connection-timeout', 'host-gone', 'host-unknown',
+ 'improper-addressing', 'internal-server-error', 'invalid-from',
+ 'invalid-namespace', 'invalid-xml', 'not-authorized',
+ 'not-well-formed', 'policy-violation', 'remote-connection-failed',
+ 'reset', 'resource-constraint', 'restricted-xml', 'see-other-host',
+ 'system-shutdown', 'undefined-condition', 'unsupported-encoding',
+ 'unsupported-feature', 'unsupported-stanza-type',
+ 'unsupported-version'))
+ condition_ns = 'urn:ietf:params:xml:ns:xmpp-streams'
diff --git a/sleekxmpp/test/sleektest.py b/sleekxmpp/test/sleektest.py
index ce582e3a..b4c912b4 100644
--- a/sleekxmpp/test/sleektest.py
+++ b/sleekxmpp/test/sleektest.py
@@ -183,8 +183,7 @@ class SleekTest(unittest.TestCase):
"""
Create and compare several stanza objects to a correct XML string.
- If use_values is False, test using getStanzaValues() and
- setStanzaValues() will not be used.
+ If use_values is False, tests using stanza.values will not be used.
Some stanzas provide default values for some interfaces, but
these defaults can be problematic for testing since they can easily
@@ -207,9 +206,8 @@ class SleekTest(unittest.TestCase):
values. These interfaces will be set to their
defaults for the given and generated stanzas to
prevent unexpected test failures.
- use_values -- Indicates if testing using getStanzaValues() and
- setStanzaValues() should be used. Defaults to
- True.
+ use_values -- Indicates if testing using stanza.values should
+ be used. Defaults to True.
"""
if method is None and hasattr(self, 'match_method'):
method = getattr(self, 'match_method')
@@ -242,10 +240,10 @@ class SleekTest(unittest.TestCase):
stanza2 = stanza_class(xml=xml)
if use_values:
- # Using getStanzaValues() and setStanzaValues() will add
- # XML for any interface that has a default value. We need
- # to set those defaults on the existing stanzas and XML
- # so that they will compare correctly.
+ # Using stanza.values will add XML for any interface that
+ # has a default value. We need to set those defaults on
+ # the existing stanzas and XML so that they will compare
+ # correctly.
default_stanza = stanza_class()
if defaults is None:
known_defaults = {
@@ -264,9 +262,9 @@ class SleekTest(unittest.TestCase):
value = default_stanza.xml.attrib[interface]
xml.attrib[interface] = value
- values = stanza2.getStanzaValues()
+ values = stanza2.values
stanza3 = stanza_class()
- stanza3.setStanzaValues(values)
+ stanza3.values = values
debug = "Three methods for creating stanzas do not match.\n"
debug += "Given XML:\n%s\n" % tostring(xml)
@@ -416,8 +414,7 @@ class SleekTest(unittest.TestCase):
'id', 'stanzapath', 'xpath', and 'mask'.
Defaults to the value of self.match_method.
use_values -- Indicates if stanza comparisons should test using
- getStanzaValues() and setStanzaValues().
- Defaults to True.
+ stanza.values. Defaults to True.
timeout -- Time to wait in seconds for data to be received by
a live connection.
"""
diff --git a/sleekxmpp/thirdparty/__init__.py b/sleekxmpp/thirdparty/__init__.py
index e69de29b..276ac3cc 100644
--- a/sleekxmpp/thirdparty/__init__.py
+++ b/sleekxmpp/thirdparty/__init__.py
@@ -0,0 +1,4 @@
+try:
+ from collections import OrderedDict
+except:
+ from sleekxmpp.thirdparty.ordereddict import OrderedDict
diff --git a/sleekxmpp/xmlstream/stanzabase.py b/sleekxmpp/xmlstream/stanzabase.py
index 5551d439..3937a7a9 100644
--- a/sleekxmpp/xmlstream/stanzabase.py
+++ b/sleekxmpp/xmlstream/stanzabase.py
@@ -14,6 +14,7 @@ from xml.etree import cElementTree as ET
from sleekxmpp.xmlstream import JID
from sleekxmpp.xmlstream.tostring import tostring
+from sleekxmpp.thirdparty import OrderedDict
log = logging.getLogger(__name__)
@@ -23,17 +24,23 @@ log = logging.getLogger(__name__)
XML_TYPE = type(ET.Element('xml'))
-def register_stanza_plugin(stanza, plugin):
+def register_stanza_plugin(stanza, plugin, iterable=False):
"""
Associate a stanza object as a plugin for another stanza.
Arguments:
- stanza -- The class of the parent stanza.
- plugin -- The class of the plugin stanza.
+ stanza -- The class of the parent stanza.
+ plugin -- The class of the plugin stanza.
+ iterable -- Indicates if the plugin stanza
+ should be included in the parent
+ stanza's iterable 'substanzas'
+ interface results.
"""
tag = "{%s}%s" % (plugin.namespace, plugin.name)
stanza.plugin_attrib_map[plugin.plugin_attrib] = plugin
stanza.plugin_tag_map[tag] = plugin
+ if iterable:
+ stanza.plugin_iterables.add(plugin)
# To maintain backwards compatibility for now, preserve the camel case name.
@@ -95,10 +102,22 @@ class ElementBase(object):
>>> message['custom']['useful_thing'] = 'foo'
If a plugin provides an interface that is the same as the plugin's
- plugin_attrib value, then the plugin's interface may be accessed
- directly from the parent stanza, as so:
+ plugin_attrib value, then the plugin's interface may be assigned
+ directly from the parent stanza, as shown below, but retrieving
+ information will require all interfaces to be used, as so:
>>> message['custom'] = 'bar' # Same as using message['custom']['custom']
+ >>> message['custom']['custom'] # Must use all interfaces
+ 'bar'
+
+ If the plugin sets the value is_extension = True, then both setting
+ and getting an interface value that is the same as the plugin's
+ plugin_attrib value will work, as so:
+
+ >>> message['custom'] = 'bar' # Using is_extension=True
+ >>> message['custom']
+ 'bar'
+
Class Attributes:
name -- The name of the stanza's main element.
@@ -108,14 +127,23 @@ class ElementBase(object):
sub_interfaces -- A subset of the set of interfaces which map
to subelements instead of attributes.
subitem -- A set of stanza classes which are allowed to
- be added as substanzas.
+ be added as substanzas. Deprecated version
+ of plugin_iterables.
types -- A set of generic type attribute values.
+ tag -- The namespaced name of the stanza's root
+ element. Example: "{foo_ns}bar"
plugin_attrib -- The interface name that the stanza uses to be
accessed as a plugin from another stanza.
plugin_attrib_map -- A mapping of plugin attribute names with the
associated plugin stanza classes.
+ plugin_iterables -- A set of stanza classes which are allowed to
+ be added as substanzas.
plugin_tag_map -- A mapping of plugin stanza tag names with
the associated plugin stanza classes.
+ is_extension -- When True, allows the stanza to provide one
+ additional interface to the parent stanza,
+ extending the interfaces supported by the
+ parent. Defaults to False.
xml_ns -- The XML namespace,
http://www.w3.org/XML/1998/namespace,
for use with xml:lang values.
@@ -128,6 +156,10 @@ class ElementBase(object):
values -- A dictionary of the stanza's interfaces
and interface values, including plugins.
+ Class Methods
+ tag_name -- Return the namespaced version of the stanza's
+ root element's name.
+
Methods:
setup -- Initialize the stanza's XML contents.
enable -- Instantiate a stanza plugin.
@@ -160,6 +192,7 @@ class ElementBase(object):
appendxml -- Add XML content to the stanza.
pop -- Remove a substanza.
next -- Return the next iterable substanza.
+ clear -- Reset the stanza's XML contents.
_fix_ns -- Apply the stanza's namespace to non-namespaced
elements in an XPath expression.
"""
@@ -171,8 +204,10 @@ class ElementBase(object):
types = set(('get', 'set', 'error', None, 'unavailable', 'normal', 'chat'))
sub_interfaces = tuple()
plugin_attrib_map = {}
+ plugin_iterables = set()
plugin_tag_map = {}
subitem = None
+ is_extension = False
xml_ns = 'http://www.w3.org/XML/1998/namespace'
def __init__(self, xml=None, parent=None):
@@ -196,9 +231,10 @@ class ElementBase(object):
self.setStanzaValues = self._set_stanza_values
self.xml = xml
- self.plugins = {}
+ self.plugins = OrderedDict()
self.iterables = []
self._index = 0
+ self.tag = self.tag_name()
if parent is None:
self.parent = None
else:
@@ -218,9 +254,11 @@ class ElementBase(object):
self.plugins[plugin.plugin_attrib] = plugin(child, self)
if self.subitem is not None:
for sub in self.subitem:
- if child.tag == "{%s}%s" % (sub.namespace, sub.name):
- self.iterables.append(sub(child, self))
- break
+ self.plugin_iterables.add(sub)
+ for sub in self.plugin_iterables:
+ if child.tag == "{%s}%s" % (sub.namespace, sub.name):
+ self.iterables.append(sub(child, self))
+ break
def setup(self, xml=None):
"""
@@ -287,14 +325,12 @@ class ElementBase(object):
for interface in self.interfaces:
values[interface] = self[interface]
for plugin, stanza in self.plugins.items():
- values[plugin] = stanza._get_stanza_values()
+ values[plugin] = stanza.values
if self.iterables:
iterables = []
for stanza in self.iterables:
- iterables.append(stanza._get_stanza_values())
- iterables[-1].update({
- '__childtag__': "{%s}%s" % (stanza.namespace,
- stanza.name)})
+ iterables.append(stanza.values)
+ iterables[-1]['__childtag__'] = stanza.tag
values['substanzas'] = iterables
return values
@@ -318,7 +354,7 @@ class ElementBase(object):
subclass.name)
if subdict['__childtag__'] == child_tag:
sub = subclass(parent=self)
- sub._set_stanza_values(subdict)
+ sub.values = subdict
self.iterables.append(sub)
break
elif interface in self.interfaces:
@@ -326,7 +362,7 @@ class ElementBase(object):
elif interface in self.plugin_attrib_map:
if interface not in self.plugins:
self.init_plugin(interface)
- self.plugins[interface]._set_stanza_values(value)
+ self.plugins[interface].values = value
return self
def __getitem__(self, attrib):
@@ -371,6 +407,8 @@ class ElementBase(object):
elif attrib in self.plugin_attrib_map:
if attrib not in self.plugins:
self.init_plugin(attrib)
+ if self.plugins[attrib].is_extension:
+ return self.plugins[attrib][attrib]
return self.plugins[attrib]
else:
return ''
@@ -467,8 +505,13 @@ class ElementBase(object):
elif attrib in self.plugin_attrib_map:
if attrib in self.plugins:
xml = self.plugins[attrib].xml
+ if self.plugins[attrib].is_extension:
+ del self.plugins[attrib][attrib]
del self.plugins[attrib]
- self.xml.remove(xml)
+ try:
+ self.xml.remove(xml)
+ except:
+ pass
return self
def _set_attr(self, name, value):
@@ -790,6 +833,28 @@ class ElementBase(object):
"""
return self.__next__()
+ def clear(self):
+ """
+ Remove all XML element contents and plugins.
+
+ Any attribute values will be preserved.
+ """
+ for child in self.xml.getchildren():
+ self.xml.remove(child)
+ for plugin in list(self.plugins.keys()):
+ del self.plugins[plugin]
+ return self
+
+ @classmethod
+ def tag_name(cls):
+ """
+ Return the namespaced name of the stanza's root element.
+
+ For example, for the stanza <foo xmlns="bar" />,
+ stanza.tag would return "{bar}foo".
+ """
+ return "{%s}%s" % (cls.namespace, cls.name)
+
@property
def attrib(self):
"""
@@ -862,13 +927,13 @@ class ElementBase(object):
return False
# Check that this stanza is a superset of the other stanza.
- values = self._get_stanza_values()
+ values = self.values
for key in other.keys():
if key not in values or values[key] != other[key]:
return False
# Check that the other stanza is a superset of this stanza.
- values = other._get_stanza_values()
+ values = other.values
for key in self.keys():
if key not in values or values[key] != self[key]:
return False
@@ -972,7 +1037,6 @@ class StanzaBase(ElementBase):
Attributes:
stream -- The XMLStream instance that will handle sending this stanza.
- tag -- The namespaced version of the stanza's name.
Methods:
set_type -- Set the type of the stanza.
@@ -983,7 +1047,6 @@ class StanzaBase(ElementBase):
get_payload -- Return the stanza's XML contents.
set_payload -- Append to the stanza's XML contents.
del_payload -- Remove the stanza's XML contents.
- clear -- Reset the stanza's XML contents.
reply -- Reset the stanza and modify the 'to' and 'from'
attributes to prepare for sending a reply.
error -- Set the stanza's type to 'error'.
@@ -1098,18 +1161,6 @@ class StanzaBase(ElementBase):
self.clear()
return self
- def clear(self):
- """
- Remove all XML element contents and plugins.
-
- Any attribute values will be preserved.
- """
- for child in self.xml.getchildren():
- self.xml.remove(child)
- for plugin in list(self.plugins.keys()):
- del self.plugins[plugin]
- return self
-
def reply(self):
"""
Reset the stanza and swap its 'from' and 'to' attributes to prepare
diff --git a/sleekxmpp/xmlstream/xmlstream.py b/sleekxmpp/xmlstream/xmlstream.py
index d5c1043b..1cd23fba 100644
--- a/sleekxmpp/xmlstream/xmlstream.py
+++ b/sleekxmpp/xmlstream/xmlstream.py
@@ -292,6 +292,7 @@ class XMLStream(object):
return True
except Socket.error as serr:
error_msg = "Could not connect to %s:%s. Socket Error #%s: %s"
+ self.event('socket_error', serr)
log.error(error_msg % (self.address[0], self.address[1],
serr.errno, serr.strerror))
time.sleep(1)
@@ -327,7 +328,7 @@ class XMLStream(object):
self.filesocket.close()
self.socket.shutdown(Socket.SHUT_RDWR)
except Socket.error as serr:
- pass
+ self.event('socket_error', serr)
finally:
#clear your application state
self.event("disconnected", direct=True)
@@ -734,7 +735,8 @@ class XMLStream(object):
except SystemExit:
log.debug("SystemExit in _process")
self.stop.set()
- except Socket.error:
+ except Socket.error as serr:
+ self.event('socket_error', serr)
log.exception('Socket Error')
except:
if not self.stop.isSet():
@@ -800,7 +802,8 @@ class XMLStream(object):
default_ns = self.default_ns
stanza_type = StanzaBase
for stanza_class in self.__root_stanza:
- if xml.tag == "{%s}%s" % (default_ns, stanza_class.name):
+ if xml.tag == "{%s}%s" % (default_ns, stanza_class.name) or \
+ xml.tag == stanza_class.tag_name():
stanza_type = stanza_class
break
stanza = stanza_type(self, xml)
@@ -825,7 +828,8 @@ class XMLStream(object):
# stanza type applies, a generic StanzaBase stanza will be used.
stanza_type = StanzaBase
for stanza_class in self.__root_stanza:
- if xml.tag == "{%s}%s" % (self.default_ns, stanza_class.name):
+ if xml.tag == "{%s}%s" % (self.default_ns, stanza_class.name) or \
+ xml.tag == stanza_class.tag_name():
stanza_type = stanza_class
break
stanza = stanza_type(self, xml)
@@ -899,7 +903,7 @@ class XMLStream(object):
args[0].exception(e)
elif etype == 'schedule':
try:
- log.debug(args)
+ log.debug('Scheduled event: %s' % args)
handler(*args[0])
except:
log.exception('Error processing scheduled task')
diff --git a/tests/test_stanza_message.py b/tests/test_stanza_message.py
index f06b0253..e55971df 100644
--- a/tests/test_stanza_message.py
+++ b/tests/test_stanza_message.py
@@ -49,7 +49,7 @@ class TestMessageStanzas(SleekTest):
msg['nick']['nick'] = 'A nickname!'
self.check(msg, """
<message>
- <nick xmlns="http://jabber.org/nick/nick">A nickname!</nick>
+ <nick xmlns="http://jabber.org/protocol/nick">A nickname!</nick>
</message>
""")
diff --git a/tests/test_stanza_presence.py b/tests/test_stanza_presence.py
index b8600b6a..2ec43b65 100644
--- a/tests/test_stanza_presence.py
+++ b/tests/test_stanza_presence.py
@@ -59,7 +59,7 @@ class TestPresenceStanzas(SleekTest):
p['nick']['nick'] = 'A nickname!'
self.check(p, """
<presence>
- <nick xmlns="http://jabber.org/nick/nick">A nickname!</nick>
+ <nick xmlns="http://jabber.org/protocol/nick">A nickname!</nick>
</presence>
""")
diff --git a/todo1.0 b/todo1.0
index 191c0e2d..3212a31a 100644
--- a/todo1.0
+++ b/todo1.0
@@ -1,123 +1,62 @@
-ElementBase sub_items not subitem?
-
-*XMPP needs to use JID class instead of lots of fields.
-
-BaseXMPP set_jid, makeIqQuery, getjidresource, getjidbare not needed
-
-Why CamelCase and underscore_names? Document semantics.
-
-conn_tests and sleekxmpp/tests and sleekxmpp/xmlstresm/test.* -> convert to either unit tests, or at least put in same place
-
-Update setup.py - github url, version #
-
-scheduler needs unit tests
-
-ClientXMPP stream:features handler should use new state machine
-
-Write stream tests for startls, features, etc.
-
-
-
--- PEP8 - all files
-
-Need to use spaces
-
-Docstrings are lacking. Need to document attributes and return values.
-
-Organize imports
-
-Use absolute, not relative imports
-
-Fix one-liner if statements
-
-Line length limit of 79 characters
-
-
-
--- Plugins
-
---- xep_0004
-
-Need more unit tests
-
---- xep_0009
-
-Need stanza objects
-
-Need unit tests
-
---- xep_0045
-
-Need to use stanza objects
-
-A few TODO comments for checking roles and using defaults
-
-Need unit tests
-
---- xep_0050
-
-Need unit tests
-
-Need stanza objects - use new xep_0004
-
---- xep_0060
-
-Need unit tests
-
-Need to use existing stanza objects
-
---- xep_0078
-
-Is it useful still?
-
-Need stanza objects/unit tests
-
---- xep_0086
-
-Is there a way to automate setting error codes?
-
-Seems like this should be part of the error stanza by default
-
-Use stanza objects
-
---- xep_0092
-
-Stanza objects
-
-Unit tests
-
---- xep_0199
-
-Stanza objects
-
-Unit tests
-
-Clean commented code
-
-Use the new scheduler
-
-
-
--- Documentation
-
-Document the Zen/Tao/Whatever of SleekXMPP to explain design goals and decisions
-
-Write architecture description
-
-XMPP:TDG needs to be rewritten.
-
-Need to update docs that reference old JID attributes of sleekxmpp objects
-
-Page describing new JID class
-
-Message page needs updating
-
-Iq page needs to be written
-
-Make guides to go with example.py and component_example.py
-
-Page on xmlstream.matchers
-
-Page on xmlstream.handlers, especially waiters
-
-Page on using xmlstream.scheduler
+Plugins:
+ 0004
+ PEP8
+ Stream/Unit tests
+ Fix serialization issue
+ Use OrderedDict for fields/values
+ 0009
+ Review contribution from dannmartens
+ 0012
+ PEP8
+ Documentation
+ Stream/Unit tests
+ 0030
+ Done
+ 0033
+ PEP8
+ Documentation
+ Stream/Unit tests
+ 0045
+ PEP8
+ Documentation
+ Stream/Unit tests
+ 0050
+ Review replacement in github.com/legastero/adhoc
+ 0059
+ Done
+ 0060
+ PEP8
+ Documentation
+ Stream/Unit tests
+ 0078
+ Will require new stream features handling, see stream_features branch.
+ PEP8
+ Documentation
+ Stream/Unit tests
+ 0085
+ PEP8
+ Documentation
+ Stream/Unit tests
+ 0086
+ PEP8
+ Documentation
+ Consider any simplifications.
+ 0092
+ Done
+ 0128
+ Needs complete rewrite to work with new 0030 plugin.
+ 0199
+ PEP8
+ Documentation
+ Stream/Unit tests
+ Needs to use scheduler instead of its own thread.
+ 0202
+ PEP8
+ Documentation
+ Stream/Unit tests
+ 0249
+ Review, minor cleanup
+ gmail_notify
+ PEP8
+ Documentation
+ Stream/Unit tests