I used file watching loops for almost my entire career (professionally and personally). It’s always been a for loop or os.walk or similar (C/C++/Python/SBCL/…), and it was always lots of boilerplate code. Not difficult, just a bunch of lines to maintain and make sure they catch all corner cases.
While setting up this blogging pipeline (thank goodness I can just concentrate on the typing part and deployment happens automagically) I got to learn about gorakhargosh’s watchdog Python library. You should totally check out their GitHub page for the project! It’s a bit dated, but does the job perfectly fine!
Basically, all you do is:
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler, FileSystemEvent
WATCH_THIS_PATH = "/home/me/Documents"
class FileChangeHandler(FileSystemEventHandler):
def __init__(self, path):
super().__init__()
self.path = path
def on_any_event(self, event: FileSystemEvent):
if event.is_directory:
return
rel_path = Path(event.src_path).relative_to(VAULT_PATH)
timestamp = datetime.now(TIMEZONE).strftime("%H:%M:%S")
match event.event_type:
case "created":
print(f"[{timestamp}] + {rel_path}")
case "modified":
print(f"[{timestamp}] ~ {rel_path}")
case "deleted":
print(f"[{timestamp}] - {rel_path}")
case "moved":
rel_dest = Path(event.dest_path).relative_to(VAULT_PATH)
print(f"[{timestamp}] → {rel_path} -> {rel_dest}")
case _:
return
handler = FileChangeHandler()
observer = Observer()
observer.schedule(handler, str(WATCH_THIS_PATH), recursive=True)
observer.start()
try:
while True:
now = datetime.now(TIMEZONE)
seconds_until_midnight = (
(24 - now.hour - 1) * 3600
+ (60 - now.minute - 1) * 60
+ (60 - now.second)
)
time.sleep(min(seconds_until_midnight, 60))
except KeyboardInterrupt:
pass
observer.stop()
observer.join()
This gives you a basic scaffolding for watching a folder and reacting to any file changes (and even by what happened to the file exactly - created, modified, deleted, moved).
This is an amazingly handy library. Self-contained, and it gets the job done, cleanly.