Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to start Tornado periodic callback at a specific time?

Tags:

python

tornado

Currently in my Tornado application, I am calling a callback periodically using PeriodicCallback every hour. Like this:

import tornado.ioloop
from tornado.ioloop import PeriodicCallback

if __name__ == "__main__":
    tornado.options.parse_command_line()
    app = tornado.web.Application(
      handlers=urls,
      template_path=os.path.join(os.path.dirname(__file__), "templates"),
      static_path=os.path.join(os.path.dirname(__file__), "static"),
      debug=True
      )
    http_server = tornado.httpserver.HTTPServer(app)
    http_server.listen(options.port)

    # Here i have option to specify interval, but how to specify when to start?
    daily_alerts = PeriodicCallback(lambda: send_email.daily_alert(), 3600000)
    daily_alerts.start()
    tornado.ioloop.IOLoop.instance().start()

Here, I have the option to set the interval time (3600000), but how do I specify when this periodical callback should start?

like image 887
Kathirmani Sukumar Avatar asked Oct 31 '25 21:10

Kathirmani Sukumar


2 Answers

If you want to control exactly when the callback is scheduled, it's better to use IOLoop.add_timeout directly instead of PeriodicCallback.

def schedule_next_email():
    tomorrow = datetime.date.today() + datetime.timedelta(days=1)
    midnight = datetime.combine(tomorrow, datetime.time.min)
    def wrapper():
        send_email.daily_alert()
        schedule_next_email()
    IOLoop.current().add_timeout(midnight, wrapper)

schedule_next_email()  # call at startup to schedule first call

Recomputing the interval every time helps you send at the same time every day even when daylight savings time changes and days can have either 23 or 25 hours.

like image 58
Ben Darnell Avatar answered Nov 02 '25 11:11

Ben Darnell


I found @ben-darnell's solution useful, but it had some issues for me so I wanted to post a working variation where the wrapper and function are split:

from tornado.ioloop import IOLoop
import datetime
import pytz    

def wrapper():
    # Add one day to the current time
    tomorrow = datetime.datetime.now(pytz.timezone('US/Pacific')) + datetime.timedelta(days=1)
    # Strip the date from "tomorrow" with the minimum timestamp time.min to get midnight
    midnight = datetime.datetime.combine(tomorrow, datetime.time.min)
    IOLoop.current().add_timeout(datetime.datetime.timestamp(midnight), print_test)

def print_test():
    print('test')
    wrapper()
like image 28
Nic Scozzaro Avatar answered Nov 02 '25 10:11

Nic Scozzaro