Package: booker
BookerTested 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 contentsBasic usageUsing the function decoratorTasks that do not repeatStarting immediatelyStarting at a specific timeStarting in 5 minutesCombining at and inTasks that repeat with the every keywordStarting nowStarting at a specific timeStarting in a whileCombining every with at and in.Running until a specific date and timeUsing the do() methodTask labelsAssigning labels with the decoratorAssigning labels using do()Cancelling tasksAllBy labelWhy?Q&ATestsPlease inspect and run tests.py yourself. All tests are passing. You
will need the (awesome)freezegunlibrary
to run the tests.Why?There are other job scheduling libraries out there that work well,
such asscheduleor the
more feature-packedAPScheduler.
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$pipinstallbookerThe keywordsevery,in,atanduntilare explained below. They
work how you might expect them to work.importbookerimporttimedefmyfunc():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:whileTrue:time.sleep(1)exceptKeyboardInterrupt:sys.exit(0)Using the function decoratorTasks that do not repeatStarting immediatelyThis is identical to just calling the method yourself.@booker.task()defmyfunc():print('Hello')Startingata specific timeUse theatkeyword for this. This keyword expects time to be in 24 hour format (HH:MM). Seconds are not given.@booker.task('at 14:00')defmyfunc():print('It is 2PM.')Startingin5 minutesUse theinkeyword for this. The task below runs 3 days and 5 minutes from now.@booker.task('in 3 days 5 minutes')defmyfunc():print('Hello')CombiningatandinYou can combineatandinto define a task that,ata specific time,
will do somethingina certain amount of time. It does not matter what
order you put these phrases in.@booker.task('in 30 minutes at 12:00')defmyfunc():print('It is 12:30')Tasks that repeat with theeverykeywordWhen you use theeverykeyword, booker looks for additional keywords,
prefixed by a number. Those keywords areday[s],hour[s]orhr[s],minute[s], andsecond[s]and they should be following a number, e.g.:7 days, 1 hour, 30 minutes, 1 secondThe commas in the syntax above aren't necessary, but you can throw them
in there. They have no effect.Starting nowLike the commas above, theandbelow 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')Startingata specific timeYou can combineeverywithat.@booker.task('every 12 hours at 12:00')@booker.task('at 16:30, do this every 30 minutes')Startingina whileUsingeverywithin, you can define a task that runsinin 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')Combiningeverywithatandin.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')Runninguntila specific date and timeUsing theuntilkeyword, 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 inMM-DD-YYYY HH:MMformat.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 thedo()methodYou can use thebooker.do()method to register a task with booker just as you
would do with the function decorator.defmyfunc():...booker.do(myfunc,'every 1 day starting at 12:00')If you build anbooker.Scheduleusingbooker.get_schedule(), you can pass
that Schedule object tobooker.do(). The example below is indentical to the
example above.defmyfunc():...schedule=booker.get_schedule('every 1 day starting at 12:00')booker.do(myfunc,schedule)Task labelsYou 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')defmyfunc():...Assigning labels usingdo()defmyfunc():...booker.do(myfunc,'every 5 seconds','my-label')Cancelling tasksBy labelbooker.cancel('my-label')# cancel all tasks with the label 'my-label'Allbooker.cancel_all()Q&AQ: What exactly is a task?A: Athreading.Timerobject.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 spawnsthreading.Timerobjects
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 setbooker.daemonizetoFalse). Usually you would do this with a
typicalwhile 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 settingbooker.daemonizetoFalse.# This example quits immediatelyimportbookerdefmyfunc():...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 passedimportbookerbooker.daemonize=Falsedefmyfunc():...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: Usebooker.get_scheduleto get anbooker.Scheduleand add 300 seconds to itstts(time-to-start) property.schedule=booker.get_schedule('in 2 hours, 59 minutes, 40 seconds')schedule.tts=schedule.tts+300booker.do(myfunc,schedule)Q: How do I pass arguments to a task?A: Use a lambda.defmyfunc(myarg):print('Hello,{}'.format(myarg))booker.do(lambda:myfunc('world!'),'in 5 seconds')Q: Can I view the status of a task?A: Fromexample.py:fortaskinbooker.tasks():print(task)output:RepeatingTask:[__main__.pong][interval:1s][tts:2s][runningin:0.00s][until:indefinitely][label:my-pong-task]SingleTask:[__main__.print_task_status][tts:3s][finished0.00sago]SingleTask:[__main__.<lambda>][tts:12s][runningin:9.00s]SingleTask:[__main__.<lambda>][tts:19s][runningin:16.00s]SingleTask:[booker.cancel_all][tts:20s][runningin:17.00s]RepeatingTask:[__main__.print_time][interval:1s][tts:0s][runningin:0.00s][until:indefinitely]RepeatingTask:[__main__.never_run][interval:1s][tts:5s][until:3shaspassed]SingleTask:[__main__.<lambda>][tts:7s][runningin:4.00s]RepeatingTask:[__main__.ping][interval:1s][tts:0s][runningin:0.00s][until:indefinitely][label:my-ping-task]SingleTask:[__main__.in_five_seconds][tts:5s][runningin:2.00s]SingleTask:[__main__.in_ten_seconds][tts:10s][runningin:7.00s]