Updated On : Jan-26,2021 Tags events, scheduler, sched
sched - How to Schedule Events in Python?

sched - How to Schedule Events in Python?

Python has a module named sched which lets us schedule events in the future by executing callable at some time in the future. It provides us with API which lets us execute callable at a specified time in the future as well as after specified delay from the current time. It has also provisions to use different time-related functions with different time units with the scheduler. As a part of this tutorial, we'll explain how we can use various methods of this module to schedule events, cancel events, etc.

The process of scheduling events through sched module requires us to follow a list of simple steps.

  • Create a scheduler instance using scheduler() method of sched module.
  • Create events using one of the enter() or enterabs() method of scheduler instance.
  • Call run() method on scheduler instance to start executing events.

Please make a note that events will start executing only after the call to run() method of scheduler instance. If there is a delay in calling run() method after the creation of events then events might get delayed as well if their time of execution has already passed when we started scheduler using run().

We'll now explain the usage of various methods of sched module with simple examples that will make them easy to understand and grasp.

Example 1

Our first example simply creates one event that executes after 3 seconds of its creation. We have created a simple function named addition(a,b) which takes two integer/floats as input and returns their addition as output. We have then created a scheduler using scheduler() method of sched module with its default parameter. Then we have created an event using enter() method of the scheduler instance which kicks off after 3 seconds of its creation. At last, we have called run() method on the scheduler to start it. The events get executed only after the scheduler has started.

sched.scheduler(timefunc=time.monotonic, delayfunc=time.sleep)

Below we have listed down important attributes of this method.

  • timefunc - It takes as input reference to any function which does not take any parameter and returns time (any units). The default is set to monotonic function of time module. The monotonic function is based on the seconds unit.
  • delayfunc - This parameter takes as input reference to a function which takes as input number and delays execution by that many time units.

The scheduler() method returns an instance of type scheduler. The scheduler class is thread-safe hence can be used with a multi-threading setup.

scheduler.enter(delay, priority, action, argument=(), kwargs={})

Below is a list of parameter explanations.

  • delay - It accepts integer/floats as value and executes event after that delay of that many time units.
  • priority - It accepts integer/floats specifying the priority of the event. The lower the value, the higher the priority.
  • action - This parameter accepts a reference to a function that we would like to execute when an event gets called.
  • argument - This parameter accepts list of values for parameter of function which we gave to action parameter.
  • kwargs - This parameter accepts a dictionary which has a mapping of parameter name and their value which are required by a function which we gave in action parameter.

We can give parameter values to the function as input by using either of argument or kwargs parameter.

The enter() method returns the object of type Event which has information about time when the event is scheduled to execute as well information about priority, reference to action function, and its arguments.

Please make a note that if any event takes a long time to execute which if pass the time of execution of future events then future events will be delayed by the extra time taken by the event. There are methods to cancel events that will be introduced in future examples.

In [15]:
import sched
from datetime import datetime
import time

def addition(a,b):
    print("\nInside Addition : ", datetime.now())
    print("Time : ", time.monotonic())
    print("Result : ", a+b)

s = sched.scheduler()

print("Start Time : ", datetime.now(), "\n")

event1 = s.enter(3, 1, addition, argument = (10,20))

print("Event Created : ", event1)

s.run()

print("\nEnd   Time : ", datetime.now())
Start Time :  2021-01-26 17:02:10.386526

Event Created :  Event(time=2349.783337, priority=1, action=<function addition at 0x7f42d5c56e18>, argument=(10, 20), kwargs={})

Inside Addition :  2021-01-26 17:02:13.396263
Time :  2349.78685869
Result :  30

End   Time :  2021-01-26 17:02:13.398676

Example 2

Our second example has exactly the same code as our first example with only a difference in a call to event() method. We have passed parameter values as dictionary for addition function using kwargs parameter.

