Merge branch 'develop' into roster

Conflicts:
	sleekxmpp/basexmpp.py
This commit is contained in:
Lance Stout 2011-02-02 09:13:22 -05:00
commit de6170a13d
21 changed files with 291 additions and 196 deletions

View file

@ -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

View file

@ -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)

View file

@ -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.

View file

@ -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

View file

@ -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))

View file

@ -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=''):

View file

@ -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)

View file

@ -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

View file

@ -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()

View file

@ -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')

View file

@ -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

View file

@ -224,4 +224,3 @@ class Iq(RootStanza):
else:
StanzaBase._set_stanza_values(self, values)
return self

View file

@ -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',))

View 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'

View file

@ -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.
"""

View file

@ -0,0 +1,4 @@
try:
from collections import OrderedDict
except:
from sleekxmpp.thirdparty.ordereddict import OrderedDict

View file

@ -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

View file

@ -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')

View file

@ -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>
""")

View file

@ -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>
""")

185
todo1.0
View file

@ -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