summaryrefslogtreecommitdiff
path: root/slixmpp/api.py
diff options
context:
space:
mode:
Diffstat (limited to 'slixmpp/api.py')
-rw-r--r--slixmpp/api.py112
1 files changed, 74 insertions, 38 deletions
diff --git a/slixmpp/api.py b/slixmpp/api.py
index f09e0365..39fed490 100644
--- a/slixmpp/api.py
+++ b/slixmpp/api.py
@@ -1,7 +1,19 @@
+from typing import Any, Optional, Callable
+from asyncio import iscoroutinefunction, Future
from slixmpp.xmlstream import JID
+APIHandler = Callable[
+ [Optional[JID], Optional[str], Optional[JID], Any],
+ Any
+]
class APIWrapper(object):
+ """Slixmpp API wrapper.
+
+ This class provide a shortened binding to access ``self.api`` from
+ plugins without having to specify the plugin name or the global
+ :class:`~.APIRegistry`.
+ """
def __init__(self, api, name):
self.api = api
@@ -37,6 +49,11 @@ class APIWrapper(object):
class APIRegistry(object):
+ """API Registry.
+
+ This class is the global Slixmpp API registry, on which any handler will
+ be registed.
+ """
def __init__(self, xmpp):
self._handlers = {}
@@ -44,11 +61,11 @@ class APIRegistry(object):
self.xmpp = xmpp
self.settings = {}
- def _setup(self, ctype, op):
+ def _setup(self, ctype: str, op: str):
"""Initialize the API callback dictionaries.
- :param string ctype: The name of the API to initialize.
- :param string op: The API operation to initialize.
+ :param ctype: The name of the API to initialize.
+ :param op: The API operation to initialize.
"""
if ctype not in self.settings:
self.settings[ctype] = {}
@@ -61,27 +78,32 @@ class APIRegistry(object):
'jid': {},
'node': {}}
- def wrap(self, ctype):
+ def wrap(self, ctype: str) -> APIWrapper:
"""Return a wrapper object that targets a specific API."""
return APIWrapper(self, ctype)
- def purge(self, ctype):
+ def purge(self, ctype: str):
"""Remove all information for a given API."""
del self.settings[ctype]
del self._handler_defaults[ctype]
del self._handlers[ctype]
- def run(self, ctype, op, jid=None, node=None, ifrom=None, args=None):
+ def run(self, ctype: str, op: str, jid: Optional[JID] = None,
+ node: Optional[str] = None, ifrom: Optional[JID] = None,
+ args: Any = None) -> Future:
"""Execute an API callback, based on specificity.
The API callback that is executed is chosen based on the combination
of the provided JID and node:
- JID | node | Handler
- ==============================
- Given | Given | Node handler
- Given | None | JID handler
- None | None | Global handler
+ ====== ======= ===================
+ JID node Handler
+ ====== ======= ===================
+ Given Given Node + JID handler
+ Given None JID handler
+ None Given Node handler
+ None None Global handler
+ ====== ======= ===================
A node handler is responsible for servicing a single node at a single
JID, while a JID handler may respond for any node at a given JID, and
@@ -90,12 +112,16 @@ class APIRegistry(object):
Handlers should check that the JID ``ifrom`` is authorized to perform
the desired action.
- :param string ctype: The name of the API to use.
- :param string op: The API operation to perform.
- :param JID jid: Optionally provide specific JID.
- :param string node: Optionally provide specific node.
- :param JID ifrom: Optionally provide the requesting JID.
- :param tuple args: Optional positional arguments to the handler.
+ .. versionchanged:: 1.8.0
+ ``run()`` always returns a future, if the handler is a coroutine
+ the future should be awaited on.
+
+ :param ctype: The name of the API to use.
+ :param op: The API operation to perform.
+ :param jid: Optionally provide specific JID.
+ :param node: Optionally provide specific node.
+ :param ifrom: Optionally provide the requesting JID.
+ :param args: Optional arguments to the handler.
"""
self._setup(ctype, op)
@@ -130,24 +156,32 @@ class APIRegistry(object):
if handler:
try:
- return handler(jid, node, ifrom, args)
+ if iscoroutinefunction(handler):
+ return self.xmpp.wrap(handler(jid, node, ifrom, args))
+ else:
+ future: Future = Future()
+ result = handler(jid, node, ifrom, args)
+ future.set_result(result)
+ return future
except TypeError:
# To preserve backward compatibility, drop the ifrom
# parameter for existing handlers that don't understand it.
return handler(jid, node, args)
- def register(self, handler, ctype, op, jid=None, node=None, default=False):
+ def register(self, handler: APIHandler, ctype: str, op: str,
+ jid: Optional[JID] = None, node: Optional[str] = None,
+ default: bool = False):
"""Register an API callback, with JID+node specificity.
The API callback can later be executed based on the
specificity of the provided JID+node combination.
- See :meth:`~ApiRegistry.run` for more details.
+ See :meth:`~.APIRegistry.run` for more details.
- :param string ctype: The name of the API to use.
- :param string op: The API operation to perform.
- :param JID jid: Optionally provide specific JID.
- :param string node: Optionally provide specific node.
+ :param ctype: The name of the API to use.
+ :param op: The API operation to perform.
+ :param jid: Optionally provide specific JID.
+ :param node: Optionally provide specific node.
"""
self._setup(ctype, op)
if jid is None and node is None:
@@ -162,17 +196,18 @@ class APIRegistry(object):
if default:
self.register_default(handler, ctype, op)
- def register_default(self, handler, ctype, op):
+ def register_default(self, handler, ctype: str, op: str):
"""Register a default, global handler for an operation.
- :param func handler: The default, global handler for the operation.
- :param string ctype: The name of the API to modify.
- :param string op: The API operation to use.
+ :param handler: The default, global handler for the operation.
+ :param ctype: The name of the API to modify.
+ :param op: The API operation to use.
"""
self._setup(ctype, op)
self._handler_defaults[ctype][op] = handler
- def unregister(self, ctype, op, jid=None, node=None):
+ def unregister(self, ctype: str, op: str, jid: Optional[JID] = None,
+ node: Optional[str] = None):
"""Remove an API callback.
The API callback chosen for removal is based on the
@@ -180,21 +215,22 @@ class APIRegistry(object):
See :meth:`~ApiRegistry.run` for more details.
- :param string ctype: The name of the API to use.
- :param string op: The API operation to perform.
- :param JID jid: Optionally provide specific JID.
- :param string node: Optionally provide specific node.
+ :param ctype: The name of the API to use.
+ :param op: The API operation to perform.
+ :param jid: Optionally provide specific JID.
+ :param node: Optionally provide specific node.
"""
self._setup(ctype, op)
self.register(None, ctype, op, jid, node)
- def restore_default(self, ctype, op, jid=None, node=None):
+ def restore_default(self, ctype: str, op: str, jid: Optional[JID] = None,
+ node: Optional[str] = None):
"""Reset an API callback to use a default handler.
- :param string ctype: The name of the API to use.
- :param string op: The API operation to perform.
- :param JID jid: Optionally provide specific JID.
- :param string node: Optionally provide specific node.
+ :param ctype: The name of the API to use.
+ :param op: The API operation to perform.
+ :param jid: Optionally provide specific JID.
+ :param node: Optionally provide specific node.
"""
self.unregister(ctype, op, jid, node)
self.register(self._handler_defaults[ctype][op], ctype, op, jid, node)