Compare commits

...

11 Commits

Author SHA1 Message Date
mathieui
36824379c3 slixmpp 1.2.1
Fix a few bugs along with the testsuite, and remove the asyncio loop
monkeypatch hack.
2016-10-05 20:32:32 +02:00
mathieui
a0a37c19ff Remove monkeypatching hack on the event loop
This allowed us to schedule events in-order later in the event loop, but
was detrimental to using other event loops and debugging.
2016-10-05 20:19:07 +02:00
mathieui
1b5fe57a5e Fix XEP-0060 tests 2016-10-04 21:21:55 +02:00
mathieui
5da31db0c7 Fix stanza accessors case in tests
They were using deprecated (and-removed) style.
2016-10-04 21:15:01 +02:00
mathieui
f8cea760b6 Fix the gmail_notify plugin 2016-10-04 21:10:10 +02:00
mathieui
5ef01ecdd1 Fix XEP-0033
Re-add relevant stanza methods, broken in 7cd1cf32ae
2016-10-04 19:47:11 +02:00
mathieui
62aafe0ee7 Attrib property has been removed 2016-10-04 19:43:45 +02:00
mathieui
cf3f36ac52 Set unset part of a JID to empty string instead of None
it breaks assumptions on the type of the value
2016-10-04 19:42:05 +02:00
mathieui
b88d2ecd77 Add more checks in the XEP-0060 stanza building
Try to not append slixmpp stanzas to ElementTree objects.
2016-10-04 19:31:49 +02:00
mathieui
e691850a2b Fix XEP-0128
Broken since 125336aeee due to unforeseen consequences of a variable
removal.
2016-10-04 19:26:03 +02:00
mathieui
d4bff8dee6 Fix XEP-0009
Broken since 3a9b45e4f because of an overzealous cleanup.
2016-10-04 19:23:21 +02:00
12 changed files with 66 additions and 92 deletions

View File

@@ -79,7 +79,7 @@ def _validate_node(node):
:returns: The local portion of a JID, as validated by nodeprep.
"""
if node is None:
return None
return ''
try:
node = nodeprep(node)
@@ -160,7 +160,7 @@ def _validate_resource(resource):
:returns: The local portion of a JID, as validated by resourceprep.
"""
if resource is None:
return None
return ''
try:
resource = resourceprep(resource)

View File

@@ -23,13 +23,13 @@ class GmailQuery(ElementBase):
plugin_attrib = 'gmail'
interfaces = set(('newer-than-time', 'newer-than-tid', 'q', 'search'))
def getSearch(self):
def get_search(self):
return self['q']
def setSearch(self, search):
def set_search(self, search):
self['q'] = search
def delSearch(self):
def del_search(self):
del self['q']
@@ -40,17 +40,17 @@ class MailBox(ElementBase):
interfaces = set(('result-time', 'total-matched', 'total-estimate',
'url', 'threads', 'matched', 'estimate'))
def getThreads(self):
def get_threads(self):
threads = []
for threadXML in self.xml.findall('{%s}%s' % (MailThread.namespace,
MailThread.name)):
threads.append(MailThread(xml=threadXML, parent=None))
return threads
def getMatched(self):
def get_matched(self):
return self['total-matched']
def getEstimate(self):
def get_estimate(self):
return self['total-estimate'] == '1'
@@ -62,7 +62,7 @@ class MailThread(ElementBase):
'senders', 'url', 'labels', 'subject', 'snippet'))
sub_interfaces = set(('labels', 'subject', 'snippet'))
def getSenders(self):
def get_senders(self):
senders = []
sendersXML = self.xml.find('{%s}senders' % self.namespace)
if sendersXML is not None:
@@ -77,10 +77,10 @@ class MailSender(ElementBase):
plugin_attrib = 'sender'
interfaces = set(('address', 'name', 'originator', 'unread'))
def getOriginator(self):
def get_originator(self):
return self.xml.attrib.get('originator', '0') == '1'
def getUnread(self):
def get_unread(self):
return self.xml.attrib.get('unread', '0') == '1'

View File

@@ -92,13 +92,13 @@ def _py2xml(*args):
def xml2py(params):
namespace = 'jabber:iq:rpc'
vals = []
for param in params.xml.findall('{%s}param' % namespace):
for param in params.findall('{%s}param' % namespace):
vals.append(_xml2py(param.find('{%s}value' % namespace)))
return vals
def _xml2py(value):
namespace = 'jabber:iq:rpc'
find_value = value.xml.find
find_value = value.find
if find_value('{%s}nil' % namespace) is not None:
return None
if find_value('{%s}i4' % namespace) is not None:

View File

@@ -117,9 +117,12 @@ for atype in ('all', 'bcc', 'cc', 'noreply', 'replyroom', 'replyto', 'to'):
setattr(Addresses, "set_%s" % atype, set_multi)
setattr(Addresses, "del_%s" % atype, del_multi)
# To retain backwards compatibility:
if atype == 'all':
Addresses.interfaces.add('addresses')
setattr(Addresses, "get_addresses", get_multi)
setattr(Addresses, "set_addresses", set_multi)
setattr(Addresses, "del_addresses", del_multi)
register_stanza_plugin(Addresses, Address, iterable=True)

