lang/python/ PythonSchedule


python -m pip install schedule

see here at pypi and here at readthedocs. Summary

import schedule
import time

def job():
    print("I'm working...")

schedule.every(10).seconds.do(job)
schedule.every(10).minutes.do(job)
schedule.every().hour.do(job)
schedule.every().day.at("10:30").do(job)
schedule.every(5).to(10).minutes.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)

aside

I wrote a very simple scheduling helper, named sleepuntil where you'd run sleepuntil 03:45; cmd args.... It doesn't do any of the actual scheduling, rather it computes the number of seconds until the desired time and sleeps that number of seconds. It is basically sleep but with a calculator to work out the seconds. It's very rough, since if I'm just writing for me, and the program is small, I don't care for 'software engineering best practices' — for me, the best way to manage code complexity is not to have it in the first place: this is around 100 lines all in, and most of that is code for parsing and printing times. At the time I wrote this, I wasn't aware of how f"{s:<50}", or f"{s^50}" worked, let alone f"{s:=^{w}}" or x="*";f"{s:{x}^{w}". See AdvancedFormatStrings.

#!/usr/bin/python

import sys, os
from datetime import datetime
from time import sleep

args = sys.argv[1:]

if len(args) < 1 or len(args) > 2:
  print("""sleepuntil time
where time has format 12:34""")
  exit(1)

def parse_time(x):
  a = x.split(":")
  if len(a) < 2 or len(a) > 3:
    raise ValueError("Time should be in the format hh:mm or hh:mm:ss")
  if len(a) == 2:
    h,m = map(int,a)
    s = 0
  else:
    h,m,s = map(int,a)
  return (h,m,s)

def format_secs(x):
  s = x % 60
  x //= 60
  m = x % 60
  x //= 60
  if m > 0 or x > 0:
    a = f"{s:02d}"
  else:
    return "{s}"
  if x > 0:
    a = f"{x}:{m:02d}:{a}"
  else:
    a = f"{m}:{a}"
  return a

def compute_secs_until(x):
  h,m,s = parse_time(x)
  now = datetime.now()
  h1 = now.hour
  m1 = now.minute
  s1 = now.second
  secs = 3600*h+60*m+s
  secs1 = 3600*h1+60*m1+s1
  dt = secs - secs1
  if dt < 0:
    dt += 24*3600
  return dt

def do_sleep():
  try:  
    secs = compute_secs_until(args[0])
  except Exception as e:
    print(f"Exception {e}")
    exit(1)
  if len(args) > 1:
    days = int(args[1])
    secs += 24*3600*days
  d = secs // (24*3600)
  h = (secs % (24*3600)) // 3600
  m = (secs % 3600) // 60
  s = (secs % 60)
  hms = []
  if d > 0:
    hms.append(f"{d} day")
    if d != 1:
      hms[-1] += "s"
  if h > 0:
    hms.append(f"{h} hour")
    if h != 1:
      hms[-1] += "s"
  if m > 0:
    hms.append(f"{m} minute")
    if m != 1:
      hms[-1] += "s"
  if s > 0:
    hms.append(f"{s} second")
    if s != 1:
      hms[-1] += "s"
  if len(hms) == 0:
    t = "0 seconds"
  elif len(hms) == 1:
    t = hms[0]
  else:
    hms1 = hms[:-1]
    hms2 = hms[-1]
    t = f"{', '.join(hms1)} and {hms2}"
  try:
    print(f"Sleeping {secs} seconds == {t}")
    for s in range(secs):
      cols = os.get_terminal_size().columns
      tr = "Time remaining: "
      a = f"{format_secs(secs-s)} == {secs-s} seconds"
      if len(a) >= cols:
        ostr = a
      elif len(a) + len(tr) + 1 >= cols:
        ostr = a + " " + "="*(cols - len(a) - 1)
      else:
        ostr = tr + a
        ostr = ostr + " " + "="*(cols - len(ostr) - 1)
      print(f"\r{ostr}",end="")
      sleep(1)
  except KeyboardInterrupt:
    print("\nKeyboard Interrupt\n")
    exit(1)

if __name__ == "__main__":
  do_sleep()