Skip to content

Instantly share code, notes, and snippets.

@ernstki
Last active February 5, 2026 05:15
Show Gist options
  • Select an option

  • Save ernstki/759383313185aae343bc87b989c44241 to your computer and use it in GitHub Desktop.

Select an option

Save ernstki/759383313185aae343bc87b989c44241 to your computer and use it in GitHub Desktop.
Very, very simple watchdog-based Python interpreter launcher; watches your script for changes and re-runs it
#!/usr/bin/env python3
##
## watchme.py - monitor *.py files or a given path and re-run 'python <path>'
##
## Author: Kevin Ernst
## Date: 4 February 2026
## License: Apache License Version 2.0
## (https://github.com/gorakhargosh/watchdog/blob/master/LICENSE)
## Homepage: https://gist.github.com/ernstki/759383313185aae343bc87b989c44241
##
## sources:
## - https://github.com/gorakhargosh/watchdog#example-api-usage
## - https://github.com/yejianye/watchdog-tricks/blob/master/watchdog_tricks/compiler.py#L32
##
import os, sys, glob, time, subprocess
from functools import wraps
from watchdog.events import FileSystemEvent, FileSystemEventHandler
from watchdog.observers import Observer
def trace_event(func):
"""
source: https://github.com/yejianye/watchdog-tricks/blob/master/watchdog_tricks/utils.py
license: see https://github.com/yejianye/watchdog-tricks/pull/1#issuecomment-3850884907
"""
@wraps(func)
def _traced_func(self, event):
if hasattr(event, 'dest_path'):
print('%s: %s -> %s' % (event.event_type, event.src_path,
event.dest_path), file=sys.stderr)
else:
print('%s %s' % (event.event_type, event.src_path),
file=sys.stderr)
return func(self, event)
return _traced_func
class SavedFileHandler(FileSystemEventHandler):
def __init__(self, *args, **kwargs):
self.watchpats = []
self.watched = []
if 'watchpats' in kwargs:
for watch in kwargs.get('watchpats'):
self.watchpats.append(watch)
del kwargs['watchpats']
if not self.watchpats:
self.watchpats = ["*.py"]
print("Watching for changes to: {}".format(", ".join(self.watchpats)),
file=sys.stderr)
for pat in self.watchpats: # expand all globs now
self.watched += glob.glob(pat)
# and make all paths absolute, but ignore this script itself
self.watched = set([
os.path.abspath(p) for p in self.watched
if os.path.abspath(p) != os.path.abspath(__file__)
])
super().__init__(*args, **kwargs)
@trace_event
def on_moved(self, e): # many/most? editors save with delete, then move
if os.path.dirname(e.src_path) == os.path.dirname(e.dest_path) \
and os.path.abspath(e.dest_path) in self.watched:
subprocess.run("python " + e.dest_path)
save_handler = SavedFileHandler(watchpats=sys.argv[1:])
observer = Observer()
observer.schedule(save_handler, ".", recursive=True)
with observer:
try:
while True:
time.sleep(20)
except KeyboardInterrupt:
print("Received interrupt, quitting...", file=sys.stderr)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment