Quickstart

Quickstart#

Below we present a simple example that monitors the current directory recursively (which means, it will traverse any sub-directories) to detect changes. Here is what we will do with the API:

  1. Create an instance of the watchdog.observers.Observer thread class.

  2. Implement a subclass of watchdog.events.FileSystemEventHandler.

  3. Schedule monitoring a few paths with the observer instance attaching the event handler.

  4. Start the observer thread and wait for it generate events without blocking our main thread.

By default, an watchdog.observers.Observer instance will not monitor sub-directories. By passing recursive=True in the call to schedule() monitoring entire directory trees is ensured.

A Simple Example#

The following example program will monitor the current directory recursively for file system changes and simply print them to the console:

 1from __future__ import annotations
 2
 3import logging
 4import sys
 5import time
 6
 7from watchdog import events
 8from watchdog.observers import Observer
 9
10logging.basicConfig(level=logging.DEBUG)
11
12
13class MyEventHandler(events.FileSystemEventHandler):
14    def on_any_event(self, event: events.FileSystemEvent) -> None:
15        logging.info("Any event: %s", event)
16
17    def on_created(self, event: events.DirCreatedEvent | events.FileCreatedEvent) -> None:
18        logging.info("Created: %s", event)
19
20    def on_deleted(self, event: events.DirDeletedEvent | events.FileDeletedEvent) -> None:
21        logging.info("Deleted: %s", event)
22
23    def on_modified(self, event: events.DirModifiedEvent | events.FileModifiedEvent) -> None:
24        logging.info("Modified: %s", event)
25
26    def on_moved(self, event: events.DirMovedEvent | events.FileMovedEvent) -> None:
27        logging.info("Moved: %s", event)
28
29    def on_opened(self, event: events.FileOpenedEvent) -> None:
30        logging.info("Opened: %s", event)
31
32    def on_closed(self, event: events.FileClosedEvent) -> None:
33        logging.info("Closed: %s", event)
34
35    def on_closed_no_write(self, event: events.FileClosedNoWriteEvent) -> None:
36        logging.info("Closed no write: %s", event)
37
38
39path = sys.argv[1] if len(sys.argv) > 1 else "."
40
41event_handler = MyEventHandler()
42observer = Observer()
43observer.schedule(event_handler, path, recursive=True)
44observer.start()
45try:
46    while True:
47        time.sleep(1)
48finally:
49    observer.stop()
50    observer.join()

To stop the program, press Control-C.

Alternatively, you can use the observer as a context manager for cleaner code:

 1 import time
 2
 3 from watchdog.events import FileSystemEvent, FileSystemEventHandler
 4 from watchdog.observers import Observer
 5
 6
 7 class MyEventHandler(FileSystemEventHandler):
 8     def on_any_event(self, event: FileSystemEvent) -> None:
 9         print(event)
10
11
12 event_handler = MyEventHandler()
13 observer = Observer()
14 observer.schedule(event_handler, ".", recursive=True)
15
16 with observer:
17     while True:
18         time.sleep(1)

The context manager automatically handles starting and stopping the observer, ensuring proper cleanup even if an exception occurs.

Typing#

If you are using type annotations it is important to note that watchdog.observers.Observer is not actually a class; it is a variable that hold the “best” observer class available on your platform.

In order to correctly type your own code your should use watchdog.observers.api.BaseObserver. For example:

 1 from watchdog.observers import Observer
 2 from watchdog.observers.api import BaseObserver
 3
 4
 5 def my_func(obs: BaseObserver) -> None:
 6     # Do something with obs
 7     pass
 8
 9 observer: BaseObserver = Observer()
10 my_func(observer)