Python sched Scheduler — Core Concepts

What sched does

The sched module provides a general-purpose event scheduler. You create a scheduler, add events with times and callbacks, then run the scheduler. It processes events in chronological order, sleeping between them.

import sched
import time

s = sched.scheduler(time.time, time.sleep)

def greet(name):
    print(f"{time.strftime('%H:%M:%S')} Hello, {name}!")

s.enter(2, 1, greet, argument=("Alice",))
s.enter(5, 1, greet, argument=("Bob",))
s.run()

enter(delay, priority, action, argument, kwargs) schedules a callback to run after delay seconds. Lower priority numbers run first if two events have the same time.

Relative vs absolute scheduling

MethodTime referenceUse case
enter(delay, ...)Relative — seconds from now”Do this in 30 seconds”
enterabs(time, ...)Absolute — epoch timestamp”Do this at 14:30:00”
import sched, time

s = sched.scheduler(time.time, time.sleep)

# Absolute: run at a specific epoch time
target = time.time() + 10  # 10 seconds from now
s.enterabs(target, 1, print, argument=("Fired!",))
s.run()

enterabs is useful when you’ve calculated exact timestamps, like “next hour on the dot.”

Cancellation

enter and enterabs return an event object you can cancel before it fires:

s = sched.scheduler(time.time, time.sleep)

event = s.enter(60, 1, print, argument=("This won't run",))
s.cancel(event)  # removed from the queue

Calling cancel on an already-fired or already-cancelled event raises ValueError.

Priority tie-breaking

When two events are scheduled for the same time, the one with the lower priority number runs first:

s = sched.scheduler(time.time, time.sleep)
s.enter(0, 2, print, argument=("Second",))
s.enter(0, 1, print, argument=("First",))
s.run()
# Output: First, then Second

Inspecting the queue

s = sched.scheduler(time.time, time.sleep)
s.enter(10, 1, print, argument=("A",))
s.enter(5, 1, print, argument=("B",))

print(s.queue)  # list of Event namedtuples, sorted by time
print(s.empty())  # False

s.queue returns a sorted list of pending events. Each event is a named tuple with time, priority, sequence, action, argument, kwargs.

Repeating events

sched doesn’t have a built-in repeat mechanism, but you can reschedule inside the callback:

import sched, time

s = sched.scheduler(time.time, time.sleep)

def heartbeat():
    print(f"{time.strftime('%H:%M:%S')} ping")
    s.enter(5, 1, heartbeat)  # reschedule itself

s.enter(0, 1, heartbeat)  # start immediately
s.run()

This creates an infinite loop of events firing every 5 seconds. Add a condition to stop when needed.

Custom time functions

The scheduler takes two functions: a “time” function and a “delay” function. You can substitute these for testing or simulation:

fake_time = [0]

def get_time():
    return fake_time[0]

def advance(seconds):
    fake_time[0] += seconds

s = sched.scheduler(get_time, advance)
s.enter(10, 1, print, argument=("Event at t=10",))
s.run()  # runs instantly — fake time jumps forward

This is powerful for testing: you can verify scheduling logic without waiting real seconds.

Common misconception

Many developers think sched runs events in background threads. It doesn’t. s.run() blocks the calling thread. If you need non-blocking scheduling, run the scheduler in a separate thread:

import threading

t = threading.Thread(target=s.run, daemon=True)
t.start()

When sched is enough vs when it’s not

Scenariosched?Better alternative
Simple script with timed events
Testing time-dependent logic
Recurring jobs in productionCelery Beat, APScheduler, cron
Thousands of concurrent timersasyncio event loop
Distributed task schedulingCelery, Temporal, Airflow
GUI event loopsFramework’s built-in scheduler

sched is a single-process, single-thread tool. It’s not designed for scale, persistence, or fault tolerance. For production workloads, tools like APScheduler or Celery Beat provide persistence, missed-job recovery, and distributed execution.

The one thing to remember: sched is Python’s built-in event scheduler — perfect for simple timed tasks and testing, but reach for APScheduler or Celery when you need persistence, concurrency, or fault tolerance.

pythonstandard-libraryconcurrency

See Also

  • Python Atexit How Python's atexit module lets your program clean up after itself right before it shuts down.
  • Python Bisect Sorted Lists How Python's bisect module finds things in sorted lists the way you'd find a word in a dictionary — by jumping to the middle.
  • Python Contextlib How Python's contextlib module makes the 'with' statement work for anything, not just files.
  • Python Copy Module Why copying data in Python isn't as simple as it sounds, and how the copy module prevents sneaky bugs.
  • Python Dataclass Field Metadata How Python dataclass fields can carry hidden notes — like sticky notes on a filing cabinet that tools read automatically.