diff --git a/slixmpp/plugins/xep_0077/register.py b/slixmpp/plugins/xep_0077/register.py index 953fee70..1850b2c9 100644 --- a/slixmpp/plugins/xep_0077/register.py +++ b/slixmpp/plugins/xep_0077/register.py @@ -203,14 +203,14 @@ class XEP_0077(BasePlugin): self.xmpp.del_filter('in', self._force_stream_feature) return stanza - def _handle_register_feature(self, features): + async def _handle_register_feature(self, features): if 'mechanisms' in self.xmpp.features: # We have already logged in with an account return False if self.create_account and self.xmpp.event_handled('register'): - form = self.get_registration() - self.xmpp.event('register', form) + form = await self.get_registration() + await self.xmpp.event_async('register', form) return True return False diff --git a/slixmpp/xmlstream/xmlstream.py b/slixmpp/xmlstream/xmlstream.py index 8a726e15..2f506018 100644 --- a/slixmpp/xmlstream/xmlstream.py +++ b/slixmpp/xmlstream/xmlstream.py @@ -850,8 +850,46 @@ class XMLStream(asyncio.BaseProtocol): """ return len(self.__event_handlers.get(name, [])) - def event(self, name, data={}): + async def event_async(self, name: str, data: Any = {}): + """Manually trigger a custom event, but await coroutines immediately. + + This event generator should only be called in situations when + in-order processing of events is important, such as features + handling. + + :param name: The name of the event to trigger. + :param data: Data that will be passed to each event handler. + Defaults to an empty dictionary, but is usually + a stanza object. + """ + handlers = self.__event_handlers.get(name, [])[:] + for handler in handlers: + handler_callback, disposable = handler + if disposable: + # If the handler is disposable, we will go ahead and + # remove it now instead of waiting for it to be + # processed in the queue. + try: + self.__event_handlers[name].remove(handler) + except ValueError: + pass + # If the callback is a coroutine, schedule it instead of + # running it directly + if iscoroutinefunction(handler_callback): + try: + await handler_callback(data) + except Exception as exc: + self.exception(exc) + else: + try: + handler_callback(data) + except Exception as e: + self.exception(e) + + def event(self, name: str, data: Any = {}): """Manually trigger a custom event. + Coroutine handlers are wrapped into a future and sent into the + event loop for their execution, and not awaited. :param name: The name of the event to trigger. :param data: Data that will be passed to each event handler.