```bash python -m pip install schedule ``` see [here at pypi](https://pypi.org/project/schedule/) and [here at readthedocs](https://schedule.readthedocs.io/en/stable/). Summary ```python 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. ```python #!/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() ```