Timers
Timer registry
Timers are held and triggered by the timer registry for each service (on the attribute service.timers
.
It is recommended that you don't interact with the registry directly - instead, you should use the service.timer method to decorate your timer handler.
That said it is possible to add a new timer directly by calling the add
method on the registry, ie service.timers.add(timer)
. This can be useful if binding a custom timer class which defines its own handler - see Timer handlers for an example.
Once a timer is registered, the service is then responsible for triggering any due (or overdue) timers at every poll, at a frequency of roughly 1/10th of a second by default (controlled by the socket_activity_timeout
setting, delayed by any event processing).
Timers are stored in a linked list in the order that they are due. The registry holds a reference to the first timer in the list (as the next
attribute), then each timer holds a reference to the next one (again, as the next
attribute).
Timer handlers
Timer handlers are the functions called when the timer is triggered. In all the built-in timers, handlers are functions assigned to an instantiated timer's fn
attribute. They are passed a single argument: timer
, the timer instance they are assigned to. This allows the timer to be modified by the handler - see Timer classes for more details.
An alternative approach to defining a timer handler is to define a custom timer class, and override the trigger
function. This would be a useful approach if you wanted to have multiple timers using the same handler - for example, you could create a subclass of DateTimer which takes a user and date in the constructor, then calls the same handler to wish the user happy birthday on the specified date. With this approach you would add the timer instances to the registry directly - see Timer registry for details.
Timer classes
Timers have an update
method which set their due
attribute - the timestamp that they are due to be triggered. When triggered, the trigger
method is called.
All timer classes also take a context
argument, so data can be passed in to the trigger function, or values persisted across calls.
The trigger function can stop the timer from being triggered again in the future by calling timer.stop()
(or changing timer.active
to False
).
mara.timers.Timer
Base class for timer classes. It is more likely that the other timer classes will be more useful in your code.
-
Arguments:
-
-
due
-
The timestamp that the event is due
-
context
-
Optional context
-
mara.timers.PeriodTimer
This repeats after the specified period (in seconds).
It is the default class for the service decorator timer(cls=PeriodTimer, **kwargs)
.
-
Arguments:
-
-
period
-
The number of seconds until it is due
-
context
-
Optional context
-
Example of a periodic announcement:
from mara import timers
@service.timer(timers.PeriodTimer, period=60)
def every_minute(timer):
service.write_all('You have wasted another minute of your life.')
Example of a delay:
# Echo client with a 3 second delay
def do_echo(timer):
event = timer.context
event.client.write(event.data)
# Only fire once
timer.stop()
from mara import timers
@service.listen(events.Receive)
def delayed_echo(event):
timer = timers.PeriodTimer(period=3, context=event)
timer.fn = do_echo
service.timers.add(timer)
mara.timers.RandomTimer
This repeats after a random amount of time, between a specified minimum and maximum period (in seconds)
-
Arguments:
-
-
min_period
-
The number of seconds until it is due
-
context
-
Optional context
-
Example of a periodic announcement, every 1 to 3 minutes:
from mara import timers
@service.timer(timers.RandomTimer, min_period=1*60, max_period=3*60)
def every_so_often(timer):
service.write_all('You are wasting your life.')
mara.timers.date.DateTimer
This timer repeats at a specified date or time.
It is defined in a separate module to the others because it depends on the module dateutil
- if not installed, it will raise an ImportError. To install dateutil
, use pip install python-dateutil
.
It takes keyword arguments describing the date and time; if a value is None, when the timer updates it will use the non-None values to give the soonest date and time; eg to fire at 2pm on the 1st of each month:
timer = DateTimer(day=1, hour=14)
The defaults provide the value 0
for minute
and second
, and None
for the others, meaning that by default it will fire at the start of every hour.
It is not timezone aware.
-
Arguments:
-
-
year
-
The year the timer will trigger (None for every year)
-
month
-
The month the timer will trigger (None for every month)
-
day
-
The day the timer will trigger (None for every day)
-
hour
-
The hour the timer will trigger (None for every hour)
-
minute
-
The minute the timer will trigger (None for every minute)
-
second
-
The second the timer will trigger (None for every second)
-
context
-
Optional context
-
Because the calculation of the next due date is more intensive than that of other timers, you should probably think carefully before setting second=None
, and consider using a more appropriate timer instead - or a combination of a DateTimer
which adds a new PeriodTimer
.
Example of an announcement at a specific date and time:
from mara.timers.time import DateTimer
@service.timer(DateTimer, month=3, day=1, hour=12)
def happy_birthday(timer):
service.write_all("Happy Birthday to Radiac! He is wasting his life.")