Skip to content
Lebret edited this page Aug 6, 2020 · 17 revisions

Quick start

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

Short Tutorial

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 four popular patterns:

  1. 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 on_loop() in daemon.py).
  2. Monitoring periodically by constructing your Notifier with a timeout value 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 timeout value is set too small.
  3. Spawning a new thread for monitoring events independently, this method instantiates ThreadedNotifier.
  4. 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.

Common Code

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 instantiating Notifier's class and the one using ThreadedNotifier

1. Using the Notifier class without timeout

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 c-c (sigint)

      notifier.loop()

2. Using the Notifier class with a timeout

Alternatively, if you wish to check for events periodically instead of blocking, construct your Notifier with a short timeout value:

      notifier = pyinotify.Notifier(wm, handler, 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.

3. Using the ThreadedNotifier Class

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

      wm.rm_watch(wdd['/tmp'], rec=True)

Or even better like this

      wm.rm_watch(wdd.values())

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

      notifier.stop()

4. Using the AsyncNotifier Class

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()

This class AsyncNotifier relies on Python standard module asyncore and is a possible alternative for polling events.

Testing it

You can download these two previous examples here tutorial_notifier.py and tutorial_threadednotifier.py

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