diff options
Diffstat (limited to 'slixmpp/api.py')
-rw-r--r-- | slixmpp/api.py | 112 |
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) |