Restarting Python SimpleHTTPServer on file system changes

I have a little static site generator that I made for my own uses. When I'm ready with my changes, I run a test command that uses Python's SimpleHTTPServer to serve the built, public folder so I can make sure it all looks good before I rsync it to my remote server.

I wanted to run some custom build code every time one of the files changed, and I wanted that hidden behind a single command. Using watchdog seemed like an easy enough solution, but running the server was an issue, since the SimpleHTTPServer included with python will block the main thread as soon as you start it. Thankfully, you can just toss it in its own process and go about your day. I ended up with something like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
def dev_command():
    from watchdog.observers import Observer
    from watchdog.events import FileSystemEventHandler

    from multiprocessing import Process
    from http.server import HTTPServer, SimpleHTTPRequestHandler

    def create_server():
        httpd = HTTPServer(('', 8000), SimpleHTTPRequestHandler)
        httpd.serve_forever()

    class Rebuilder(FileSystemEventHandler):

        p = None

        def stop_server(self):
            if self.p:
                print("Stopping server")
                self.p.terminate()

        def rebuild(self):
            # run all your rebuild commands

        def start_server(self):
            if self.p:
                self.stop_server()
            print("Starting Server")
            self.p = Process(target=create_server)
            self.p.start()

        def on_modified(self, event):
            if self.should_rebuild(event):
                print("Rebuilding")
                self.rebuild()

        def should_rebuild(self, event):
            """Include code to make sure you only rebuild on events you care about. Or
               you could implement watchdog's RegexMatchingEventHandler"""
            return True

    rebuilder = Rebuilder()
    rebuilder.start_server()

    observer = Observer()
    observer.schedule(rebuilder, "/path/to/watch/", recursive=True)
    observer.start()

    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        observer.stop()
    observer.join()

2021-01-10