mirror of
https://github.com/correl/SleekXMPP.git
synced 2024-11-27 11:09:56 +00:00
Merge branch 'develop' into roster
Conflicts: sleekxmpp/basexmpp.py
This commit is contained in:
commit
de6170a13d
21 changed files with 291 additions and 196 deletions
|
@ -33,7 +33,7 @@ class testps(sleekxmpp.ClientXMPP):
|
||||||
self.node = "pstestnode_%s"
|
self.node = "pstestnode_%s"
|
||||||
self.pshost = pshost
|
self.pshost = pshost
|
||||||
if pshost is None:
|
if pshost is None:
|
||||||
self.pshost = self.server
|
self.pshost = self.boundjid.host
|
||||||
self.nodenum = int(nodenum)
|
self.nodenum = int(nodenum)
|
||||||
self.leafnode = self.nodenum + 1
|
self.leafnode = self.nodenum + 1
|
||||||
self.collectnode = self.nodenum + 2
|
self.collectnode = self.nodenum + 2
|
||||||
|
|
|
@ -16,7 +16,7 @@ import sleekxmpp
|
||||||
from sleekxmpp import plugins
|
from sleekxmpp import plugins
|
||||||
|
|
||||||
import sleekxmpp.roster as roster
|
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.roster import Roster
|
||||||
from sleekxmpp.stanza.nick import Nick
|
from sleekxmpp.stanza.nick import Nick
|
||||||
from sleekxmpp.stanza.htmlim import HTMLIM
|
from sleekxmpp.stanza.htmlim import HTMLIM
|
||||||
|
@ -133,6 +133,10 @@ class BaseXMPP(XMLStream):
|
||||||
Callback('Presence',
|
Callback('Presence',
|
||||||
MatchXPath("{%s}presence" % self.default_ns),
|
MatchXPath("{%s}presence" % self.default_ns),
|
||||||
self._handle_presence))
|
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.add_event_handler('disconnected',
|
||||||
self._handle_disconnected)
|
self._handle_disconnected)
|
||||||
|
@ -165,6 +169,7 @@ class BaseXMPP(XMLStream):
|
||||||
self.register_stanza(Message)
|
self.register_stanza(Message)
|
||||||
self.register_stanza(Iq)
|
self.register_stanza(Iq)
|
||||||
self.register_stanza(Presence)
|
self.register_stanza(Presence)
|
||||||
|
self.register_stanza(StreamError)
|
||||||
|
|
||||||
# Initialize a few default stanza plugins.
|
# Initialize a few default stanza plugins.
|
||||||
register_stanza_plugin(Iq, Roster)
|
register_stanza_plugin(Iq, Roster)
|
||||||
|
@ -606,6 +611,9 @@ class BaseXMPP(XMLStream):
|
||||||
"""When disconnected, reset the roster"""
|
"""When disconnected, reset the roster"""
|
||||||
self.roster = {}
|
self.roster = {}
|
||||||
|
|
||||||
|
def _handle_stream_error(self, error):
|
||||||
|
self.event('stream_error', error)
|
||||||
|
|
||||||
def _handle_message(self, msg):
|
def _handle_message(self, msg):
|
||||||
"""Process incoming message stanzas."""
|
"""Process incoming message stanzas."""
|
||||||
self.event('message', msg)
|
self.event('message', msg)
|
||||||
|
|
|
@ -163,11 +163,13 @@ class ClientXMPP(BaseXMPP):
|
||||||
log.debug("Since no address is supplied," + \
|
log.debug("Since no address is supplied," + \
|
||||||
"attempting SRV lookup.")
|
"attempting SRV lookup.")
|
||||||
try:
|
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)
|
answers = dns.resolver.query(xmpp_srv, dns.rdatatype.SRV)
|
||||||
except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
|
except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
|
||||||
log.debug("No appropriate SRV record found." + \
|
log.debug("No appropriate SRV record found." + \
|
||||||
" Using JID server name.")
|
" Using JID server name.")
|
||||||
|
except (dns.exception.Timeout,):
|
||||||
|
log.debug("DNS resolution timed out.")
|
||||||
else:
|
else:
|
||||||
# Pick a random server, weighted by priority.
|
# Pick a random server, weighted by priority.
|
||||||
|
|
||||||
|
|
|
@ -177,7 +177,10 @@ class xep_0030(base_plugin):
|
||||||
elif node is None:
|
elif node is None:
|
||||||
self._handlers[htype]['jid'][jid] = handler
|
self._handlers[htype]['jid'][jid] = handler
|
||||||
elif jid is None:
|
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
|
self._handlers[htype]['node'][(jid, node)] = handler
|
||||||
else:
|
else:
|
||||||
self._handlers[htype]['node'][(jid, node)] = handler
|
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)
|
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.
|
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.
|
subnode -- Optional node for the item.
|
||||||
ijid -- The JID to modify.
|
ijid -- The JID to modify.
|
||||||
"""
|
"""
|
||||||
|
if not jid:
|
||||||
|
jid = self.xmpp.boundjid.full
|
||||||
kwargs = {'ijid': jid,
|
kwargs = {'ijid': jid,
|
||||||
'name': name,
|
'name': name,
|
||||||
'inode': subnode}
|
'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):
|
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.
|
data -- Optional, custom data to pass to the handler.
|
||||||
"""
|
"""
|
||||||
if jid is None:
|
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:
|
if node is None:
|
||||||
node = ''
|
node = ''
|
||||||
|
|
||||||
|
@ -526,8 +534,12 @@ class xep_0030(base_plugin):
|
||||||
if iq['type'] == 'get':
|
if iq['type'] == 'get':
|
||||||
log.debug("Received disco info query from " + \
|
log.debug("Received disco info query from " + \
|
||||||
"<%s> to <%s>." % (iq['from'], iq['to']))
|
"<%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',
|
info = self._run_node_handler('get_info',
|
||||||
iq['to'].full,
|
jid,
|
||||||
iq['disco_info']['node'],
|
iq['disco_info']['node'],
|
||||||
iq)
|
iq)
|
||||||
iq.reply()
|
iq.reply()
|
||||||
|
@ -552,8 +564,12 @@ class xep_0030(base_plugin):
|
||||||
if iq['type'] == 'get':
|
if iq['type'] == 'get':
|
||||||
log.debug("Received disco items query from " + \
|
log.debug("Received disco items query from " + \
|
||||||
"<%s> to <%s>." % (iq['from'], iq['to']))
|
"<%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',
|
items = self._run_node_handler('get_items',
|
||||||
iq['to'].full,
|
jid,
|
||||||
iq['disco_items']['node'])
|
iq['disco_items']['node'])
|
||||||
iq.reply()
|
iq.reply()
|
||||||
if items:
|
if items:
|
||||||
|
@ -590,3 +606,4 @@ class xep_0030(base_plugin):
|
||||||
"Using default disco#info feature.")
|
"Using default disco#info feature.")
|
||||||
info.add_feature(info.namespace)
|
info.add_feature(info.namespace)
|
||||||
return info
|
return info
|
||||||
|
|
||||||
|
|
|
@ -247,8 +247,8 @@ class StaticDisco(object):
|
||||||
self.add_node(jid, node)
|
self.add_node(jid, node)
|
||||||
self.nodes[(jid, node)]['items'].add_item(
|
self.nodes[(jid, node)]['items'].add_item(
|
||||||
data.get('ijid', ''),
|
data.get('ijid', ''),
|
||||||
node=data.get('inode', None),
|
node=data.get('inode', ''),
|
||||||
name=data.get('name', None))
|
name=data.get('name', ''))
|
||||||
|
|
||||||
def del_item(self, jid, node, data):
|
def del_item(self, jid, node, data):
|
||||||
"""
|
"""
|
||||||
|
@ -262,3 +262,4 @@ class StaticDisco(object):
|
||||||
self.nodes[(jid, node)]['items'].del_item(
|
self.nodes[(jid, node)]['items'].del_item(
|
||||||
data.get('ijid', ''),
|
data.get('ijid', ''),
|
||||||
node=data.get('inode', None))
|
node=data.get('inode', None))
|
||||||
|
|
||||||
|
|
|
@ -316,6 +316,7 @@ class xep_0045(base.base_plugin):
|
||||||
x = ET.Element('{jabber:x:data}x', type='cancel')
|
x = ET.Element('{jabber:x:data}x', type='cancel')
|
||||||
query.append(x)
|
query.append(x)
|
||||||
iq = self.xmpp.makeIqSet(query)
|
iq = self.xmpp.makeIqSet(query)
|
||||||
|
iq['to'] = room
|
||||||
iq.send()
|
iq.send()
|
||||||
|
|
||||||
def setRoomConfig(self, room, config, ifrom=''):
|
def setRoomConfig(self, room, config, ifrom=''):
|
||||||
|
|
|
@ -36,7 +36,7 @@ class xep_0078(base.base_plugin):
|
||||||
log.debug("Starting jabber:iq:auth Authentication")
|
log.debug("Starting jabber:iq:auth Authentication")
|
||||||
auth_request = self.xmpp.makeIqGet()
|
auth_request = self.xmpp.makeIqGet()
|
||||||
auth_request_query = ET.Element('{jabber:iq:auth}query')
|
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 = ET.Element('username')
|
||||||
username.text = self.xmpp.username
|
username.text = self.xmpp.username
|
||||||
auth_request_query.append(username)
|
auth_request_query.append(username)
|
||||||
|
|
|
@ -84,5 +84,5 @@ class xep_0092(base_plugin):
|
||||||
result = iq.send()
|
result = iq.send()
|
||||||
|
|
||||||
if result and result['type'] != 'error':
|
if result and result['type'] != 'error':
|
||||||
return result['software_version']._get_stanza_values()
|
return result['software_version'].values
|
||||||
return False
|
return False
|
||||||
|
|
|
@ -33,7 +33,7 @@ class xep_0199(base.base_plugin):
|
||||||
|
|
||||||
def scheduled_ping(self):
|
def scheduled_ping(self):
|
||||||
log.debug("pinging...")
|
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.")
|
log.debug("Did not recieve ping back in time. Requesting Reconnect.")
|
||||||
self.xmpp.reconnect()
|
self.xmpp.reconnect()
|
||||||
|
|
||||||
|
|
|
@ -27,10 +27,12 @@ class EntityTime(ElementBase):
|
||||||
interfaces = set(('tzo', 'utc'))
|
interfaces = set(('tzo', 'utc'))
|
||||||
sub_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
|
#pass
|
||||||
|
|
||||||
def set_tzo(self, tzo): # TODO: support datetime.tzinfo objects?
|
def set_tzo(self, tzo):
|
||||||
if isinstance(tzo, tzinfo):
|
if isinstance(tzo, tzinfo):
|
||||||
td = datetime.now(tzo).utcoffset() # What if we are faking the time? datetime.now() shouldn't be used here'
|
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
|
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?
|
# Returns a datetime object instead the string. Is this a good idea?
|
||||||
value = self._get_sub_text('utc')
|
value = self._get_sub_text('utc')
|
||||||
if '.' in value:
|
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:
|
else:
|
||||||
return datetime.strptime(value, '%Y-%m-%dT%H:%M:%SZ')
|
return datetime.strptime(value, '%Y-%m-%dT%H:%M:%SZ')
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
|
|
||||||
from sleekxmpp.stanza.error import Error
|
from sleekxmpp.stanza.error import Error
|
||||||
|
from sleekxmpp.stanza.stream_error import StreamError
|
||||||
from sleekxmpp.stanza.iq import Iq
|
from sleekxmpp.stanza.iq import Iq
|
||||||
from sleekxmpp.stanza.message import Message
|
from sleekxmpp.stanza.message import Message
|
||||||
from sleekxmpp.stanza.presence import Presence
|
from sleekxmpp.stanza.presence import Presence
|
||||||
|
|
|
@ -224,4 +224,3 @@ class Iq(RootStanza):
|
||||||
else:
|
else:
|
||||||
StanzaBase._set_stanza_values(self, values)
|
StanzaBase._set_stanza_values(self, values)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ class Nick(ElementBase):
|
||||||
del_nick -- Remove the <nick> element.
|
del_nick -- Remove the <nick> element.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
namespace = 'http://jabber.org/nick/nick'
|
namespace = 'http://jabber.org/protocol/nick'
|
||||||
name = 'nick'
|
name = 'nick'
|
||||||
plugin_attrib = name
|
plugin_attrib = name
|
||||||
interfaces = set(('nick',))
|
interfaces = set(('nick',))
|
||||||
|
|
69
sleekxmpp/stanza/stream_error.py
Normal file
69
sleekxmpp/stanza/stream_error.py
Normal file
|
@ -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'
|
|
@ -183,8 +183,7 @@ class SleekTest(unittest.TestCase):
|
||||||
"""
|
"""
|
||||||
Create and compare several stanza objects to a correct XML string.
|
Create and compare several stanza objects to a correct XML string.
|
||||||
|
|
||||||
If use_values is False, test using getStanzaValues() and
|
If use_values is False, tests using stanza.values will not be used.
|
||||||
setStanzaValues() will not be used.
|
|
||||||
|
|
||||||
Some stanzas provide default values for some interfaces, but
|
Some stanzas provide default values for some interfaces, but
|
||||||
these defaults can be problematic for testing since they can easily
|
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
|
values. These interfaces will be set to their
|
||||||
defaults for the given and generated stanzas to
|
defaults for the given and generated stanzas to
|
||||||
prevent unexpected test failures.
|
prevent unexpected test failures.
|
||||||
use_values -- Indicates if testing using getStanzaValues() and
|
use_values -- Indicates if testing using stanza.values should
|
||||||
setStanzaValues() should be used. Defaults to
|
be used. Defaults to True.
|
||||||
True.
|
|
||||||
"""
|
"""
|
||||||
if method is None and hasattr(self, 'match_method'):
|
if method is None and hasattr(self, 'match_method'):
|
||||||
method = getattr(self, 'match_method')
|
method = getattr(self, 'match_method')
|
||||||
|
@ -242,10 +240,10 @@ class SleekTest(unittest.TestCase):
|
||||||
stanza2 = stanza_class(xml=xml)
|
stanza2 = stanza_class(xml=xml)
|
||||||
|
|
||||||
if use_values:
|
if use_values:
|
||||||
# Using getStanzaValues() and setStanzaValues() will add
|
# Using stanza.values will add XML for any interface that
|
||||||
# XML for any interface that has a default value. We need
|
# has a default value. We need to set those defaults on
|
||||||
# to set those defaults on the existing stanzas and XML
|
# the existing stanzas and XML so that they will compare
|
||||||
# so that they will compare correctly.
|
# correctly.
|
||||||
default_stanza = stanza_class()
|
default_stanza = stanza_class()
|
||||||
if defaults is None:
|
if defaults is None:
|
||||||
known_defaults = {
|
known_defaults = {
|
||||||
|
@ -264,9 +262,9 @@ class SleekTest(unittest.TestCase):
|
||||||
value = default_stanza.xml.attrib[interface]
|
value = default_stanza.xml.attrib[interface]
|
||||||
xml.attrib[interface] = value
|
xml.attrib[interface] = value
|
||||||
|
|
||||||
values = stanza2.getStanzaValues()
|
values = stanza2.values
|
||||||
stanza3 = stanza_class()
|
stanza3 = stanza_class()
|
||||||
stanza3.setStanzaValues(values)
|
stanza3.values = values
|
||||||
|
|
||||||
debug = "Three methods for creating stanzas do not match.\n"
|
debug = "Three methods for creating stanzas do not match.\n"
|
||||||
debug += "Given XML:\n%s\n" % tostring(xml)
|
debug += "Given XML:\n%s\n" % tostring(xml)
|
||||||
|
@ -416,8 +414,7 @@ class SleekTest(unittest.TestCase):
|
||||||
'id', 'stanzapath', 'xpath', and 'mask'.
|
'id', 'stanzapath', 'xpath', and 'mask'.
|
||||||
Defaults to the value of self.match_method.
|
Defaults to the value of self.match_method.
|
||||||
use_values -- Indicates if stanza comparisons should test using
|
use_values -- Indicates if stanza comparisons should test using
|
||||||
getStanzaValues() and setStanzaValues().
|
stanza.values. Defaults to True.
|
||||||
Defaults to True.
|
|
||||||
timeout -- Time to wait in seconds for data to be received by
|
timeout -- Time to wait in seconds for data to be received by
|
||||||
a live connection.
|
a live connection.
|
||||||
"""
|
"""
|
||||||
|
|
4
sleekxmpp/thirdparty/__init__.py
vendored
4
sleekxmpp/thirdparty/__init__.py
vendored
|
@ -0,0 +1,4 @@
|
||||||
|
try:
|
||||||
|
from collections import OrderedDict
|
||||||
|
except:
|
||||||
|
from sleekxmpp.thirdparty.ordereddict import OrderedDict
|
|
@ -14,6 +14,7 @@ from xml.etree import cElementTree as ET
|
||||||
|
|
||||||
from sleekxmpp.xmlstream import JID
|
from sleekxmpp.xmlstream import JID
|
||||||
from sleekxmpp.xmlstream.tostring import tostring
|
from sleekxmpp.xmlstream.tostring import tostring
|
||||||
|
from sleekxmpp.thirdparty import OrderedDict
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
@ -23,17 +24,23 @@ log = logging.getLogger(__name__)
|
||||||
XML_TYPE = type(ET.Element('xml'))
|
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.
|
Associate a stanza object as a plugin for another stanza.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
stanza -- The class of the parent stanza.
|
stanza -- The class of the parent stanza.
|
||||||
plugin -- The class of the plugin 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)
|
tag = "{%s}%s" % (plugin.namespace, plugin.name)
|
||||||
stanza.plugin_attrib_map[plugin.plugin_attrib] = plugin
|
stanza.plugin_attrib_map[plugin.plugin_attrib] = plugin
|
||||||
stanza.plugin_tag_map[tag] = 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.
|
# To maintain backwards compatibility for now, preserve the camel case name.
|
||||||
|
@ -95,10 +102,22 @@ class ElementBase(object):
|
||||||
>>> message['custom']['useful_thing'] = 'foo'
|
>>> message['custom']['useful_thing'] = 'foo'
|
||||||
|
|
||||||
If a plugin provides an interface that is the same as the plugin's
|
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
|
plugin_attrib value, then the plugin's interface may be assigned
|
||||||
directly from the parent stanza, as so:
|
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'] = '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:
|
Class Attributes:
|
||||||
name -- The name of the stanza's main element.
|
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
|
sub_interfaces -- A subset of the set of interfaces which map
|
||||||
to subelements instead of attributes.
|
to subelements instead of attributes.
|
||||||
subitem -- A set of stanza classes which are allowed to
|
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.
|
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
|
plugin_attrib -- The interface name that the stanza uses to be
|
||||||
accessed as a plugin from another stanza.
|
accessed as a plugin from another stanza.
|
||||||
plugin_attrib_map -- A mapping of plugin attribute names with the
|
plugin_attrib_map -- A mapping of plugin attribute names with the
|
||||||
associated plugin stanza classes.
|
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
|
plugin_tag_map -- A mapping of plugin stanza tag names with
|
||||||
the associated plugin stanza classes.
|
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,
|
xml_ns -- The XML namespace,
|
||||||
http://www.w3.org/XML/1998/namespace,
|
http://www.w3.org/XML/1998/namespace,
|
||||||
for use with xml:lang values.
|
for use with xml:lang values.
|
||||||
|
@ -128,6 +156,10 @@ class ElementBase(object):
|
||||||
values -- A dictionary of the stanza's interfaces
|
values -- A dictionary of the stanza's interfaces
|
||||||
and interface values, including plugins.
|
and interface values, including plugins.
|
||||||
|
|
||||||
|
Class Methods
|
||||||
|
tag_name -- Return the namespaced version of the stanza's
|
||||||
|
root element's name.
|
||||||
|
|
||||||
Methods:
|
Methods:
|
||||||
setup -- Initialize the stanza's XML contents.
|
setup -- Initialize the stanza's XML contents.
|
||||||
enable -- Instantiate a stanza plugin.
|
enable -- Instantiate a stanza plugin.
|
||||||
|
@ -160,6 +192,7 @@ class ElementBase(object):
|
||||||
appendxml -- Add XML content to the stanza.
|
appendxml -- Add XML content to the stanza.
|
||||||
pop -- Remove a substanza.
|
pop -- Remove a substanza.
|
||||||
next -- Return the next iterable substanza.
|
next -- Return the next iterable substanza.
|
||||||
|
clear -- Reset the stanza's XML contents.
|
||||||
_fix_ns -- Apply the stanza's namespace to non-namespaced
|
_fix_ns -- Apply the stanza's namespace to non-namespaced
|
||||||
elements in an XPath expression.
|
elements in an XPath expression.
|
||||||
"""
|
"""
|
||||||
|
@ -171,8 +204,10 @@ class ElementBase(object):
|
||||||
types = set(('get', 'set', 'error', None, 'unavailable', 'normal', 'chat'))
|
types = set(('get', 'set', 'error', None, 'unavailable', 'normal', 'chat'))
|
||||||
sub_interfaces = tuple()
|
sub_interfaces = tuple()
|
||||||
plugin_attrib_map = {}
|
plugin_attrib_map = {}
|
||||||
|
plugin_iterables = set()
|
||||||
plugin_tag_map = {}
|
plugin_tag_map = {}
|
||||||
subitem = None
|
subitem = None
|
||||||
|
is_extension = False
|
||||||
xml_ns = 'http://www.w3.org/XML/1998/namespace'
|
xml_ns = 'http://www.w3.org/XML/1998/namespace'
|
||||||
|
|
||||||
def __init__(self, xml=None, parent=None):
|
def __init__(self, xml=None, parent=None):
|
||||||
|
@ -196,9 +231,10 @@ class ElementBase(object):
|
||||||
self.setStanzaValues = self._set_stanza_values
|
self.setStanzaValues = self._set_stanza_values
|
||||||
|
|
||||||
self.xml = xml
|
self.xml = xml
|
||||||
self.plugins = {}
|
self.plugins = OrderedDict()
|
||||||
self.iterables = []
|
self.iterables = []
|
||||||
self._index = 0
|
self._index = 0
|
||||||
|
self.tag = self.tag_name()
|
||||||
if parent is None:
|
if parent is None:
|
||||||
self.parent = None
|
self.parent = None
|
||||||
else:
|
else:
|
||||||
|
@ -218,9 +254,11 @@ class ElementBase(object):
|
||||||
self.plugins[plugin.plugin_attrib] = plugin(child, self)
|
self.plugins[plugin.plugin_attrib] = plugin(child, self)
|
||||||
if self.subitem is not None:
|
if self.subitem is not None:
|
||||||
for sub in self.subitem:
|
for sub in self.subitem:
|
||||||
if child.tag == "{%s}%s" % (sub.namespace, sub.name):
|
self.plugin_iterables.add(sub)
|
||||||
self.iterables.append(sub(child, self))
|
for sub in self.plugin_iterables:
|
||||||
break
|
if child.tag == "{%s}%s" % (sub.namespace, sub.name):
|
||||||
|
self.iterables.append(sub(child, self))
|
||||||
|
break
|
||||||
|
|
||||||
def setup(self, xml=None):
|
def setup(self, xml=None):
|
||||||
"""
|
"""
|
||||||
|
@ -287,14 +325,12 @@ class ElementBase(object):
|
||||||
for interface in self.interfaces:
|
for interface in self.interfaces:
|
||||||
values[interface] = self[interface]
|
values[interface] = self[interface]
|
||||||
for plugin, stanza in self.plugins.items():
|
for plugin, stanza in self.plugins.items():
|
||||||
values[plugin] = stanza._get_stanza_values()
|
values[plugin] = stanza.values
|
||||||
if self.iterables:
|
if self.iterables:
|
||||||
iterables = []
|
iterables = []
|
||||||
for stanza in self.iterables:
|
for stanza in self.iterables:
|
||||||
iterables.append(stanza._get_stanza_values())
|
iterables.append(stanza.values)
|
||||||
iterables[-1].update({
|
iterables[-1]['__childtag__'] = stanza.tag
|
||||||
'__childtag__': "{%s}%s" % (stanza.namespace,
|
|
||||||
stanza.name)})
|
|
||||||
values['substanzas'] = iterables
|
values['substanzas'] = iterables
|
||||||
return values
|
return values
|
||||||
|
|
||||||
|
@ -318,7 +354,7 @@ class ElementBase(object):
|
||||||
subclass.name)
|
subclass.name)
|
||||||
if subdict['__childtag__'] == child_tag:
|
if subdict['__childtag__'] == child_tag:
|
||||||
sub = subclass(parent=self)
|
sub = subclass(parent=self)
|
||||||
sub._set_stanza_values(subdict)
|
sub.values = subdict
|
||||||
self.iterables.append(sub)
|
self.iterables.append(sub)
|
||||||
break
|
break
|
||||||
elif interface in self.interfaces:
|
elif interface in self.interfaces:
|
||||||
|
@ -326,7 +362,7 @@ class ElementBase(object):
|
||||||
elif interface in self.plugin_attrib_map:
|
elif interface in self.plugin_attrib_map:
|
||||||
if interface not in self.plugins:
|
if interface not in self.plugins:
|
||||||
self.init_plugin(interface)
|
self.init_plugin(interface)
|
||||||
self.plugins[interface]._set_stanza_values(value)
|
self.plugins[interface].values = value
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __getitem__(self, attrib):
|
def __getitem__(self, attrib):
|
||||||
|
@ -371,6 +407,8 @@ class ElementBase(object):
|
||||||
elif attrib in self.plugin_attrib_map:
|
elif attrib in self.plugin_attrib_map:
|
||||||
if attrib not in self.plugins:
|
if attrib not in self.plugins:
|
||||||
self.init_plugin(attrib)
|
self.init_plugin(attrib)
|
||||||
|
if self.plugins[attrib].is_extension:
|
||||||
|
return self.plugins[attrib][attrib]
|
||||||
return self.plugins[attrib]
|
return self.plugins[attrib]
|
||||||
else:
|
else:
|
||||||
return ''
|
return ''
|
||||||
|
@ -467,8 +505,13 @@ class ElementBase(object):
|
||||||
elif attrib in self.plugin_attrib_map:
|
elif attrib in self.plugin_attrib_map:
|
||||||
if attrib in self.plugins:
|
if attrib in self.plugins:
|
||||||
xml = self.plugins[attrib].xml
|
xml = self.plugins[attrib].xml
|
||||||
|
if self.plugins[attrib].is_extension:
|
||||||
|
del self.plugins[attrib][attrib]
|
||||||
del self.plugins[attrib]
|
del self.plugins[attrib]
|
||||||
self.xml.remove(xml)
|
try:
|
||||||
|
self.xml.remove(xml)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def _set_attr(self, name, value):
|
def _set_attr(self, name, value):
|
||||||
|
@ -790,6 +833,28 @@ class ElementBase(object):
|
||||||
"""
|
"""
|
||||||
return self.__next__()
|
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
|
@property
|
||||||
def attrib(self):
|
def attrib(self):
|
||||||
"""
|
"""
|
||||||
|
@ -862,13 +927,13 @@ class ElementBase(object):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Check that this stanza is a superset of the other stanza.
|
# Check that this stanza is a superset of the other stanza.
|
||||||
values = self._get_stanza_values()
|
values = self.values
|
||||||
for key in other.keys():
|
for key in other.keys():
|
||||||
if key not in values or values[key] != other[key]:
|
if key not in values or values[key] != other[key]:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Check that the other stanza is a superset of this stanza.
|
# Check that the other stanza is a superset of this stanza.
|
||||||
values = other._get_stanza_values()
|
values = other.values
|
||||||
for key in self.keys():
|
for key in self.keys():
|
||||||
if key not in values or values[key] != self[key]:
|
if key not in values or values[key] != self[key]:
|
||||||
return False
|
return False
|
||||||
|
@ -972,7 +1037,6 @@ class StanzaBase(ElementBase):
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
stream -- The XMLStream instance that will handle sending this stanza.
|
stream -- The XMLStream instance that will handle sending this stanza.
|
||||||
tag -- The namespaced version of the stanza's name.
|
|
||||||
|
|
||||||
Methods:
|
Methods:
|
||||||
set_type -- Set the type of the stanza.
|
set_type -- Set the type of the stanza.
|
||||||
|
@ -983,7 +1047,6 @@ class StanzaBase(ElementBase):
|
||||||
get_payload -- Return the stanza's XML contents.
|
get_payload -- Return the stanza's XML contents.
|
||||||
set_payload -- Append to the stanza's XML contents.
|
set_payload -- Append to the stanza's XML contents.
|
||||||
del_payload -- Remove 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'
|
reply -- Reset the stanza and modify the 'to' and 'from'
|
||||||
attributes to prepare for sending a reply.
|
attributes to prepare for sending a reply.
|
||||||
error -- Set the stanza's type to 'error'.
|
error -- Set the stanza's type to 'error'.
|
||||||
|
@ -1098,18 +1161,6 @@ class StanzaBase(ElementBase):
|
||||||
self.clear()
|
self.clear()
|
||||||
return self
|
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):
|
def reply(self):
|
||||||
"""
|
"""
|
||||||
Reset the stanza and swap its 'from' and 'to' attributes to prepare
|
Reset the stanza and swap its 'from' and 'to' attributes to prepare
|
||||||
|
|
|
@ -292,6 +292,7 @@ class XMLStream(object):
|
||||||
return True
|
return True
|
||||||
except Socket.error as serr:
|
except Socket.error as serr:
|
||||||
error_msg = "Could not connect to %s:%s. Socket Error #%s: %s"
|
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],
|
log.error(error_msg % (self.address[0], self.address[1],
|
||||||
serr.errno, serr.strerror))
|
serr.errno, serr.strerror))
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
@ -327,7 +328,7 @@ class XMLStream(object):
|
||||||
self.filesocket.close()
|
self.filesocket.close()
|
||||||
self.socket.shutdown(Socket.SHUT_RDWR)
|
self.socket.shutdown(Socket.SHUT_RDWR)
|
||||||
except Socket.error as serr:
|
except Socket.error as serr:
|
||||||
pass
|
self.event('socket_error', serr)
|
||||||
finally:
|
finally:
|
||||||
#clear your application state
|
#clear your application state
|
||||||
self.event("disconnected", direct=True)
|
self.event("disconnected", direct=True)
|
||||||
|
@ -734,7 +735,8 @@ class XMLStream(object):
|
||||||
except SystemExit:
|
except SystemExit:
|
||||||
log.debug("SystemExit in _process")
|
log.debug("SystemExit in _process")
|
||||||
self.stop.set()
|
self.stop.set()
|
||||||
except Socket.error:
|
except Socket.error as serr:
|
||||||
|
self.event('socket_error', serr)
|
||||||
log.exception('Socket Error')
|
log.exception('Socket Error')
|
||||||
except:
|
except:
|
||||||
if not self.stop.isSet():
|
if not self.stop.isSet():
|
||||||
|
@ -800,7 +802,8 @@ class XMLStream(object):
|
||||||
default_ns = self.default_ns
|
default_ns = self.default_ns
|
||||||
stanza_type = StanzaBase
|
stanza_type = StanzaBase
|
||||||
for stanza_class in self.__root_stanza:
|
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
|
stanza_type = stanza_class
|
||||||
break
|
break
|
||||||
stanza = stanza_type(self, xml)
|
stanza = stanza_type(self, xml)
|
||||||
|
@ -825,7 +828,8 @@ class XMLStream(object):
|
||||||
# stanza type applies, a generic StanzaBase stanza will be used.
|
# stanza type applies, a generic StanzaBase stanza will be used.
|
||||||
stanza_type = StanzaBase
|
stanza_type = StanzaBase
|
||||||
for stanza_class in self.__root_stanza:
|
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
|
stanza_type = stanza_class
|
||||||
break
|
break
|
||||||
stanza = stanza_type(self, xml)
|
stanza = stanza_type(self, xml)
|
||||||
|
@ -899,7 +903,7 @@ class XMLStream(object):
|
||||||
args[0].exception(e)
|
args[0].exception(e)
|
||||||
elif etype == 'schedule':
|
elif etype == 'schedule':
|
||||||
try:
|
try:
|
||||||
log.debug(args)
|
log.debug('Scheduled event: %s' % args)
|
||||||
handler(*args[0])
|
handler(*args[0])
|
||||||
except:
|
except:
|
||||||
log.exception('Error processing scheduled task')
|
log.exception('Error processing scheduled task')
|
||||||
|
|
|
@ -49,7 +49,7 @@ class TestMessageStanzas(SleekTest):
|
||||||
msg['nick']['nick'] = 'A nickname!'
|
msg['nick']['nick'] = 'A nickname!'
|
||||||
self.check(msg, """
|
self.check(msg, """
|
||||||
<message>
|
<message>
|
||||||
<nick xmlns="http://jabber.org/nick/nick">A nickname!</nick>
|
<nick xmlns="http://jabber.org/protocol/nick">A nickname!</nick>
|
||||||
</message>
|
</message>
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,7 @@ class TestPresenceStanzas(SleekTest):
|
||||||
p['nick']['nick'] = 'A nickname!'
|
p['nick']['nick'] = 'A nickname!'
|
||||||
self.check(p, """
|
self.check(p, """
|
||||||
<presence>
|
<presence>
|
||||||
<nick xmlns="http://jabber.org/nick/nick">A nickname!</nick>
|
<nick xmlns="http://jabber.org/protocol/nick">A nickname!</nick>
|
||||||
</presence>
|
</presence>
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
|
185
todo1.0
185
todo1.0
|
@ -1,123 +1,62 @@
|
||||||
ElementBase sub_items not subitem?
|
Plugins:
|
||||||
|
0004
|
||||||
*XMPP needs to use JID class instead of lots of fields.
|
PEP8
|
||||||
|
Stream/Unit tests
|
||||||
BaseXMPP set_jid, makeIqQuery, getjidresource, getjidbare not needed
|
Fix serialization issue
|
||||||
|
Use OrderedDict for fields/values
|
||||||
Why CamelCase and underscore_names? Document semantics.
|
0009
|
||||||
|
Review contribution from dannmartens
|
||||||
conn_tests and sleekxmpp/tests and sleekxmpp/xmlstresm/test.* -> convert to either unit tests, or at least put in same place
|
0012
|
||||||
|
PEP8
|
||||||
Update setup.py - github url, version #
|
Documentation
|
||||||
|
Stream/Unit tests
|
||||||
scheduler needs unit tests
|
0030
|
||||||
|
Done
|
||||||
ClientXMPP stream:features handler should use new state machine
|
0033
|
||||||
|
PEP8
|
||||||
Write stream tests for startls, features, etc.
|
Documentation
|
||||||
|
Stream/Unit tests
|
||||||
|
0045
|
||||||
|
PEP8
|
||||||
-- PEP8 - all files
|
Documentation
|
||||||
|
Stream/Unit tests
|
||||||
Need to use spaces
|
0050
|
||||||
|
Review replacement in github.com/legastero/adhoc
|
||||||
Docstrings are lacking. Need to document attributes and return values.
|
0059
|
||||||
|
Done
|
||||||
Organize imports
|
0060
|
||||||
|
PEP8
|
||||||
Use absolute, not relative imports
|
Documentation
|
||||||
|
Stream/Unit tests
|
||||||
Fix one-liner if statements
|
0078
|
||||||
|
Will require new stream features handling, see stream_features branch.
|
||||||
Line length limit of 79 characters
|
PEP8
|
||||||
|
Documentation
|
||||||
|
Stream/Unit tests
|
||||||
|
0085
|
||||||
-- Plugins
|
PEP8
|
||||||
|
Documentation
|
||||||
--- xep_0004
|
Stream/Unit tests
|
||||||
|
0086
|
||||||
Need more unit tests
|
PEP8
|
||||||
|
Documentation
|
||||||
--- xep_0009
|
Consider any simplifications.
|
||||||
|
0092
|
||||||
Need stanza objects
|
Done
|
||||||
|
0128
|
||||||
Need unit tests
|
Needs complete rewrite to work with new 0030 plugin.
|
||||||
|
0199
|
||||||
--- xep_0045
|
PEP8
|
||||||
|
Documentation
|
||||||
Need to use stanza objects
|
Stream/Unit tests
|
||||||
|
Needs to use scheduler instead of its own thread.
|
||||||
A few TODO comments for checking roles and using defaults
|
0202
|
||||||
|
PEP8
|
||||||
Need unit tests
|
Documentation
|
||||||
|
Stream/Unit tests
|
||||||
--- xep_0050
|
0249
|
||||||
|
Review, minor cleanup
|
||||||
Need unit tests
|
gmail_notify
|
||||||
|
PEP8
|
||||||
Need stanza objects - use new xep_0004
|
Documentation
|
||||||
|
Stream/Unit tests
|
||||||
--- 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
|
|
||||||
|
|
Loading…
Reference in a new issue