Schedule Documentation: Release 1.1.0
Schedule Documentation: Release 1.1.0
Release 1.1.0
Daniel Bader
1 Example 3
3 Read More 7
3.1 Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
3.2 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
3.3 Run in the background . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
3.4 Parallel execution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
3.5 Exception Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
3.6 Logging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
3.7 Multiple schedulers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.8 Frequently Asked Questions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
3.9 Reference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3.10 Development . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
3.11 History . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
4 Issues 29
5 About Schedule 31
Index 35
i
ii
schedule Documentation, Release 1.1.0
Python job scheduling for humans. Run Python functions (or any other callable) periodically using a friendly syntax.
• A simple to use API for scheduling jobs, made for humans.
• In-process scheduler for periodic jobs. No extra processes needed!
• Very lightweight and no external dependencies.
• Excellent test coverage.
• Tested on Python 3.6, 3.7, 3.8 and 3.9
Contents 1
schedule Documentation, Release 1.1.0
2 Contents
CHAPTER 1
Example
import schedule
import time
def job():
print("I'm working...")
schedule.every(10).minutes.do(job)
schedule.every().hour.do(job)
schedule.every().day.at("10:30").do(job)
schedule.every().monday.do(job)
schedule.every().wednesday.at("13:15").do(job)
schedule.every().minute.at(":17").do(job)
while True:
schedule.run_pending()
time.sleep(1)
More Examples
3
schedule Documentation, Release 1.1.0
4 Chapter 1. Example
CHAPTER 2
Let’s be honest, Schedule is not a ‘one size fits all’ scheduling library. This library is designed to be a simple solution
for simple scheduling problems. You should probably look somewhere else if you need:
• Job persistence (remember schedule between restarts)
• Exact timing (sub-second precision execution)
• Concurrent execution (multiple threads)
• Localization (time zones, workdays or holidays)
Schedule does not account for the time it takes for the job function to execute. To guarantee a stable execution
schedule you need to move long-running jobs off the main-thread (where the scheduler runs). See Parallel execution
for a sample implementation.
5
schedule Documentation, Release 1.1.0
Read More
3.1 Installation
We recommend using the latest version of Python. Schedule is tested on Python 3.6, 3.7, 3.8 and 3.9
Want to use Schedule on Python 2.7 or 3.5? Use version 0.6.0.
3.1.2 Dependencies
Schedule has no dependencies. None. Zero. Nada. Nopes. We plan to keep it that way.
PIP (preferred)
The recommended way to install this package is to use pip. Use the following command to install it:
Schedule is now installed. Check out the examples or go to the the documentation overview.
Schedule is available through some linux package managers. These packages are not maintained by the maintainers of
this project. It cannot be guarantee that these packages are up-to-date (and will stay up-to-date) with the latest released
version. If you don’t mind having an old version, you can use it.
7
schedule Documentation, Release 1.1.0
Ubuntu
OUTDATED! At the time of writing, the packages for 20.04LTS and below use version 0.3.2 (2015).
Debian
OUTDATED! At the time of writing, the packages for buster and below use version 0.3.2 (2015).
Arch
On the Arch Linux User repository (AUR) the package is available using the name python-schedule. See the package
page here. For yay users, run:
$ yay -S python-schedule
Conda (Anaconda)
Install manually
If you don’t have access to a package manager or need more control, you can manually copy the library into your
project. This is easy as the schedule library consists of a single sourcefile MIT licenced. However, this method is
highly discouraged as you won’t receive automatic updates.
1. Go to the Github repo.
2. Open file schedule/__init__.py and copy the code.
3. In your project, create a packaged named schedule and paste the code in a file named __init__.py.
3.2 Examples
Eager to get started? This page gives a good introduction to Schedule. It assumes you already have Schedule installed.
If you do not, head over to Installation.
import schedule
import time
def job():
print("I'm working...")
while True:
schedule.run_pending()
time.sleep(1)
Use the @repeat to schedule a function. Pass it an interval using the same syntax as above while omitting the
.do().
@repeat(every(10).minutes)
def job():
print("I am a scheduled job")
while True:
run_pending()
time.sleep(1)
3.2. Examples 9
schedule Documentation, Release 1.1.0
import schedule
def greet(name):
print('Hello', name)
schedule.every(2).seconds.do(greet, name='Alice')
schedule.every(4).seconds.do(greet, name='Bob')
@repeat(every().second, "World")
@repeat(every().day, "Mars")
def hello(planet):
print("Hello", planet)
import schedule
def some_task():
print('Hello world')
job = schedule.every().day.at('22:30').do(some_task)
schedule.cancel_job(job)
import schedule
import time
def job_that_executes_once():
# Do some work that only needs to happen once...
return schedule.CancelJob
schedule.every().day.at('22:30').do(job_that_executes_once)
while True:
schedule.run_pending()
time.sleep(1)
import schedule
def hello():
print('Hello world')
schedule.every().second.do(hello)
all_jobs = schedule.get_jobs()
def greet(name):
print('Hello {}'.format(name))
schedule.every().second.do(greet)
schedule.clear()
You can retrieve a group of jobs from the scheduler, selecting them by a unique identifier.
import schedule
def greet(name):
print('Hello {}'.format(name))
friends = schedule.get_jobs('friend')
You can cancel the scheduling of a group of jobs selecting them by a unique identifier.
import schedule
def greet(name):
print('Hello {}'.format(name))
3.2. Examples 11
schedule Documentation, Release 1.1.0
schedule.clear('daily-tasks')
def my_job():
print('Foo')
every(A).to(B).seconds executes the job function every N seconds such that A <= N <= B.
import schedule
from datetime import datetime, timedelta, time
def job():
print('Boo')
The until method sets the jobs deadline. The job will not run after the deadline.
Use schedule.idle_seconds() to get the number of seconds until the next job is scheduled to run. The returned
value is negative if the next scheduled jobs was scheduled to run in the past. Returns None if no jobs are scheduled.
import schedule
import time
def job():
print('Hello')
schedule.every(5).seconds.do(job)
(continues on next page)
while 1:
n = schedule.idle_seconds()
if n is None:
# no more jobs
break
elif n > 0:
# sleep exactly the right amount of time
time.sleep(n)
schedule.run_pending()
To run all jobs regardless if they are scheduled to run or not, use schedule.run_all(). Jobs are re-scheduled
after finishing, just like they would if they were executed using run_pending().
import schedule
def job_1():
print('Foo')
def job_2():
print('Bar')
schedule.every().monday.at("12:40").do(job_1)
schedule.every().tuesday.at("16:40").do(job_2)
schedule.run_all()
Out of the box it is not possible to run the schedule in the background. However, you can create a thread yourself and
use it to run jobs without blocking the main thread. This is an example of how you could do this:
import threading
import time
import schedule
def run_continuously(interval=1):
"""Continuously run, while executing pending jobs at each
elapsed time interval.
@return cease_continuous_run: threading. Event which can
be set to cease continuous run. Please note that it is
*intended behavior that run_continuously() does not run
missed jobs*. For example, if you've registered a job that
should run every minute and you set a continuous run
(continues on next page)
class ScheduleThread(threading.Thread):
@classmethod
def run(cls):
while not cease_continuous_run.is_set():
schedule.run_pending()
time.sleep(interval)
continuous_thread = ScheduleThread()
continuous_thread.start()
return cease_continuous_run
def background_job():
print('Hello from the background thread')
schedule.every().second.do(background_job)
I am trying to execute 50 items every 10 seconds, but from the my logs it says it executes every item in 10 second
schedule serially, is there a work around?
By default, schedule executes all jobs serially. The reasoning behind this is that it would be difficult to find a model
for parallel execution that makes everyone happy.
You can work around this limitation by running each of the jobs in its own thread:
import threading
import time
import schedule
def job():
print("I'm running on thread %s" % threading.current_thread())
def run_threaded(job_func):
job_thread = threading.Thread(target=job_func)
job_thread.start()
schedule.every(10).seconds.do(run_threaded, job)
schedule.every(10).seconds.do(run_threaded, job)
(continues on next page)
while 1:
schedule.run_pending()
time.sleep(1)
If you want tighter control on the number of threads use a shared jobqueue and one or more worker threads:
import time
import threading
import schedule
import queue
def job():
print("I'm working")
def worker_main():
while 1:
job_func = jobqueue.get()
job_func()
jobqueue.task_done()
jobqueue = queue.Queue()
schedule.every(10).seconds.do(jobqueue.put, job)
schedule.every(10).seconds.do(jobqueue.put, job)
schedule.every(10).seconds.do(jobqueue.put, job)
schedule.every(10).seconds.do(jobqueue.put, job)
schedule.every(10).seconds.do(jobqueue.put, job)
worker_thread = threading.Thread(target=worker_main)
worker_thread.start()
while 1:
schedule.run_pending()
time.sleep(1)
This model also makes sense for a distributed application where the workers are separate processes that receive jobs
from a distributed work queue. I like using beanstalkd with the beanstalkc Python library.
Schedule doesn’t catch exceptions that happen during job execution. Therefore any exceptions thrown during job
execution will bubble up and interrupt schedule’s run_xyz function.
If you want to guard against exceptions you can wrap your job function in a decorator like this:
import functools
def catch_exceptions(cancel_on_failure=False):
def catch_exceptions_decorator(job_func):
(continues on next page)
@catch_exceptions(cancel_on_failure=True)
def bad_task():
return 1 / 0
schedule.every(5).minutes.do(bad_task)
Another option would be to subclass Schedule like @mplewis did in this example.
3.6 Logging
Schedule logs messages to the Python logger named schedule at DEBUG level. To receive logs from Schedule, set
the logging level to DEBUG.
import schedule
import logging
logging.basicConfig()
schedule_logger = logging.getLogger('schedule')
schedule_logger.setLevel(level=logging.DEBUG)
def job():
print("Hello, Logs")
schedule.every().second.do(job)
schedule.run_all()
schedule.clear()
The easiest way to add reusable logging to jobs is to implement a decorator that handles logging. As an example,
below code adds the print_elapsed_time decorator:
import functools
import time
import schedule
# This decorator can be applied to any job function to log the elapsed time of each
˓→job
def print_elapsed_time(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start_timestamp = time.time()
print('LOG: Running job "%s"' % func.__name__)
result = func(*args, **kwargs)
print('LOG: Job "%s" completed in %d seconds' % (func.__name__, time.time() -
˓→start_timestamp))
return result
return wrapper
@print_elapsed_time
def job():
print('Hello, Logs')
time.sleep(5)
schedule.every().second.do(job)
schedule.run_all()
This outputs:
LOG: Running job "job"
Hello, Logs
LOG: Job "job" completed in 5 seconds
You can run as many jobs from a single scheduler as you wish. However, for larger installations it might be desirable
to have multiple schedulers. This is supported:
import time
import schedule
def fooJob():
print("Foo")
def barJob():
print("Bar")
while True:
# run_pending needs to be called on every scheduler
scheduler1.run_pending()
scheduler2.run_pending()
time.sleep(1)
Frequently asked questions on the usage of schedule. Did you get here using an ‘old’ link and expected to see more
questions?
I’m getting
It seems python can’t find the schedule package. Let’s check some common causes.
Did you install schedule? If not, follow Installation. Validate installation:
• Did you install using pip? Run pip3 list | grep schedule. This should return schedule 0.6.0
(or a higher version number)
• Did you install using apt? Run dpkg -l | grep python3-schedule. This should return something
along the lines of python3-schedule 0.3.2-1.1 Job scheduling for humans (Python
3) (or a higher version number)
Are you used python 3 to install Schedule, and are running the script using python 3? For example, if you installed
schedule using a version of pip that uses Python 2, and your code runs in Python 3, the package won’t be found. In
this case the solution is to install Schedule using pip3: pip3 install schedule.
Are you using virtualenv? Check that you are running the script inside the same virtualenv where you installed
schedule.
Is this problem occurring when running the program from inside and IDE like PyCharm or VSCode? Try to run your
program from a commandline outside of the IDE. If it works there, the problem is with your IDE configuration. It
might be that your IDE uses a different Python interpreter installation.
Still having problems? Use Google and StackOverflow before submitting an issue.
Vanilla schedule doesn’t support time zones at the moment. If you need this functionality please check out @imiric’s
work here. He added time zone support to schedule using python-dateutil.
See Examples.
See Examples.
3.8.8 How to continuously run the scheduler without blocking the main thread?
Background Execution.
If you are left with an unanswered question, browse the issue tracker to see if your question has been asked before.
Feel free to create a new issue if that’s not the case. Thank you
3.9 Reference
3.9. Reference 19
schedule Documentation, Release 1.1.0
3.9.2 Classes
class schedule.Scheduler
Objects instantiated by the Scheduler are factories to create jobs, keep record of scheduled jobs and handle
their execution.
run_pending() → None
Run all jobs that are scheduled to run.
Please note that it is intended behavior that run_pending() does not run missed jobs. For example, if you’ve
registered a job that should run every minute and you only call run_pending() in one hour increments then
your job won’t be run 60 times in between but only once.
run_all(delay_seconds: int = 0) → None
Run all jobs regardless if they are scheduled to run or not.
A delay of delay seconds is added between each job. This helps distribute system load generated by the
jobs more evenly over time.
Parameters delay_seconds – A delay added between every executed job
get_jobs(tag: Optional[collections.abc.Hashable] = None) → List[schedule.Job]
Gets scheduled jobs marked with the given tag, or all jobs if tag is omitted.
Parameters tag – An identifier used to identify a subset of jobs to retrieve
clear(tag: Optional[collections.abc.Hashable] = None) → None
Deletes scheduled jobs marked with the given tag, or all jobs if tag is omitted.
Parameters tag – An identifier used to identify a subset of jobs to delete
cancel_job(job: schedule.Job) → None
Delete a scheduled job.
Parameters job – The job to be unscheduled
every(interval: int = 1) → schedule.Job
Schedule a new periodic job.
Parameters interval – A quantity of a certain time unit
Returns An unconfigured Job
next_run
Datetime when the next job should run.
3.9. Reference 21
schedule Documentation, Release 1.1.0
deadline, CancelJob is returned after the execution. In this latter case CancelJob takes priority over any
other returned value.
Returns The return value returned by the job_func, or CancelJob if the job’s deadline is reached.
3.9.3 Exceptions
exception schedule.CancelJob
Can be returned from a job to unschedule itself.
3.10 Development
These instructions are geared towards people who want to help develop this library.
All required tooling and libraries can be installed using the requirements-dev.txt file:
pytest is used to run tests. Run all tests with coverage and formatting checks:
black .
The documentation is written in reStructuredText. It is processed using Sphinx using the alabaster theme. After
installing the development requirements it is just a matter of running:
cd docs
make html
3.10. Development 23
schedule Documentation, Release 1.1.0
Update the HISTORY.rst and AUTHORS.rst files. Bump the version in setup.py and docs/conf.py.
Merge these changes into master. Finally:
3.11 History
Depending on your configuration, the following bugfixes might change schedule’s behaviour:
• Fix: idle_seconds crashes when no jobs are scheduled. See #401. Thanks @yoonghm!
• Fix: day.at(‘HH:MM:SS’) where HMS=now+10s doesn’t run today. See #331. Thanks @qmorek!
• Fix: hour.at(‘MM:SS’), the seconds are set to 00. See #290. Thanks @eladbi!
• Fix: Long-running jobs skip a day when they finish in the next day #404. Thanks @4379711!
Other changes:
• Dropped Python 2.7 and 3.5 support, added 3.8 and 3.9 support. See #409
• Fix RecursionError when the job is passed to the do function as an arg. See #190. Thanks @connorskees!
• Fix DeprecationWarning of ‘collections’. See #296. Thanks @gaguirregabiria!
• Replaced Travis with Github Actions for automated testing
• Revamp and extend documentation. See #395
• Improved tests. Thanks @connorskees and @Jamim!
• Changed log messages to DEBUG level. Thanks @aisk!
• Make at() accept timestamps with 1 second precision (#267). Thanks @NathanWailes!
• Introduce proper exception hierarchy (#271). Thanks @ConnorSkees!
• Fixed issues where scheduling a job with a functools.partial as the job function fails. Thanks @dylwhich.
• Fixed an issue where scheduling a job to run every >= 2 days would cause the initial execution to happen one
day early. Thanks @WoLfulus for identifying this and providing a fix.
• Added a FAQ item to describe how to schedule a job that runs only once.
• Fixed an issue with unicode handling in setup.py that was causing trouble on Python 3 and Debian (https:
//github.com/dbader/schedule/issues/27). Thanks to @waghanza for reporting it.
• Added an FAQ item to describe how to deal with job functions that throw exceptions. Thanks @mplewis.
3.11. History 25
schedule Documentation, Release 1.1.0
• Fixed an issue with next_run() throwing a ValueError exception when the job queue is empty. Thanks
to @dpagano for pointing this out and thanks to @mrhwick for quickly providing a fix.
• Fixed issue with at_time jobs not running on the same day the job is created (Thanks to @mattss)
• Added schedule.next_run()
• Added schedule.idle_seconds()
• Args passed into do() are forwarded to the job function at call time
• Increased test coverage to 100%
• Fix packaging
• README fixes
• Initial release
3.11. History 27
schedule Documentation, Release 1.1.0
Issues
If you encounter any problems, please file an issue along with a detailed description. Please also use the search feature
in the issue tracker beforehand to avoid creating duplicates. Thank you
29
schedule Documentation, Release 1.1.0
30 Chapter 4. Issues
CHAPTER 5
About Schedule
31
schedule Documentation, Release 1.1.0
• aisk <https://github.com/aisk>
• MichaelCorleoneLi <https://github.com/MichaelCorleoneLi>
• sijmenhuizenga <https://github.com/SijmenHuizenga>
• eladbi <https://github.com/eladbi>
• chankeypathak <https://github.com/chankeypathak>
• vubon <https://github.com/vubon>
• gaguirregabiria <https://github.com/gaguirregabiria>
• rhagenaars <https://github.com/RHagenaars>
• Skenvy <https://github.com/skenvy>
• zcking <https://github.com/zcking>
• Martin Thoma <https://github.com/MartinThoma>
• ebllg <https://github.com/ebllg>
• fredthomsen <https://github.com/fredthomsen>
• biggerfisch <https://github.com/biggerfisch>
• sosolidkk <https://github.com/sosolidkk>
s
schedule, 19
33
schedule Documentation, Release 1.1.0
A M
at() (schedule.Job method), 21 minute (schedule.Job attribute), 21
minutes (schedule.Job attribute), 21
C monday (schedule.Job attribute), 21
cancel_job() (in module schedule), 20
cancel_job() (schedule.Scheduler method), 20 N
CancelJob, 23 next_run (schedule.Scheduler attribute), 20
clear() (in module schedule), 20 next_run() (in module schedule), 20
clear() (schedule.Scheduler method), 20
R
D run() (schedule.Job method), 22
day (schedule.Job attribute), 21 run_all() (in module schedule), 19
days (schedule.Job attribute), 21 run_all() (schedule.Scheduler method), 20
default_scheduler (in module schedule), 19 run_pending() (in module schedule), 19
do() (schedule.Job method), 22 run_pending() (schedule.Scheduler method), 20
E S
every() (in module schedule), 19 saturday (schedule.Job attribute), 21
every() (schedule.Scheduler method), 20 schedule (module), 19
Scheduler (class in schedule), 20
F second (schedule.Job attribute), 21
friday (schedule.Job attribute), 21 seconds (schedule.Job attribute), 21
should_run (schedule.Job attribute), 22
G sunday (schedule.Job attribute), 21
get_jobs() (in module schedule), 20
get_jobs() (schedule.Scheduler method), 20
T
tag() (schedule.Job method), 21
H thursday (schedule.Job attribute), 21
hour (schedule.Job attribute), 21 to() (schedule.Job method), 22
hours (schedule.Job attribute), 21 tuesday (schedule.Job attribute), 21
I U
idle_seconds (schedule.Scheduler attribute), 21 until() (schedule.Job method), 22
idle_seconds() (in module schedule), 20
W
J wednesday (schedule.Job attribute), 21
Job (class in schedule), 21 week (schedule.Job attribute), 21
jobs (in module schedule), 19 weeks (schedule.Job attribute), 21
35