View File

@@ -206,7 +206,10 @@ class Options(ElementBase):
return form
def set_options(self, value):
self.xml.append(value)
if isinstance(value, ElementBase):
self.xml.append(value.xml)
else:
self.xml.append(value)
return self
def del_options(self):
@@ -238,7 +241,10 @@ class PublishOptions(ElementBase):
if value is None:
self.del_publish_options()
else:
self.xml.append(value)
if isinstance(value, ElementBase):
self.xml.append(value.xml)
else:
self.xml.append(value)
return self
def del_publish_options(self):

View File

@@ -38,9 +38,8 @@ class StaticExtendedDisco(object):
The data parameter may provide:
data -- Either a single data form, or a list of data forms.
"""
with self.static.lock:
self.del_extended_info(jid, node, ifrom, data)
self.add_extended_info(jid, node, ifrom, data)
self.del_extended_info(jid, node, ifrom, data)
self.add_extended_info(jid, node, ifrom, data)
def add_extended_info(self, jid, node, ifrom, data):
"""
@@ -49,16 +48,15 @@ class StaticExtendedDisco(object):
The data parameter may provide:
data -- Either a single data form, or a list of data forms.
"""
with self.static.lock:
self.static.add_node(jid, node)
self.static.add_node(jid, node)
forms = data.get('data', [])
if not isinstance(forms, list):
forms = [forms]
forms = data.get('data', [])
if not isinstance(forms, list):
forms = [forms]
info = self.static.get_node(jid, node)['info']
for form in forms:
info.append(form)
info = self.static.get_node(jid, node)['info']
for form in forms:
info.append(form)
def del_extended_info(self, jid, node, ifrom, data):
"""
@@ -66,8 +64,7 @@ class StaticExtendedDisco(object):
The data parameter is not used.
"""
with self.static.lock:
if self.static.node_exists(jid, node):
info = self.static.get_node(jid, node)['info']
for form in info['substanza']:
info.xml.remove(form.xml)
if self.static.node_exists(jid, node):
info = self.static.get_node(jid, node)['info']
for form in info['substanza']:
info.xml.remove(form.xml)

View File

@@ -9,5 +9,5 @@
# We don't want to have to import the entire library
# just to get the version info for setup.py
__version__ = '1.2'
__version_info__ = (1, 2)
__version__ = '1.2.1'
__version_info__ = (1, 2, 1)

View File

@@ -1,38 +1,10 @@
"""
A module that monkey patches the standard asyncio module to add an
idle_call() method to the main loop. This method is used to execute a
callback whenever the loop is not busy handling anything else. This means
that it is a callback with lower priority than IO, timer, or even
call_soon() ones. These callback are called only once each.
asyncio-related utilities
"""
import asyncio
from asyncio import events
from functools import wraps
import collections
def idle_call(self, callback):
if asyncio.iscoroutinefunction(callback):
raise TypeError("coroutines cannot be used with idle_call()")
handle = events.Handle(callback, [], self)
self._idle.append(handle)
def my_run_once(self):
if self._idle:
self._ready.append(events.Handle(lambda: None, (), self))
real_run_once(self)
if self._idle:
handle = self._idle.popleft()
handle._run()
cls = asyncio.get_event_loop().__class__
cls._idle = collections.deque()
cls.idle_call = idle_call
real_run_once = cls._run_once
cls._run_once = my_run_once
def future_wrapper(func):
"""
Make sure the result of a function call is an asyncio.Future()

View File

