Python task scheduling with a simple, English syntax.
Project description
Booker
Tested on Python 2.7, 3.5, 3.6 and 3.7.
Cron-esque, in-script task scheduler with an incredibly easy to use English syntax. Booker can make calls (tasks) with specific intervals, start times and end times.
booker.do(mycallable, 'every 7 days at 12:00 until 01-30-2020')
Table of contents
- Basic usage
- Using the function decorator
- Using the do() method
- Task labels
- Cancelling tasks
- Why?
- Q&A
Tests
Please inspect and run tests.py yourself. All tests are passing. You will need the (awesome) freezegun library to run the tests.
Why?
There are other job scheduling libraries out there that work well, such as schedule or the more feature-packed APScheduler. However, as far as I know, there are none that employ the English language as the syntax for constructing schedules. Maybe there's a good reason for that. ¯\_(ツ)_/¯
Again, why!
- It's cool.
- Forgetting how to use it is nearly impossible.
- More reasons.
Basic usage
$ pip install booker
The keywords every
, in
, at
and until
are explained below. They
work how you might expect them to work.
import booker
import time
def myfunc():
print('Task has been run')
# Runs daily at noon until a long, long time from now.
booker.do(myfunc, 'every 1 day at 12:00 until 01-30-2030 12:00')
# Runs at 2PM. If the current time is past 2PM, it will run tomorrow at 2PM.
booker.do(myfunc, 'at 14:00')
# Runs every 15 minutes, starting an hour from now, indefinitely.
booker.do(myfunc, 'every 15 minutes in 1 hour')
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
sys.exit(0)
Using the function decorator
Tasks that do not repeat
Starting immediately
This is identical to just calling the method yourself.
@booker.task()
def myfunc():
print('Hello')
Starting at
a specific time
Use the at
keyword for this. This keyword expects time to be in 24 hour format (HH:MM). Seconds are not given.
@booker.task('at 14:00')
def myfunc():
print('It is 2PM.')
Starting in
5 minutes
Use the in
keyword for this. The task below runs 3 days and 5 minutes from now.
@booker.task('in 3 days 5 minutes')
def myfunc():
print('Hello')
Combining at
and in
You can combine at
and in
to define a task that, at
a specific time,
will do something in
a certain amount of time. It does not matter what
order you put these phrases in.
@booker.task('in 30 minutes at 12:00')
def myfunc():
print('It is 12:30')
Tasks that repeat with the every
keyword
When you use the every
keyword, booker looks for additional keywords,
prefixed by a number. Those keywords are day[s]
, hour[s]
or hr[s]
,
minute[s]
, and second[s]
and they should be following a number, e.g.:
7 days, 1 hour, 30 minutes, 1 second
The commas in the syntax above aren't necessary, but you can throw them in there. They have no effect.
Starting now
Like the commas above, the and
below is not required, but you can add
it for readability. Booker will ignore what it does not understand.
@booker.task('every 3 days and 12 hours')
@booker.task('every 15 minutes 15 seconds')
Starting at
a specific time
You can combine every
with at
.
@booker.task('every 12 hours at 12:00')
@booker.task('at 16:30, do this every 30 minutes')
Starting in
a while
Using every
with in
, you can define a task that runs
in in a certain amount of time after the first epoch.
@booker.task('every 1 day in 3 hours')
@booker.task('in 30 minutes, do this every 5 seconds')
Combining every
with at
and in
.
Combining all of the above, the task below runs daily at 12:30. It does not matter what order you put these phrases in.
@booker.task('in 30 minutes at 12:00 every 1 day')
Running until
a specific date and time
Using the until
keyword, you can tell booker when to end
a task.
You need to provide the month, day, year, hour, and minute that
you want the task to end in MM-DD-YYYY HH:MM
format.
The task below would run at noon, every week, until 6PM on January 2nd of the year 2020. Unless you had a power outage before then, or something.
@booker.task('every 7 days at 12:00 until 01-02-2020 18:00')
Using the do()
method
You can use the booker.do()
method to register a task with booker just as you
would do with the function decorator.
def myfunc(): ...
booker.do(myfunc, 'every 1 day starting at 12:00')
If you build an booker.Schedule
using booker.get_schedule()
, you can pass
that Schedule object to booker.do()
. The example below is indentical to the
example above.
def myfunc(): ...
schedule = booker.get_schedule('every 1 day starting at 12:00')
booker.do(myfunc, schedule)
Task labels
You can assign labels to tasks. Giving a task a label means that you can cancel it at any time.
Assigning labels with the decorator
@booker.task('every 5 seconds', 'my-label')
def myfunc(): ...
Assigning labels using do()
def myfunc(): ...
booker.do(myfunc, 'every 5 seconds', 'my-label')
Cancelling tasks
By label
booker.cancel('my-label') # cancel all tasks with the label 'my-label'
All
booker.cancel_all()
Q&A
Q: What exactly is a task?
A: A threading.Timer
object.
Q: Will booker block my main thread?
A: No. Booker doesn't have its own thread, or control loop,
or anything like that. It just spawns threading.Timer
objects
for you when you create a task. It's up to you to keep your program
alive throughout the duration of task execution (unless you set
booker.daemonize
to False
). Usually you would do this with a
typical while True: time.sleep(1)
loop.
The example below would quit immediately
and the task that was defined would never run because, by default,
booker daemonizes all of its tasks. You can disable this by setting
booker.daemonize
to False
.
# This example quits immediately
import booker
def myfunc():
...
booker.do(myfunc, 'in 10 seconds')
This example, on the other hand, would block your main thread and prevent the program from exiting until the task has finished.
# This example quits after 10 seconds has passed
import booker
booker.daemonize = False
def myfunc():
...
booker.do(myfunc, 'in 10 seconds')
Q: It is 6PM. What happens with this task?
booker.do(myfunc, 'at 12:00')
A: It will run, once, tomorrow at noon.
Q: I have a string: 2 hours, 59 minutes, 40 seconds
, and I want to run a task 5 minutes after that. How?
A: Use booker.get_schedule
to get an booker.Schedule
and add 300 seconds to its tts
(time-to-start) property.
schedule = booker.get_schedule('in 2 hours, 59 minutes, 40 seconds')
schedule.tts = schedule.tts + 300
booker.do(myfunc, schedule)
Q: How do I pass arguments to a task?
A: Use a lambda.
def myfunc(myarg):
print('Hello, {}'.format(myarg))
booker.do(lambda: myfunc('world!'), 'in 5 seconds')
Q: Can I view the status of a task?
A: From example.py
:
for task in booker.tasks():
print(task)
output:
RepeatingTask: [__main__.pong] [interval: 1s] [tts: 2s] [running in: 0.00s] [until: indefinitely] [label: my-pong-task]
SingleTask: [__main__.print_task_status] [tts: 3s] [finished 0.00s ago]
SingleTask: [__main__.<lambda>] [tts: 12s] [running in: 9.00s]
SingleTask: [__main__.<lambda>] [tts: 19s] [running in: 16.00s]
SingleTask: [booker.cancel_all] [tts: 20s] [running in: 17.00s]
RepeatingTask: [__main__.print_time] [interval: 1s] [tts: 0s] [running in: 0.00s] [until: indefinitely]
RepeatingTask: [__main__.never_run] [interval: 1s] [tts: 5s] [until: 3s has passed]
SingleTask: [__main__.<lambda>] [tts: 7s] [running in: 4.00s]
RepeatingTask: [__main__.ping] [interval: 1s] [tts: 0s] [running in: 0.00s] [until: indefinitely] [label: my-ping-task]
SingleTask: [__main__.in_five_seconds] [tts: 5s] [running in: 2.00s]
SingleTask: [__main__.in_ten_seconds] [tts: 10s] [running in: 7.00s]
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file booker-1.0.1.tar.gz
.
File metadata
- Download URL: booker-1.0.1.tar.gz
- Upload date:
- Size: 13.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/1.12.1 pkginfo/1.4.2 requests/2.21.0 setuptools/40.6.3 requests-toolbelt/0.8.0 tqdm/4.28.1 CPython/3.7.2
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | b6bac891f46757c98a77fe1f4fa4552cccfb88e9ed418d14b8b52bebbd5a998b |
|
MD5 | aa8225daa0388264f704baa11041bafe |
|
BLAKE2b-256 | ab9cf5907065a0f6438aabfa125f2f6f5aac1af7c493643788791e63d950e9ec |
File details
Details for the file booker-1.0.1-py2.py3-none-any.whl
.
File metadata
- Download URL: booker-1.0.1-py2.py3-none-any.whl
- Upload date:
- Size: 10.7 kB
- Tags: Python 2, Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/1.12.1 pkginfo/1.4.2 requests/2.21.0 setuptools/40.6.3 requests-toolbelt/0.8.0 tqdm/4.28.1 CPython/3.7.2
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | c3be147a18b9129a4ce975728ca16b85c6edb85ec7cc590408e068f96094e4bb |
|
MD5 | e91aef94d856910ea28e1e86cb505dd9 |
|
BLAKE2b-256 | 7c4272e79c22c42c8f3203eddaeec566a470ac794d6969f7e0b59aac22fa7917 |