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.