def __init__(self, name, matcher, pointer, once=False, instream=False, stream=None): BaseHandler.__init__(self, name, matcher, stream) if not asyncio.iscoroutinefunction(pointer): raise ValueError("Given function is not a coroutine") async def pointer_wrapper(stanza, *args, **kwargs): try: await pointer(stanza, *args, **kwargs) except Exception as e: stanza.exception(e) self._pointer = pointer_wrapper self._once = once self._instream = instream
def event(self, name, data={}): """Manually trigger a custom event. :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. """ log.debug("Event triggered: %s", name) handlers = self.__event_handlers.get(name, []) for handler in handlers: handler_callback, disposable = handler old_exception = getattr(data, 'exception', None) # If the callback is a coroutine, schedule it instead of # running it directly if asyncio.iscoroutinefunction(handler_callback): async def handler_callback_routine(cb): try: await cb(data) except Exception as e: if old_exception: old_exception(e) else: self.exception(e) asyncio.ensure_future( handler_callback_routine(handler_callback), loop=self.loop, ) else: try: handler_callback(data) except Exception as e: if old_exception: old_exception(e) else: self.exception(e) 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
def send(self, callback=None, timeout=None, timeout_callback=None): """Send an <iq> stanza over the XML stream. A callback handler can be provided that will be executed when the Iq stanza's result reply is received. Returns a future which result will be set to the result Iq if it is of type 'get' or 'set' (when it is received), or a future with the result set to None if it has another type. Overrides StanzaBase.send :param function callback: Optional reference to a stream handler function. Will be executed when a reply stanza is received. :param int timeout: The length of time (in seconds) to wait for a response before the timeout_callback is called, instead of the regular callback :param function timeout_callback: Optional reference to a stream handler function. Will be executed when the timeout expires before a response has been received for the originally-sent IQ stanza. :rtype: asyncio.Future """ if self.stream.session_bind_event.is_set(): matcher = MatchIDSender({"id": self["id"], "self": self.stream.boundjid, "peer": self["to"]}) else: matcher = MatcherId(self["id"]) future = asyncio.Future() def callback_success(result): if result["type"] == "error": future.set_exception(IqError(result)) else: future.set_result(result) if timeout is not None: self.stream.cancel_schedule("IqTimeout_%s" % self["id"]) if callback is not None: callback(result) def callback_timeout(): future.set_exception(IqTimeout(self)) self.stream.remove_handler("IqCallback_%s" % self["id"]) if timeout_callback is not None: timeout_callback(self) if self["type"] in ("get", "set"): handler_name = "IqCallback_%s" % self["id"] if asyncio.iscoroutinefunction(callback): constr = CoroutineCallback else: constr = Callback if timeout is not None: self.stream.schedule("IqTimeout_%s" % self["id"], timeout, callback_timeout, repeat=False) handler = constr(handler_name, matcher, callback_success, once=True) self.stream.register_handler(handler) else: future.set_result(None) StanzaBase.send(self) return future
def send(self, callback=None, timeout=None, timeout_callback=None): """Send an <iq> stanza over the XML stream. A callback handler can be provided that will be executed when the Iq stanza's result reply is received. Returns a future which result will be set to the result Iq if it is of type 'get' or 'set' (when it is received), or a future with the result set to None if it has another type. Overrides StanzaBase.send :param function callback: Optional reference to a stream handler function. Will be executed when a reply stanza is received. :param int timeout: The length of time (in seconds) to wait for a response before the timeout_callback is called, instead of the regular callback :param function timeout_callback: Optional reference to a stream handler function. Will be executed when the timeout expires before a response has been received for the originally-sent IQ stanza. :rtype: asyncio.Future """ if self.stream.session_bind_event.is_set(): matcher = MatchIDSender({ 'id': self['id'], 'self': self.stream.boundjid, 'peer': self['to'] }) else: matcher = MatcherId(self['id']) future = asyncio.Future() def callback_success(result): if result['type'] == 'error': future.set_exception(IqError(result)) else: future.set_result(result) if timeout is not None: self.stream.cancel_schedule('IqTimeout_%s' % self['id']) if callback is not None: callback(result) def callback_timeout(): future.set_exception(IqTimeout(self)) self.stream.remove_handler('IqCallback_%s' % self['id']) if timeout_callback is not None: timeout_callback(self) if self['type'] in ('get', 'set'): handler_name = 'IqCallback_%s' % self['id'] if asyncio.iscoroutinefunction(callback): constr = CoroutineCallback else: constr = Callback if timeout is not None: self.stream.schedule('IqTimeout_%s' % self['id'], timeout, callback_timeout, repeat=False) handler = constr(handler_name, matcher, callback_success, once=True) self.stream.register_handler(handler) else: future.set_result(None) StanzaBase.send(self) return future
def send(self, callback=None, timeout=None, timeout_callback=None): """Send an <iq> stanza over the XML stream. A callback handler can be provided that will be executed when the Iq stanza's result reply is received. Returns a future which result will be set to the result Iq if it is of type 'get' or 'set' (when it is received), or a future with the result set to None if it has another type. Overrides StanzaBase.send :param function callback: Optional reference to a stream handler function. Will be executed when a reply stanza is received. :param int timeout: The length of time (in seconds) to wait for a response before the timeout_callback is called, instead of the regular callback :param function timeout_callback: Optional reference to a stream handler function. Will be executed when the timeout expires before a response has been received for the originally-sent IQ stanza. :rtype: asyncio.Future """ if self.stream.session_bind_event.is_set(): matcher = MatchIDSender({ 'id': self['id'], 'self': self.stream.boundjid, 'peer': self['to'] }) else: matcher = MatcherId(self['id']) future = asyncio.Future() # Prevents handlers from existing forever. if timeout is None: timeout = 120 def callback_success(result): type_ = result['type'] if type_ == 'result': future.set_result(result) elif type_ == 'error': future.set_exception(IqError(result)) else: # Most likely an iq addressed to ourself, rearm the callback. handler = constr(handler_name, matcher, callback_success, once=True) self.stream.register_handler(handler) return if timeout is not None: self.stream.cancel_schedule('IqTimeout_%s' % self['id']) if callback is not None: callback(result) def callback_timeout(): future.set_exception(IqTimeout(self)) self.stream.remove_handler('IqCallback_%s' % self['id']) if timeout_callback is not None: timeout_callback(self) if self['type'] in ('get', 'set'): handler_name = 'IqCallback_%s' % self['id'] if asyncio.iscoroutinefunction(callback): constr = CoroutineCallback else: constr = Callback if timeout is not None: self.stream.schedule('IqTimeout_%s' % self['id'], timeout, callback_timeout, repeat=False) handler = constr(handler_name, matcher, callback_success, once=True) self.stream.register_handler(handler) else: future.set_result(None) StanzaBase.send(self) return future