XEP-0030: API changes
- ``supports``, ``has_identity``, ``get_info``, ``get_items`` are now coroutines - ``set_info````set_items``, ``del_items``, ``add_item``, ``add_identity``, ``del_identity``, ``set_identities``, ``del_identities``, ``add_feature``, ``del_feature``, ``set_feature``, ``set_features``, ``del_features`` now return a Future also fix has_identity and supports which have been broken in forever
This commit is contained in:
parent
7772e26a8c
commit
13de36baa1
3 changed files with 125 additions and 71 deletions
|
@ -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,
|
||||
info = await self.api['get_cached_info'](
|
||||
jid, node,
|
||||
kwargs.get('ifrom', None),
|
||||
kwargs)
|
||||
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,7 +688,7 @@ 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'],
|
||||
info = await self.api['get_info'](iq['to'],
|
||||
iq['disco_info']['node'],
|
||||
iq['from'],
|
||||
iq)
|
||||
|
@ -662,13 +713,13 @@ class XEP_0030(BasePlugin):
|
|||
ito = iq['to'].full
|
||||
else:
|
||||
ito = None
|
||||
self.api['cache_info'](iq['from'],
|
||||
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,7 +730,7 @@ 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'],
|
||||
items = await self.api['get_items'](iq['to'],
|
||||
iq['disco_items']['node'],
|
||||
iq['from'],
|
||||
iq)
|
||||
|
|
|
@ -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,7 +137,7 @@ class StaticDisco(object):
|
|||
return False
|
||||
|
||||
try:
|
||||
info = self.disco.get_info(jid=jid, node=node,
|
||||
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']
|
||||
|
@ -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,7 +176,7 @@ class StaticDisco(object):
|
|||
'cached': data.get('cached', True)}
|
||||
|
||||
try:
|
||||
info = self.disco.get_info(jid=jid, node=node,
|
||||
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])
|
||||
|
|
|
@ -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">
|
||||
|
|
Loading…
Reference in a new issue