Please make a note that sched module does not provide a way to collect return values if a function is returning some value (it'll be lost). It just executes the code inside of the function. If you want to maintain the return value of the function then you'll need to use some workaround like global variables or shared data structures available through the threading module in case of a multi-threading example.

In [16]:
import sched
from datetime import datetime
import time

def addition(a,b):
    print("\nInside Addition : ", datetime.now())
    print("Time : ", time.monotonic())
    print("Result : ", a+b)

s = sched.scheduler()

print("Start Time : ", datetime.now(), "\n")

event1 = s.enter(3, 1, addition, kwargs = {"a":10, "b":20})

print("Event Created : ", event1)

s.run()

print("\nEnd   Time : ", datetime.now())
Start Time :  2021-01-26 17:02:28.063455

Event Created :  Event(time=2367.453920403, priority=1, action=<function addition at 0x7f42d5c56ea0>, argument=(), kwargs={'a': 10, 'b': 20})

Inside Addition :  2021-01-26 17:02:31.066836
Time :  2367.457455959
Result :  30

End   Time :  2021-01-26 17:02:31.069057

Example 3

Our third example demonstrates how we can schedule events in the future at a particular time by using enterabs() method. Our majority of code is the same as our previous example with few minor changes. We are recording the time before the creation of the event and adding 5 units to it to create a new timestamp. We are then giving this time to enterabs() method to run the event after five-time units have passed.

scheduler.eventabs(time, priority, action, argument=(), kwargs={})

Below is an explanation of the parameters.

  • time - It accepts integer/floats as value and executes event at that time. It'll follow time units based on function provided to scheduler() method's timefunc parameter.
  • priority - It accepts integer/floats specifying the priority of the event. The lower the value, the higher the priority.
  • action - This parameter accepts a reference to a function that we would like to execute when an event gets called.
  • argument - This parameter accepts list of values for parameter of function which we gave to action parameter.
  • kwargs - This parameter accepts a dictionary which has a mapping of parameter name and their value which are required by a function which we gave in action parameter.
In [18]:
import sched
from datetime import datetime
import time

def addition(a,b):
    print("\nInside Addition : ", datetime.now())
    print("Time : ", time.monotonic())
    print("Result : ", a+b)

s = sched.scheduler()

print("Start Time : ", datetime.now(), "\n")

current_time = time.monotonic()
five_seconds_past_curr_time = current_time + 5

event1 = s.enterabs(five_seconds_past_curr_time, 1, addition, kwargs = {"a":10, "b":20})

print("Current Time  : ", current_time)
print("\nEvent Created : ", event1)

s.run()

print("\nEnd   Time : ", datetime.now())
Start Time :  2021-01-26 17:09:57.992568

Current Time  :  2814.383603151

Event Created :  Event(time=2819.383603151, priority=1, action=<function addition at 0x7f42d5c56598>, argument=(), kwargs={'a': 10, 'b': 20})

Inside Addition :  2021-01-26 17:10:02.998516
Time :  2819.389124872
Result :  30

End   Time :  2021-01-26 17:10:03.000554

Example 4

As a part of our fourth example, we have demonstrated how we can use functions with different time units with the scheduler. We have created a function named minute_delay_func() which takes as input number and delays execution by that many minutes. It uses sleep() method of time module for this. We have used time() function of time module as timefunc. It returns time as a number of seconds passed since Jan 1, 1971, and is based on a second-time unit. We have called enter() function with a delay of 0.5 which should start the event after 0.5 time units after its creation. The 0.5 time units in our case will become 30 seconds delay based on minute_delay_func.

The majority of our remaining code is almost the same as our previous example.

In [20]:
import sched
from datetime import datetime
import time

def minute_delay_func(delay=1):
    time.sleep(delay * 60)

def hour_delay_func(delay=1):
    time.sleep(delay * 60 * 60)


def addition(a,b):
    print("\nInside Addition : ", datetime.now())
    print("Time : ", time.time())
    print("Result : ", a+b)

s = sched.scheduler(timefunc=time.time, delayfunc=minute_delay_func)

print("Start Time : ", datetime.now(), "\n")

event1 = s.enter(0.5, 1, addition, kwargs = {"a":10, "b":20})

print("Event Created : ", event1)

s.run()

print("\nEnd   Time : ", datetime.now())
Start Time :  2021-01-26 17:15:51.942507

Event Created :  Event(time=1611661552.443486, priority=1, action=<function addition at 0x7f42d5c56ea0>, argument=(), kwargs={'a': 10, 'b': 20})

Inside Addition :  2021-01-26 17:16:21.941948
Time :  1611661581.9422078
Result :  30

End   Time :  2021-01-26 17:16:21.942605

Example 5

As a part of our fifth example, we demonstrate how we can scheduler more than one event and how different priority values affect the execution of events that are scheduled to run at the same time. We are using time.time() function as our time unit function of scheduler.

We have created five events. The first two start after 5 seconds delay of their creation and has a priority of 5 and 10 respectively. The next two start after 3 seconds delay of their creation and has a priority of 20 and 15 respectively. The last event starts after 1 second of its creation and has a priority of 25. All events execute our addition() function with different arguments to catch difference in execution sequence.

We can notice from the output that the fifth event runs first. Events 3 and 4 had the same execution time (3 seconds after creation) but different priorities. Event 3 had a priority of 20 and event 4 had a priority of 15, hence the scheduler executes event 4 first because lower priority means a higher chance of execution. Event 1 and 2 also had the same execution time (5 seconds after creation) but different priorities. Event 1 had a priority of 5 and event 2 had a priority of 10, hence the scheduler executes event 1 first due to its high priority.

In [21]:
import sched
from datetime import datetime
import time

def addition(a,b):
    print("\nInside Addition : ", datetime.now())
    print("Time : ", time.time())
    print("Result : ", a+b)

s = sched.scheduler(timefunc=time.time)

print("Start Time : ", datetime.now())

current_time = time.time()

event1 = s.enterabs(current_time +5, 5, addition, kwargs = {"a":10, "b":20})
event2 = s.enterabs(current_time +5, 10, addition, kwargs = {"a":20, "b":30})
event3 = s.enterabs(current_time +3, 20, addition, kwargs = {"a":30, "b":40})
event4 = s.enterabs(current_time +3, 15, addition, kwargs = {"a":40, "b":50})
event5 = s.enterabs(current_time +1, 25, addition, kwargs = {"a":50, "b":60})

s.run()


print("\nEnd   Time : ", datetime.now())
Start Time :  2021-01-26 17:16:21.955502

Inside Addition :  2021-01-26 17:16:22.956850
Time :  1611661582.9570847
Result :  110

Inside Addition :  2021-01-26 17:16:24.957921
Time :  1611661584.95812
Result :  90

Inside Addition :  2021-01-26 17:16:24.958273
Time :  1611661584.9583411
Result :  70

Inside Addition :  2021-01-26 17:16:26.957722
Time :  1611661586.9584885
Result :  30

Inside Addition :  2021-01-26 17:16:26.959396
Time :  1611661586.9598615
Result :  50

End   Time :  2021-01-26 17:16:26.960840

Example 6

Our sixth example demonstrates to us how we can get details of pending events at any time. Our code for this part is almost the same as our previous example with the minor addition of code to the previous example.

We have introduced the usage of queue attribute and empty() method of scheduler instance as a part of this example.

The queue attribute returns a list of events that are still pending for execution. It'll have events in the order in which they are pending for execution. It's a read-only attribute.

The empty() method returns boolean value True indicating that queue is empty hence not pending events to execute else False.

We have introduced code in addition method which checks whether the queue is empty and if it’s not empty then prints the number of events that are still pending. We are also printing a number of events that are pending at the beginning.

In [83]:
import sched
from datetime import datetime
import time

def addition(a,b):
    print("\nInside Addition : ", datetime.now())
    if not s.empty():
        print("Pending Events : ", len(s.queue))
    print("Result : ", a+b)

s = sched.scheduler(timefunc=time.time)

print("Start Time : ", datetime.now())

current_time = time.time()

event1 = s.enterabs(current_time +5, 5, addition, kwargs = {"a":10, "b":20})
event2 = s.enterabs(current_time +5, 10, addition, kwargs = {"a":20, "b":30})
event3 = s.enterabs(current_time +3, 20, addition, kwargs = {"a":30, "b":40})
event4 = s.enterabs(current_time +3, 15, addition, kwargs = {"a":40, "b":50})
event5 = s.enterabs(current_time +1, 25, addition, kwargs = {"a":50, "b":60})

print("\nPending Events at Beginning : ", len(s.queue))

s.run()


print("\nEnd   Time : ", datetime.now())
Start Time :  2021-01-25 14:05:02.477546

Pending Events at Beginning :  5

Inside Addition :  2021-01-25 14:05:03.478990
Pending Events :  4
Result :  110

Inside Addition :  2021-01-25 14:05:05.479970
Pending Events :  3
Result :  90

Inside Addition :  2021-01-25 14:05:05.481560
Pending Events :  2
Result :  70

Inside Addition :  2021-01-25 14:05:07.480123
Pending Events :  1
Result :  30

Inside Addition :  2021-01-25 14:05:07.481789
Result :  50

End   Time :  2021-01-25 14:05:07.483058

Example 7

As a part of our seventh example, we are demonstrating how we can cancel any pending event. Our code for this part is almost the same as our previous example with few additions and few modifications.

We can cancel events using cancel(event) method of the scheduler instance. We have modified our code for the addition where we have added a loop which goes through all events. It then checks if the time pending for execution of the event is more than 15 seconds then cancel that event.

We have modified our code where we create events to have all events a same priority. All the remaining code is almost the same as our previous examples.

When we run the code the only event which had more than 15 seconds pending to run was our fifth event which gets canceled. It would have printed the result of 110 of addition if got chance to execute.

In [85]:
import sched
from datetime import datetime
import time

def addition(a,b):
    print("\nInside Addition : ", datetime.now())
    if not s.empty():
        print("Pending Events : ", len(s.queue))

    events = s.queue
    for event in events:
        diff = event.time - time.time()
        if diff > 15:
            print("Cancelling Event : {}".format(event))
            s.cancel(event)

    print("Result : ", a+b)

s = sched.scheduler(timefunc=time.time)

print("Start Time : ", datetime.now(), "\n")

current_time = time.time()

event1 = s.enterabs(current_time +5, 1, addition, kwargs = {"a":10, "b":20})
event2 = s.enterabs(current_time +10, 1, addition, kwargs = {"a":20, "b":30})
event3 = s.enterabs(current_time +15, 1, addition, kwargs = {"a":30, "b":40})
event4 = s.enterabs(current_time +20, 1, addition, kwargs = {"a":40, "b":50})
event5 = s.enterabs(current_time +25, 1, addition, kwargs = {"a":50, "b":60})

s.run()


print("\nEnd   Time : ", datetime.now())
Start Time :  2021-01-25 14:05:30.853639


Inside Addition :  2021-01-25 14:05:35.861816
Pending Events :  4
Cancelling Event : Event(time=1611563755.8566895, priority=1, action=<function addition at 0x7fd58dd89c80>, argument=(), kwargs={'a': 50, 'b': 60})
Result :  30

Inside Addition :  2021-01-25 14:05:40.862024
Pending Events :  2
Result :  50

Inside Addition :  2021-01-25 14:05:45.860908
Pending Events :  1
Result :  70

Inside Addition :  2021-01-25 14:05:50.862050
Result :  90

End   Time :  2021-01-25 14:05:50.863862

Example 8

Our eighth example again demonstrates how we can cancel events. The code for this example is almost the same as our previous example but with a minor change in condition in addition() method which checks for canceling events. We have changed the condition which cancels events that are scheduled to run after more than 5 seconds from the current time and have a priority greater than 5 seconds. We are canceling events that are a little far away to execute in the future and have low priority. We have also modified the priority of five events during creation.

When we run the below code, 2 events get canceled (events 2 and 5).

In [86]:
import sched
from datetime import datetime
import time

def addition(a,b):
    print("\nInside Addition : ", datetime.now())
    if not s.empty():
        print("Pending Events : ", len(s.queue))

    events = s.queue
    for event in events:
        diff = event.time - time.time()
        if diff > 5 and event.priority > 5:
            print("Cancelling Event : {}".format(event))
            s.cancel(event)

    print("Result : ", a+b)

s = sched.scheduler(timefunc=time.time)

print("Start Time : ", datetime.now(), "\n")

current_time = time.time()

event1 = s.enterabs(current_time +5, 10, addition, kwargs = {"a":10, "b":20})
event2 = s.enterabs(current_time +15, 7, addition, kwargs = {"a":20, "b":30})
event3 = s.enterabs(current_time +15, 4, addition, kwargs = {"a":30, "b":40})
event4 = s.enterabs(current_time +20, 3, addition, kwargs = {"a":40, "b":50})
event5 = s.enterabs(current_time +20, 10, addition, kwargs = {"a":50, "b":60})

s.run()


print("\nEnd   Time : ", datetime.now())
Start Time :  2021-01-25 14:05:50.874577


Inside Addition :  2021-01-25 14:05:55.885735
Pending Events :  4
Cancelling Event : Event(time=1611563765.8805854, priority=7, action=<function addition at 0x7fd58deb9bf8>, argument=(), kwargs={'a': 20, 'b': 30})
Cancelling Event : Event(time=1611563770.8805854, priority=10, action=<function addition at 0x7fd58deb9bf8>, argument=(), kwargs={'a': 50, 'b': 60})
Result :  30

Inside Addition :  2021-01-25 14:06:05.880867
Pending Events :  1
Result :  70

Inside Addition :  2021-01-25 14:06:10.885838
Result :  90

End   Time :  2021-01-25 14:06:10.886389

Example 9

Our ninth example demonstrates us if somehow the call to run() method of scheduler gets delayed and the time to execute event has passed then it'll execute events immediately after it starts for which time has passed.

Our code for this example with exactly the same as our first example with the addition of 1 line before the call to run(). We have introduced a sleep time of 5 seconds before starting the scheduler. The event is created and scheduled to run 3 seconds after its creation. This will result in the event getting executed immediately once the scheduler has started because its time to run has already passed.

In [9]:
import sched
from datetime import datetime
import time

def addition(a,b):
    print("\nInside Addition : ", datetime.now())
    print("Result : ", a+b)

s = sched.scheduler()

print("Start Time : ", datetime.now(), "\n")

event1 = s.enter(3, 1, addition, argument = (10,20))

print("Event Created : ", event1)

time.sleep(5)

s.run()

print("\nEnd   Time : ", datetime.now())
Start Time :  2021-01-26 16:42:24.446233

Event Created :  Event(time=1163.837401092, priority=1, action=<function addition at 0x7f42e42d1598>, argument=(10, 20), kwargs={})

Inside Addition :  2021-01-26 16:42:29.452650
Result :  30

End   Time :  2021-01-26 16:42:29.454266

This ends our small tutorial demonstrating the usage of various methods of sched module API. Please feel free to let us know your views in the comments section.



Sunny Solanki  Sunny Solanki