Clone this wiki locally
To familiarize yourself with pyinotify, run a first example like this from its local directory:
$ python pyinotify.py # By default watch /tmp for all events $ python pyinotify.py my-dir-to-watch # Watch the path my-dir-to-watch for all events
Or if you already have installed pyinotify you could use this command:
$ python -m pyinotify $ python -m pyinotify my-dir-to-watch
Obviously my-dir-to-watch must be a path leading to a valid directory. If you go into that directory, and take some actions on its content (read a file...) you should observe new events.
Type this command to visualize all the different options:
$ python pyinotify.py -h
Let's start a more detailed example. Say, we want to monitor the temp directory
/tmp and all its subdirectories for every new file creation or deletion. For sake of simplicity, we only print messages for every notification on standard output.
Now there are several strategies for reading the events and taking appropriate actions, among them there are three popular patterns:
- Monitoring endlessly with loop()'s method of Notifier, in this case the current process will be dedicated to this task, maybe interleaving each non-deterministic event loop with an unrelated callback method execution if needed (e.g. see method
- Monitoring periodically by constructing your
timeoutvalue and explicitly calling processing methods whenever it's convenient. In this case it does not block your current thread but it could also lead to a small loss of reactivity or an increased use of resources if
timeoutvalue is set too small.
- Spawning a new thread for monitoring events independently, this method instantiates ThreadedNotifier.
- Asynchronously monitoring via the AsyncNotifier class.
Which is the right strategy mainly depends on your needs and on your context, in the next sections we will briefly implement each ones of these strategies.
import pyinotify # The watch manager stores the watches and provides operations on watches wm = pyinotify.WatchManager() mask = pyinotify.IN_DELETE | pyinotify.IN_CREATE # watched events class EventHandler(pyinotify.ProcessEvent): def process_IN_CREATE(self, event): print "Creating:", event.pathname def process_IN_DELETE(self, event): print "Removing:", event.pathname
The EventHandler class inherits from a processing base class called ProcessEvent; it handles notifications and takes actions through specific processing methods. For an EVENT_TYPE, a process_EVENT_TYPE function will execute. See EventsCodes for more details.
Next, we describe respectively the code instanciating Notifier's class and the one using ThreadedNotifier
Notifier class without timeout1. Using the
The corresponding tutorial file can be found here: tutorial_notifier.py.
handler = EventHandler() notifier = pyinotify.Notifier(wm, handler) # Internally, 'handler' is a callable object which on new events will be called like this: handler(new_event)
The next statement adds a new watch on the first parameter and recursively on all its subdirectories (with
rec=True), note that symlinks are not followed. By default, the monitoring is limited to the level of the given directory. It returns a
dict where keys are paths and values are their corresponding watch descriptors (wd). An unique wd is attributed to every new watch. It is useful (and often necessary) to keep those wds for further updating or removing some of those watches.
wdd = wm.add_watch('/tmp', mask, rec=True)
With this last statement your program starts processing the events. The call to this method is blocking until we type
Notifier class with a timeout2. Using the
Alternatively, if you wish to check for events periodically instead of blocking, construct your Notifier with a short timeout value:
notifier = Notifier(wm, p, timeout=10)
And check for and process events with a function such as this:
def quick_check(notifier): assert notifier._timeout is not None, 'Notifier must be constructed with a short timeout' notifier.process_events() while notifier.check_events(): #loop in case more events appear while we are processing notifier.read_events() notifier.process_events()
This method is ideal for event-driven GUI applications (e.g. GTK). You can setup a timer event which calls
quick_check() every few hundred milliseconds. This primary advantage of this is that inotify's events are processed in the same thread as other GUI events thus avoiding the complexity of multi-threaded applications. On the downside, it risks over-polling if the timeout value is too small; you'll have to find the right balance between reactivity and resources overhead.
ThreadedNotifier Class3. Using the
The corresponding tutorial file can be found here: tutorial_threadednotifier.py.
notifier = pyinotify.ThreadedNotifier(wm, EventHandler()) # Start the notifier from a new thread, without doing anything as no directory or file are currently monitored yet. notifier.start() # Start watching a path wdd = wm.add_watch('/tmp', mask, rec=True)
At any moment we can for example remove the watch on
/tmp like that
if wdd['/tmp'] > 0: # test if the wd is valid, ie. if /tmp is being watched, this test is not mandatory though wm.rm_watch(wdd['/tmp'])
Note that its subdirectories (if any) are still being watched. If we wanted to remove
/tmp and all the watches on its subdirectories, we'd have to proceed like this
Or even better like this
This is it, most of the code is written, next, we can add, update or remove watches on files or directories with the same principles. The only remaining important task is to stop the thread when we want stop monitoring
AsyncNotifier Class4. Using the
The corresponding tutorial file can be found here: tutorial_asyncnotifier.py.
notifier = pyinotify.AsyncNotifier(wm, EventHandler()) wdd = wm.add_watch('/tmp', mask, rec=True) import asyncore asyncore.loop()
AsyncNotifier relies on Python standard module asyncore and is a possible alternative for polling events.
For the first example you can execute it in a console:
$ python tutorial_notifier.py
Then, in another console execute the following commands:
$ touch /tmp/foo && rm -f /tmp/foo
The first console should display now:
Creating: /tmp/foo Removing: /tmp/foo