sched module
The sched module provides a general-purpose event scheduler. You give it a time source and a delay function, then schedule callable objects to run after a delay or at an absolute time. Events run sequentially — there’s no concurrency — which makes it simple and predictable.
The Scheduler Constructor
import sched
scheduler = sched.scheduler(timefunc=time.monotonic, delayfunc=time.sleep)
The constructor takes two callables:
timefuncreturns the current time (any unit, any zero point). Default istime.monotonic— suitable for measuring elapsed intervals.delayfuncdelays for a given number of time units. Default istime.sleep.
You can swap either one. For example, if you want to use wall-clock time, pass timefunc=time.time and delayfunc=time.sleep.
Scheduling with enter
enter() schedules an event to run after a relative delay:
import sched, time
scheduler = sched.scheduler()
def print_hello():
print("hello at", time.time())
# Run print_hello 5 seconds from now, priority 1
event = scheduler.enter(5, 1, print_hello)
The event returned is an opaque object you can use to cancel the event later.
The priority only matters when two events are scheduled for the exact same time — lower numbers run first:
# Two events at the same absolute time
scheduler.enterabs(1000, 2, print_hello) # priority 2, runs second
scheduler.enterabs(1000, 1, print_hello) # priority 1, runs first
Scheduling with enterabs
enterabs() schedules an event at an absolute timestamp:
def print_hello():
print("hello at", time.time())
now = time.time()
scheduler.enterabs(now + 10, 1, print_hello) # 10 seconds from now
Both enter() and enterabs() accept argument (a tuple) and kwargs (a dict) for passing arguments to the callable:
scheduler.enter(5, 1, print, argument=("hello",))
scheduler.enter(5, 1, print, argument=(), kwargs={"end": "!\n"})
The Event Object and cancel
enter() and enterabs() return an event object. You can pass it to cancel() to remove the event before it runs:
event = scheduler.enter(10, 1, print_hello)
scheduler.cancel(event) # removes it, no error if already run
If you try to cancel an event that already ran (or wasn’t in the queue), cancel() raises ValueError.
Checking the Queue
empty() returns True when there are no events scheduled:
scheduler.empty() # True or False
scheduler.queue is a read-only list of all upcoming events, in the order they’ll run. Each entry is a named tuple with fields: (time, priority, action, argument, kwargs):
for evt in scheduler.queue:
print(f" at {evt.time}: {evt.action.__name__}")
Running the Scheduler
run() starts executing events. By default it blocks until all events have run:
scheduler.run() # blocks until done
While waiting between events, it calls delayfunc with the time until the next event. This means if you schedule an event for 10 seconds from now and nothing else is scheduled before it, run() will sleep for 10 seconds.
If you pass blocking=False, run() executes all events whose time has passed, then returns immediately:
deadline = scheduler.run(blocking=False)
# deadline is the next event's timestamp, or None if queue is empty
This is useful when you need to interleave the scheduler with other work in a loop.
Important Behaviors
run() is blocking. It doesn’t return until the queue is empty (or you pass blocking=False). Don’t call run() on the main thread without understanding this — it will freeze your program.
Events can fall behind. If one event takes longer than the interval before the next, the scheduler falls behind. It will not skip or drop events — it just keeps running them late. Your code is responsible for canceling events that are no longer relevant.
Exceptions stop the chain. If an event’s callable raises an exception, that exception propagates out of run() and no further events run. Wrap event callables in try/except if you need resilience.
Thread-safe. Since Python 3.3, scheduler is safe to use from multiple threads.
Basic Example
import sched, time
def job(name):
print(f"{name} at {time.time():.2f}")
s = sched.scheduler(time.time, time.sleep)
now = time.time()
s.enterabs(now + 1, 1, job, argument=("one",))
s.enterabs(now + 2, 1, job, argument=("two",))
s.enterabs(now + 3, 1, job, argument=("three",))
print("Starting scheduler at", time.time())
s.run()
See Also
- time module —
time.time(),time.monotonic(), andtime.sleep() - threading module — running scheduler in a background thread
- asyncio basics — async alternatives for Python async programs