From 87cf38995a283e4a476fd124fcc55e0f80edf1ad Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Fri, 1 Aug 2014 01:22:59 +0200 Subject: Make the TimedEvents work with asyncio MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Improvements: events now occur precisely at the specified date. You don’t need to stop touching your keyboard to execute them. --- src/core/core.py | 22 +++----------- src/tabs/basetabs.py | 22 +++++--------- src/timed_events.py | 82 ++++++++++++++-------------------------------------- 3 files changed, 32 insertions(+), 94 deletions(-) diff --git a/src/core/core.py b/src/core/core.py index 446cc9f0..b2951aea 100644 --- a/src/core/core.py +++ b/src/core/core.py @@ -262,8 +262,6 @@ class Core(object): self.initial_joins = [] - self.timed_events = set() - self.connected_events = {} self.pending_invites = {} @@ -749,22 +747,13 @@ class Core(object): def remove_timed_event(self, event): """Remove an existing timed event""" - if event and event in self.timed_events: - self.timed_events.remove(event) + event.handler.cancel() def add_timed_event(self, event): """Add a new timed event""" - self.timed_events.add(event) - - def check_timed_events(self): - """Check for the execution of timed events""" - now = datetime.now() - for event in self.timed_events: - if event.has_timed_out(now): - res = event() - if not res: - self.timed_events.remove(event) - break + event.handler = asyncio.get_event_loop().call_later(event.delay, + event.callback, + *event.args) ####################### XMPP-related actions ################################## @@ -1628,9 +1617,6 @@ class Core(object): a non-None value), but we check for timed events instead. """ res = self.keyboard.get_user_input(self.stdscr) - while res is None: - self.check_timed_events() - res = self.keyboard.get_user_input(self.stdscr) return res def escape_next_key(self): diff --git a/src/tabs/basetabs.py b/src/tabs/basetabs.py index c2efc9bc..95e47e4e 100644 --- a/src/tabs/basetabs.py +++ b/src/tabs/basetabs.py @@ -624,17 +624,12 @@ class ChatTab(Tab): """ if not config.get_by_tabname('send_chat_states', True, self.general_jid, True): return - if self.timed_event_paused: - # check the weakref - event = self.timed_event_paused() - if event: - # the event already exists: we just update - # its date - event.change_date(datetime.now() + timedelta(seconds=4)) - return + # First, cancel the delay if it already exists, before rescheduling + # it at a new date + self.cancel_paused_delay() new_event = timed_events.DelayedEvent(4, self.send_chat_state, 'paused') self.core.add_timed_event(new_event) - self.timed_event_paused = weakref.ref(new_event) + self.timed_event_paused = new_event def cancel_paused_delay(self): """ @@ -642,12 +637,9 @@ class ChatTab(Tab): Called for example when the input is emptied, or when the message is sent """ - if self.timed_event_paused: - event = self.timed_event_paused() - if event: - self.core.remove_timed_event(event) - del event - self.timed_event_paused = None + if self.timed_event_paused is not None: + self.core.remove_timed_event(self.timed_event_paused) + self.timed_event_paused = None def command_correct(self, line): """ diff --git a/src/timed_events.py b/src/timed_events.py index a922ee03..6160645b 100644 --- a/src/timed_events.py +++ b/src/timed_events.py @@ -13,87 +13,47 @@ Once created, they must be added to the list of checked events with :py:func:`.PluginAPI.add_timed_event` (within a plugin). """ +import asyncio import logging log = logging.getLogger(__name__) import datetime -class TimedEvent(object): +class DelayedEvent(object): """ - An event with a callback that is called when the specified time is passed. - - Note that these events can NOT be used for very small delay or a very - precise date, since the check for events is done once per second, as - a maximum. - - The callback and its arguments should be passed as the lasts arguments. + A TimedEvent, but with the date calculated from now + a delay in seconds. + Use it if you want an event to happen in, e.g. 6 seconds. """ - def __init__(self, date, callback, *args): + def __init__(self, delay, callback, *args): """ - Create a new timed event. + Create a new DelayedEvent. - :param datetime.datetime date: Time at which the callback must be run. + :param int delay: The number of seconds. :param function callback: The handler that will be executed. :param \*args: Optional arguments passed to the handler. """ - self._callback = callback + self.callback = callback self.args = args self.repetive = False - self.next_call_date = date - - def __call__(self): - """ - the call should return False if this event should be remove from - the events list. - If it’s true, the date should be updated beforehand to a later date, - or else it will be called every second - """ - self._callback(*self.args) - return self.repetive + self.delay = delay + # An asyncio handler, as returned by call_later() or call_at() + self.handler = None - def has_timed_out(self, current_date): - """ - Check if the event has timed out. - - :param datetime.datetime current_date: The current date. - :returns: True if the callback should be called - :rtype: bool - """ - if self.next_call_date < current_date: - return True - else: - return False - - def change_date(self, date): - """ - Simply change the date of the event. - - :param datetime.datetime date: Next date. - """ - self.next_call_date = date - - def add_delay(self, delay): - """ - Add a delay (in seconds) to the date. - - :param int delay: The delay to add. - """ - self.next_call_date += datetime.timedelta(seconds=delay) - -class DelayedEvent(TimedEvent): +class TimedEvent(DelayedEvent): """ - A TimedEvent, but with the date calculated from now + a delay in seconds. - Use it if you want an event to happen in, e.g. 6 seconds. + An event with a callback that is called when the specified time is passed. + + The callback and its arguments should be passed as the lasts arguments. """ - def __init__(self, delay, callback, *args): + def __init__(self, date, callback, *args): """ - Create a new DelayedEvent. + Create a new timed event. - :param int delay: The number of seconds. + :param datetime.datetime date: Time at which the callback must be run. :param function callback: The handler that will be executed. :param \*args: Optional arguments passed to the handler. """ - date = datetime.datetime.now() + datetime.timedelta(seconds=delay) - TimedEvent.__init__(self, date, callback, *args) - + delta = date - datetime.datetime.now() + delay = delta.total_seconds() + DelayedEvent.__init__(self, delay, callback, *args) -- cgit v1.2.3