Kathirmani Sukumar
Kathirmani Sukumar

Reputation: 10980

How to start Tornado periodic callback at a specific time?

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?

Upvotes: 4

Views: 3180

Answers (2)

Nic Scozzaro
Nic Scozzaro

Reputation: 7373

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()

Upvotes: 1

Ben Darnell
Ben Darnell

Reputation: 22154

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.

Upvotes: 7

Related Questions