Python Central HubSign in

Automation and Scripting / Phase 6: Task Scheduling & Deployment

Using the schedule Library for Python

Run jobs on a schedule inside Python with the schedule package.

Why schedule

It’s simple for:

  • “run every hour”
  • “run at 09
    daily”

Example

Python
import schedule
import time
 
 
def job():
    print("running")
 
 
schedule.every(10).seconds.do(job)
 
while True:
    schedule.run_pending()
    time.sleep(1)

Notes

  • this keeps a process running
  • for server deployment, cron/systemd is usually better

How the schedule library works

schedule is a small package (pip install schedule) that lets you describe timing in nearly plain English. It does not run anything by itself and it does not use threads. You register jobs, then call schedule.run_pending() in a loop, and each call runs the jobs that are due. That is why the example has a while True loop with a one-second sleep: without it nothing would ever fire.

Some ways to describe a schedule:

Python
import schedule
 
 
def job(name="job"):
    print("running", name)
 
 
schedule.every(10).minutes.do(job)                 # every ten minutes
schedule.every().hour.do(job)                      # every hour
schedule.every().day.at("09:00").do(job)           # daily at 09:00
schedule.every().monday.at("08:30").do(job)        # Mondays at 08:30
schedule.every(2).to(5).seconds.do(job)            # random gap of 2 to 5 seconds
schedule.every().day.at("18:00").do(job, name="evening")   # pass arguments
 
print(len(schedule.get_jobs()), "jobs registered")
print("next run at", schedule.next_run())

Times given with .at() use the local time of the machine. schedule.next_run() shows when the next job is due, which is a quick check that you described the timing correctly.

Errors, and jobs that run once

If a job raises an exception, run_pending() raises it too, and your loop ends. For a service that should keep going, wrap the job so that failures are logged and the loop continues. Return schedule.CancelJob from a job to make it run only once.

Python
import functools
import logging
import time
 
import schedule
 
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
log = logging.getLogger("scheduler")
 
 
def safe(fn):
    @functools.wraps(fn)
    def wrapper(*args, **kwargs):
        try:
            return fn(*args, **kwargs)
        except Exception:
            log.exception("job %s failed", fn.__name__)
    return wrapper
 
 
@safe
def report():
    log.info("building report")
 
 
def once():
    log.info("this runs only once")
    return schedule.CancelJob
 
 
schedule.every().day.at("09:00").do(report)
schedule.every(5).seconds.do(once)
 
try:
    while True:
        schedule.run_pending()
        time.sleep(1)
except KeyboardInterrupt:
    log.info("stopped")

The safe decorator catches and logs any error from a job, and log.exception keeps the traceback. functools.wraps keeps the original function name in the messages.

Limits you need to know

  • One job at a time. Jobs run in order inside your loop. A job that takes ten minutes delays everything after it. Run long work in a thread or a separate process.
  • No memory. If the process stops, or the machine sleeps, schedules restart from zero and missed runs are not made up.
  • Not a service. The script must stay running. For a real server, cron, systemd timers, or Task Scheduler start the script again if it crashes, and survive reboots. Use schedule for a small always-on tool, or inside a container.
  • Time zones. It uses the machine's local time and does not handle daylight-saving changes specially.

Common mistakes

  • Calling the function instead of passing it: .do(job()) runs it now, and .do(job) schedules it.
  • Forgetting the run_pending loop.
  • A long time.sleep in the loop, which delays jobs. One second is normal.
  • Running the script in a terminal that you close. Use a service manager.
  • No logging, so you cannot tell whether it ran.

🧪 Try It Yourself

Exercise 1 – List Files with os.listdir

exerciseYour turn

Tab indents. Press Escape, then Tab, to leave the editor. Control plus Enter runs the code; Control plus Shift plus Enter submits it.

Output
Press Run to see what your code prints.

Exercise 2 – Join Paths with os.path.join

exerciseYour turn

Tab indents. Press Escape, then Tab, to leave the editor. Control plus Enter runs the code; Control plus Shift plus Enter submits it.

Output
Press Run to see what your code prints.

Exercise 3 – Write and Read a File

exerciseYour turn

Tab indents. Press Escape, then Tab, to leave the editor. Control plus Enter runs the code; Control plus Shift plus Enter submits it.

Output
Press Run to see what your code prints.

Was this page helpful?

Let us know how we did