@@ -380,7 +380,7 @@ class XMLStream(asyncio.BaseProtocol):
elif self.xml_depth == 1:
# A stanza is an XML element that is a direct child of
# the root element, hence the check of depth == 1
self.loop.idle_call(functools.partial(self.__spawn_event, xml))
self._spawn_event(xml)
if self.xml_root is not None:
# Keep the root element empty of children to
# save on memory use.
@@ -893,7 +893,7 @@ class XMLStream(asyncio.BaseProtocol):
stanza['lang'] = self.peer_default_lang
return stanza
def __spawn_event(self, xml):
def _spawn_event(self, xml):
"""
Analyze incoming XML stanzas and convert them into stanza
objects if applicable and queue stream events to be processed

View File

@@ -142,7 +142,7 @@ class TestElementBase(SlixTest):
interfaces = set(('bar', 'baz', 'qux'))
sub_interfaces = set(('baz',))
def getQux(self):
def get_qux(self):
return 'qux'
class TestStanzaPlugin(ElementBase):
@@ -188,7 +188,7 @@ class TestElementBase(SlixTest):
interfaces = set(('bar', 'baz', 'qux'))
sub_interfaces = set(('baz',))
def setQux(self, value):
def set_qux(self, value):
pass
class TestStanzaPlugin(ElementBase):
@@ -222,7 +222,7 @@ class TestElementBase(SlixTest):
interfaces = set(('bar', 'baz', 'qux'))
sub_interfaces = set(('bar',))
def delQux(self):
def del_qux(self):
pass
class TestStanzaPlugin(ElementBase):
@@ -300,14 +300,14 @@ class TestElementBase(SlixTest):
namespace = "foo"
interfaces = set(('bar',))
def setBar(self, value):
def set_bar(self, value):
wrapper = ET.Element("{foo}wrapper")
bar = ET.Element("{foo}bar")
bar.text = value
wrapper.append(bar)
self.xml.append(wrapper)
def getBar(self):
def get_bar(self):
return self._get_sub_text("wrapper/bar", default="not found")
stanza = TestStanza()
@@ -333,16 +333,16 @@ class TestElementBase(SlixTest):
namespace = "foo"
interfaces = set(('bar', 'baz'))
def setBaz(self, value):
def set_baz(self, value):
self._set_sub_text("wrapper/baz", text=value)
def getBaz(self):
def get_baz(self):
return self._get_sub_text("wrapper/baz")
def setBar(self, value):
def set_bar(self, value):
self._set_sub_text("wrapper/bar", text=value)
def getBar(self):
def get_bar(self):
return self._get_sub_text("wrapper/bar")
stanza = TestStanza()
@@ -384,22 +384,22 @@ class TestElementBase(SlixTest):
namespace = "foo"
interfaces = set(('bar', 'baz'))
def setBar(self, value):
def set_bar(self, value):
self._set_sub_text("path/to/only/bar", value)
def getBar(self):
def get_bar(self):
return self._get_sub_text("path/to/only/bar")
def delBar(self):
def del_bar(self):
self._del_sub("path/to/only/bar")
def setBaz(self, value):
def set_baz(self, value):
self._set_sub_text("path/to/just/baz", value)
def getBaz(self):
def get_baz(self):
return self._get_sub_text("path/to/just/baz")
def delBaz(self):
def del_baz(self):
self._del_sub("path/to/just/baz")
stanza = TestStanza()
@@ -466,10 +466,10 @@ class TestElementBase(SlixTest):
interfaces = set(('bar','baz', 'qux'))
sub_interfaces = set(('qux',))
def setQux(self, value):
def set_qux(self, value):
self._set_sub_text('qux', text=value)
def getQux(self):
def get_qux(self):
return self._get_sub_text('qux')
class TestStanzaPlugin(ElementBase):

View File

@@ -20,12 +20,6 @@ class TestMessageStanzas(SlixTest):
msg = msg.reply()
self.failUnless(str(msg['to']) == 'room@someservice.someserver.tld')
def testAttribProperty(self):
"Test attrib property returning self"
msg = self.Message()
msg.attrib.attrib.attrib['to'] = 'usr@server.tld'
self.failUnless(str(msg['to']) == 'usr@server.tld')
def testHTMLPlugin(self):
"Test message/html/body stanza"
msg = self.Message()

View File

@@ -136,11 +136,11 @@ class TestPubsubStanzas(SlixTest):
iq = self.Iq()
iq['pubsub_owner']['default']
iq['pubsub_owner']['default']['node'] = 'mynode'
iq['pubsub_owner']['default']['form'].addField('pubsub#title',
iq['pubsub_owner']['default']['form'].add_field('pubsub#title',
ftype='text-single',
value='This thing is awesome')
self.check(iq, """
<iq id="0">
<iq id="0">
<pubsub xmlns="http://jabber.org/protocol/pubsub#owner">
<default node="mynode">
<x xmlns="jabber:x:data" type="form">
@@ -161,7 +161,8 @@ class TestPubsubStanzas(SlixTest):
iq['pubsub']['subscribe']['options']['node'] = 'cheese'
iq['pubsub']['subscribe']['options']['jid'] = 'fritzy@netflint.net/slixmpp'
form = xep_0004.Form()
form.addField('pubsub#title', ftype='text-single', value='this thing is awesome')
form['type'] = 'submit'
form.add_field('pubsub#title', ftype='text-single', value='this thing is awesome')
iq['pubsub']['subscribe']['options']['options'] = form
self.check(iq, """
<iq id="0">
@@ -201,6 +202,7 @@ class TestPubsubStanzas(SlixTest):
iq['pubsub']['publish'].append(item)
iq['pubsub']['publish'].append(item2)
form = xep_0004.Form()
form['type'] = 'submit'
form.addField('pubsub#description', ftype='text-single', value='this thing is awesome')
iq['pubsub']['publish_options'] = form
@@ -253,7 +255,7 @@ class TestPubsubStanzas(SlixTest):
pub = iq['pubsub']
pub['create']['node'] = 'testnode2'
pub['configure']['form']['type'] = 'submit'
pub['configure']['form'].setFields([
pub['configure']['form'].set_fields([
('FORM_TYPE', {'type': 'hidden',
'value': 'http://jabber.org/protocol/pubsub#node_config'}),
('pubsub#node_type', {'type': 'list-single',