summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--slixmpp/plugins/xep_0030/disco.py175
-rw-r--r--slixmpp/plugins/xep_0030/static.py12
-rw-r--r--tests/test_stream_xep_0030.py9
3 files changed, 125 insertions, 71 deletions
diff --git a/slixmpp/plugins/xep_0030/disco.py b/slixmpp/plugins/xep_0030/disco.py
index 9c4c5269..6aeadc84 100644
--- a/slixmpp/plugins/xep_0030/disco.py
+++ b/slixmpp/plugins/xep_0030/disco.py
@@ -6,13 +6,13 @@
import asyncio
import logging
-
+from asyncio import Future
from typing import Optional, Callable
from slixmpp import Iq
from slixmpp import future_wrapper
from slixmpp.plugins import BasePlugin
-from slixmpp.xmlstream.handler import Callback
+from slixmpp.xmlstream.handler import Callback, CoroutineCallback
from slixmpp.xmlstream.matcher import StanzaPath
from slixmpp.xmlstream import register_stanza_plugin, JID
from slixmpp.plugins.xep_0030 import stanza, DiscoInfo, DiscoItems
@@ -91,12 +91,12 @@ class XEP_0030(BasePlugin):
Start the XEP-0030 plugin.
"""
self.xmpp.register_handler(
- Callback('Disco Info',
+ CoroutineCallback('Disco Info',
StanzaPath('iq/disco_info'),
self._handle_disco_info))
self.xmpp.register_handler(
- Callback('Disco Items',
+ CoroutineCallback('Disco Items',
StanzaPath('iq/disco_items'),
self._handle_disco_items))
@@ -228,10 +228,13 @@ class XEP_0030(BasePlugin):
self.api.restore_default(op, jid, node)
def supports(self, jid=None, node=None, feature=None, local=False,
- cached=True, ifrom=None):
+ cached=True, ifrom=None) -> Future:
"""
Check if a JID supports a given feature.
+ .. versionchanged:: 1.8.0
+ This function now returns a Future.
+
Return values:
:param True: The feature is supported
:param False: The feature is not listed as supported
@@ -259,10 +262,13 @@ class XEP_0030(BasePlugin):
return self.api['supports'](jid, node, ifrom, data)
def has_identity(self, jid=None, node=None, category=None, itype=None,
- lang=None, local=False, cached=True, ifrom=None):
+ lang=None, local=False, cached=True, ifrom=None) -> Future:
"""
Check if a JID provides a given identity.
+ .. versionchanged:: 1.8.0
+ This function now returns a Future.
+
Return values:
:param True: The identity is provided
:param False: The identity is not listed
@@ -324,8 +330,7 @@ class XEP_0030(BasePlugin):
callback(results)
return results
- @future_wrapper
- def get_info(self, jid=None, node=None, local=None,
+ async def get_info(self, jid=None, node=None, local=None,
cached=None, **kwargs):
"""
Retrieve the disco#info results from a given JID/node combination.
@@ -338,6 +343,9 @@ class XEP_0030(BasePlugin):
If requesting items from a local JID/node, then only a DiscoInfo
stanza will be returned. Otherwise, an Iq stanza will be returned.
+ .. versionchanged:: 1.8.0
+ This function is now a coroutine.
+
:param jid: Request info from this JID.
:param node: The particular node to query.
:param local: If true, then the query is for a JID/node
@@ -369,18 +377,21 @@ class XEP_0030(BasePlugin):
if local:
log.debug("Looking up local disco#info data " + \
"for %s, node %s.", jid, node)
- info = self.api['get_info'](jid, node,
- kwargs.get('ifrom', None),
- kwargs)
+ info = await self.api['get_info'](
+ jid, node, kwargs.get('ifrom', None),
+ kwargs
+ )
info = self._fix_default_info(info)
return self._wrap(kwargs.get('ifrom', None), jid, info)
if cached:
log.debug("Looking up cached disco#info data " + \
"for %s, node %s.", jid, node)
- info = self.api['get_cached_info'](jid, node,
- kwargs.get('ifrom', None),
- kwargs)
+ info = await self.api['get_cached_info'](
+ jid, node,
+ kwargs.get('ifrom', None),
+ kwargs
+ )
if info is not None:
return self._wrap(kwargs.get('ifrom', None), jid, info)
@@ -390,21 +401,24 @@ class XEP_0030(BasePlugin):
iq['to'] = jid
iq['type'] = 'get'
iq['disco_info']['node'] = node if node else ''
- return iq.send(timeout=kwargs.get('timeout', None),
+ return await iq.send(timeout=kwargs.get('timeout', None),
callback=kwargs.get('callback', None),
timeout_callback=kwargs.get('timeout_callback', None))
- def set_info(self, jid=None, node=None, info=None):
+ def set_info(self, jid=None, node=None, info=None) -> Future:
"""
Set the disco#info data for a JID/node based on an existing
disco#info stanza.
+
+ .. versionchanged:: 1.8.0
+ This function now returns a Future.
+
"""
if isinstance(info, Iq):
info = info['disco_info']
- self.api['set_info'](jid, node, None, info)
+ return self.api['set_info'](jid, node, None, info)
- @future_wrapper
- def get_items(self, jid=None, node=None, local=False, **kwargs):
+ async def get_items(self, jid=None, node=None, local=False, **kwargs):
"""
Retrieve the disco#items results from a given JID/node combination.
@@ -416,6 +430,9 @@ class XEP_0030(BasePlugin):
If requesting items from a local JID/node, then only a DiscoItems
stanza will be returned. Otherwise, an Iq stanza will be returned.
+ .. versionchanged:: 1.8.0
+ This function is now a coroutine.
+
:param jid: Request info from this JID.
:param node: The particular node to query.
:param local: If true, then the query is for a JID/node
@@ -428,7 +445,7 @@ class XEP_0030(BasePlugin):
Otherwise the parameter is ignored.
"""
if local or local is None and jid is None:
- items = self.api['get_items'](jid, node,
+ items = await self.api['get_items'](jid, node,
kwargs.get('ifrom', None),
kwargs)
return self._wrap(kwargs.get('ifrom', None), jid, items)
@@ -440,43 +457,52 @@ class XEP_0030(BasePlugin):
iq['type'] = 'get'
iq['disco_items']['node'] = node if node else ''
if kwargs.get('iterator', False) and self.xmpp['xep_0059']:
- raise NotImplementedError("XEP 0059 has not yet been fixed")
return self.xmpp['xep_0059'].iterate(iq, 'disco_items')
else:
- return iq.send(timeout=kwargs.get('timeout', None),
+ return await iq.send(timeout=kwargs.get('timeout', None),
callback=kwargs.get('callback', None),
timeout_callback=kwargs.get('timeout_callback', None))
- def set_items(self, jid=None, node=None, **kwargs):
+ def set_items(self, jid=None, node=None, **kwargs) -> Future:
"""
Set or replace all items for the specified JID/node combination.
The given items must be in a list or set where each item is a
tuple of the form: (jid, node, name).
+
+ .. versionchanged:: 1.8.0
+ This function now returns a Future.
+
:param jid: The JID to modify.
:param node: Optional node to modify.
:param items: A series of items in tuple format.
"""
- self.api['set_items'](jid, node, None, kwargs)
+ return self.api['set_items'](jid, node, None, kwargs)
- def del_items(self, jid=None, node=None, **kwargs):
+ def del_items(self, jid=None, node=None, **kwargs) -> Future:
"""
Remove all items from the given JID/node combination.
+ .. versionchanged:: 1.8.0
+ This function now returns a Future.
+
Arguments:
:param jid: The JID to modify.
:param node: Optional node to modify.
"""
- self.api['del_items'](jid, node, None, kwargs)
+ return self.api['del_items'](jid, node, None, kwargs)
- def add_item(self, jid='', name='', node=None, subnode='', ijid=None):
+ def add_item(self, jid='', name='', node=None, subnode='', ijid=None) -> Future:
"""
Add a new item element to the given JID/node combination.
Each item is required to have a JID, but may also specify
a node value to reference non-addressable entities.
+ .. versionchanged:: 1.8.0
+ This function now returns a Future.
+
:param jid: The JID for the item.
:param name: Optional name for the item.
:param node: The node to modify.
@@ -488,9 +514,9 @@ class XEP_0030(BasePlugin):
kwargs = {'ijid': jid,
'name': name,
'inode': subnode}
- self.api['add_item'](ijid, node, None, kwargs)
+ return self.api['add_item'](ijid, node, None, kwargs)
- def del_item(self, jid=None, node=None, **kwargs):
+ def del_item(self, jid=None, node=None, **kwargs) -> Future:
"""
Remove a single item from the given JID/node combination.
@@ -499,10 +525,10 @@ class XEP_0030(BasePlugin):
:param ijid: The item's JID.
:param inode: The item's node.
"""
- self.api['del_item'](jid, node, None, kwargs)
+ return self.api['del_item'](jid, node, None, kwargs)
def add_identity(self, category='', itype='', name='',
- node=None, jid=None, lang=None):
+ node=None, jid=None, lang=None) -> Future:
"""
Add a new identity to the given JID/node combination.
@@ -514,6 +540,9 @@ class XEP_0030(BasePlugin):
category/type/xml:lang pairs are allowed so long as the
names are different. A category and type is always required.
+ .. versionchanged:: 1.8.0
+ This function now returns a Future.
+
:param category: The identity's category.
:param itype: The identity's type.
:param name: Optional name for the identity.
@@ -525,24 +554,31 @@ class XEP_0030(BasePlugin):
'itype': itype,
'name': name,
'lang': lang}
- self.api['add_identity'](jid, node, None, kwargs)
+ return self.api['add_identity'](jid, node, None, kwargs)
def add_feature(self, feature: str, node: Optional[str] = None,
- jid: Optional[JID] = None):
+ jid: Optional[JID] = None) -> Future:
"""
Add a feature to a JID/node combination.
+ .. versionchanged:: 1.8.0
+ This function now returns a Future.
+
:param feature: The namespace of the supported feature.
:param node: The node to modify.
:param jid: The JID to modify.
"""
kwargs = {'feature': feature}
- self.api['add_feature'](jid, node, None, kwargs)
+ return self.api['add_feature'](jid, node, None, kwargs)
- def del_identity(self, jid: Optional[JID] = None, node: Optional[str] = None, **kwargs):
+ def del_identity(self, jid: Optional[JID] = None,
+ node: Optional[str] = None, **kwargs) -> Future:
"""
Remove an identity from the given JID/node combination.
+ .. versionchanged:: 1.8.0
+ This function now returns a Future.
+
:param jid: The JID to modify.
:param node: The node to modify.
:param category: The identity's category.
@@ -550,67 +586,82 @@ class XEP_0030(BasePlugin):
:param name: Optional, human readable name for the identity.
:param lang: Optional, the identity's xml:lang value.
"""
- self.api['del_identity'](jid, node, None, kwargs)
+ return self.api['del_identity'](jid, node, None, kwargs)
- def del_feature(self, jid=None, node=None, **kwargs):
+ def del_feature(self, jid=None, node=None, **kwargs) -> Future:
"""
Remove a feature from a given JID/node combination.
+ .. versionchanged:: 1.8.0
+ This function now returns a Future.
+
:param jid: The JID to modify.
:param node: The node to modify.
:param feature: The feature's namespace.
"""
- self.api['del_feature'](jid, node, None, kwargs)
+ return self.api['del_feature'](jid, node, None, kwargs)
- def set_identities(self, jid=None, node=None, **kwargs):
+ def set_identities(self, jid=None, node=None, **kwargs) -> Future:
"""
Add or replace all identities for the given JID/node combination.
The identities must be in a set where each identity is a tuple
of the form: (category, type, lang, name)
+ .. versionchanged:: 1.8.0
+ This function now returns a Future.
+
:param jid: The JID to modify.
:param node: The node to modify.
:param identities: A set of identities in tuple form.
:param lang: Optional, xml:lang value.
"""
- self.api['set_identities'](jid, node, None, kwargs)
+ return self.api['set_identities'](jid, node, None, kwargs)
- def del_identities(self, jid=None, node=None, **kwargs):
+ def del_identities(self, jid=None, node=None, **kwargs) -> Future:
"""
Remove all identities for a JID/node combination.
If a language is specified, only identities using that
language will be removed.
+ .. versionchanged:: 1.8.0
+ This function now returns a Future.
+
:param jid: The JID to modify.
:param node: The node to modify.
:param lang: Optional. If given, only remove identities
using this xml:lang value.
"""
- self.api['del_identities'](jid, node, None, kwargs)
+ return self.api['del_identities'](jid, node, None, kwargs)
- def set_features(self, jid=None, node=None, **kwargs):
+ def set_features(self, jid=None, node=None, **kwargs) -> Future:
"""
Add or replace the set of supported features
for a JID/node combination.
+ .. versionchanged:: 1.8.0
+ This function now returns a Future.
+
:param jid: The JID to modify.
:param node: The node to modify.
:param features: The new set of supported features.
"""
- self.api['set_features'](jid, node, None, kwargs)
+ return self.api['set_features'](jid, node, None, kwargs)
- def del_features(self, jid=None, node=None, **kwargs):
+ def del_features(self, jid=None, node=None, **kwargs) -> Future:
"""
Remove all features from a JID/node combination.
+ .. versionchanged:: 1.8.0
+ This function now returns a Future.
+
:param jid: The JID to modify.
:param node: The node to modify.
"""
- self.api['del_features'](jid, node, None, kwargs)
+ return self.api['del_features'](jid, node, None, kwargs)
- def _run_node_handler(self, htype, jid, node=None, ifrom=None, data=None):
+ async def _run_node_handler(self, htype, jid, node=None, ifrom=None, data=None):
"""
Execute the most specific node handler for the given
JID/node combination.
@@ -623,9 +674,9 @@ class XEP_0030(BasePlugin):
if not data:
data = {}
- return self.api[htype](jid, node, ifrom, data)
+ return await self.api[htype](jid, node, ifrom, data)
- def _handle_disco_info(self, iq):
+ async def _handle_disco_info(self, iq):
"""
Process an incoming disco#info stanza. If it is a get
request, find and return the appropriate identities
@@ -637,10 +688,10 @@ class XEP_0030(BasePlugin):
if iq['type'] == 'get':
log.debug("Received disco info query from " + \
"<%s> to <%s>.", iq['from'], iq['to'])
- info = self.api['get_info'](iq['to'],
- iq['disco_info']['node'],
- iq['from'],
- iq)
+ info = await self.api['get_info'](iq['to'],
+ iq['disco_info']['node'],
+ iq['from'],
+ iq)
if isinstance(info, Iq):
info['id'] = iq['id']
info.send()
@@ -662,13 +713,13 @@ class XEP_0030(BasePlugin):
ito = iq['to'].full
else:
ito = None
- self.api['cache_info'](iq['from'],
- iq['disco_info']['node'],
- ito,
- iq)
+ await self.api['cache_info'](iq['from'],
+ iq['disco_info']['node'],
+ ito,
+ iq)
self.xmpp.event('disco_info', iq)
- def _handle_disco_items(self, iq):
+ async def _handle_disco_items(self, iq):
"""
Process an incoming disco#items stanza. If it is a get
request, find and return the appropriate items. If it
@@ -679,10 +730,10 @@ class XEP_0030(BasePlugin):
if iq['type'] == 'get':
log.debug("Received disco items query from " + \
"<%s> to <%s>.", iq['from'], iq['to'])
- items = self.api['get_items'](iq['to'],
- iq['disco_items']['node'],
- iq['from'],
- iq)
+ items = await self.api['get_items'](iq['to'],
+ iq['disco_items']['node'],
+ iq['from'],
+ iq)
if isinstance(items, Iq):
items.send()
else:
diff --git a/slixmpp/plugins/xep_0030/static.py b/slixmpp/plugins/xep_0030/static.py
index 1b5ff2d8..1ae34148 100644
--- a/slixmpp/plugins/xep_0030/static.py
+++ b/slixmpp/plugins/xep_0030/static.py
@@ -109,7 +109,7 @@ class StaticDisco(object):
# the requester's JID, except for cached results. To do that,
# register a custom node handler.
- def supports(self, jid, node, ifrom, data):
+ async def supports(self, jid, node, ifrom, data):
"""
Check if a JID supports a given feature.
@@ -137,8 +137,8 @@ class StaticDisco(object):
return False
try:
- info = self.disco.get_info(jid=jid, node=node,
- ifrom=ifrom, **data)
+ info = await self.disco.get_info(jid=jid, node=node,
+ ifrom=ifrom, **data)
info = self.disco._wrap(ifrom, jid, info, True)
features = info['disco_info']['features']
return feature in features
@@ -147,7 +147,7 @@ class StaticDisco(object):
except IqTimeout:
return None
- def has_identity(self, jid, node, ifrom, data):
+ async def has_identity(self, jid, node, ifrom, data):
"""
Check if a JID has a given identity.
@@ -176,8 +176,8 @@ class StaticDisco(object):
'cached': data.get('cached', True)}
try:
- info = self.disco.get_info(jid=jid, node=node,
- ifrom=ifrom, **data)
+ info = await self.disco.get_info(jid=jid, node=node,
+ ifrom=ifrom, **data)
info = self.disco._wrap(ifrom, jid, info, True)
trunc = lambda i: (i[0], i[1], i[2])
return identity in map(trunc, info['disco_info']['identities'])
diff --git a/tests/test_stream_xep_0030.py b/tests/test_stream_xep_0030.py
index d1ad9087..8cba8280 100644
--- a/tests/test_stream_xep_0030.py
+++ b/tests/test_stream_xep_0030.py
@@ -1,5 +1,5 @@
+import asyncio
import time
-import threading
import unittest
from slixmpp.test import SlixTest
@@ -288,7 +288,9 @@ class TestStreamDisco(SlixTest):
self.xmpp.add_event_handler('disco_info', handle_disco_info)
- self.xmpp['xep_0030'].get_info('user@localhost', 'foo')
+
+ self.xmpp.wrap(self.xmpp['xep_0030'].get_info('user@localhost', 'foo'))
+ self.wait_()
self.send("""
<iq type="get" to="user@localhost" id="1">
@@ -483,7 +485,8 @@ class TestStreamDisco(SlixTest):
self.xmpp.add_event_handler('disco_items', handle_disco_items)
- self.xmpp['xep_0030'].get_items('user@localhost', 'foo')
+ self.xmpp.wrap(self.xmpp['xep_0030'].get_items('user@localhost', 'foo'))
+ self.wait_()
self.send("""
<iq type="get" to="user@localhost" id="1">