pyguides

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:

  • timefunc returns the current time (any unit, any zero point). Default is time.monotonic — suitable for measuring elapsed intervals.
  • delayfunc delays for a given number of time units. Default is time.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