The EventsCodes Class
| Event Name | Is an Event | Description |
| IN_ACCESS | Yes | file was accessed. |
| IN_ATTRIB | Yes | metadata changed. |
| IN_CLOSE_NOWRITE | Yes | unwritable file was closed. |
| IN_CLOSE_WRITE | Yes | writable file was closed. |
| IN_CREATE | Yes | file/dir was created in watched directory. |
| IN_DELETE | Yes | file/dir was deleted in watched directory. |
| IN_DELETE_SELF | Yes | watched item itself was deleted. |
| IN_DONT_FOLLOW | No | don't follow a symlink (lk 2.6.15). |
| IN_IGNORED | Yes | raised when a watch is removed. Probably useless for you, prefer instead IN_DELETE*. |
| IN_ISDIR | No | event occurred against a directory. It is always piggybacked with an event. The Event structure automatically provides this information via attribute event.is_dir |
| IN_MASK_ADD | No | to update a mask without overwriting the previous value (lk 2.6.14). Useful when updating a watch. |
| IN_MODIFY | Yes | file was modified. |
| IN_MOVE_SELF | Yes | watched item itself was moved, currently its full pathname destination can only be known if its source directory and destination directory are both watched. Otherwise, the file is still being watched but you cannot rely anymore on the given path attribute event.path |
| IN_MOVED_FROM | Yes | file/dir in a watched dir was moved from X. Can trace the full move of an item when IN_MOVED_TO is available too, in this case if the moved item is itself watched, its path will be updated (see IN_MOVE_SELF). |
| IN_MOVED_TO | Yes | file/dir was moved to Y in a watched dir (see IN_MOVE_FROM). |
| IN_ONLYDIR | No | only watch the path if it is a directory (lk 2.6.15). Usable when calling watch_manager.add_watch(). |
| IN_OPEN | Yes | file was opened. |
| IN_Q_OVERFLOW | Yes | event's queue overflown. This event doesn't is not associated with a watch. |
| IN_UNMOUNT | Yes | backing fs was unmounted. Notified to each watch of this fs. |
Subclassing ProcessEvent
We can subclass ProcessEvent and provide our own event handler by defining appropriate methods. See the commented example below:
import pyinotify class MyEventHandler(pyinotify.ProcessEvent): def my_init(self, file_object=sys.stdout): """ This is your constructor it is automatically called from ProcessEvent.__init__(), And extra arguments passed to __init__() would be delegated automatically to my_init(). """ self._file_object = file_object def process_IN_DELETE(event): """ This method processes a specific type of event: IN_DELETE. event is an instance of Event. """ self._file_object.write('deleting: %s\n' event.pathname) def process_IN_CLOSE(event): """ This method is called on these events: IN_CLOSE_WRITE and IN_CLOSE_NOWRITE. """ self._file_object.write('closing: %s\n' event.pathname) def process_default(event): """ Eventually, this method is called for all others types of events. This method can be useful when an action fits all events. """ self._file_object.write('default processing\n') # A way to instantiate this class could be: p = MyEventHandler(file('/tmp/output', 'w'))
Explanations and details:
- IN_DELETE has a specific method. An individual processing method is provided by a method whose name is written with specific syntax: process_EVENT_NAME where EVENT_NAME is the name of the handled event to process.
- There are related events which need most of the time the same treatment. It would be annoying to have to implement multiple times the same code. In those cases common methods can be defined. For example for those two following related events:
mask = pyinotify.IN_CLOSE_WRITE | pyinotify.IN_CLOSE_NOWRITE
There is a method named process_IN_CLOSE according to syntax pattern process_IN_FAMILYBASENAME. In this case, the two previous events would be processed by this method. Beware not to also implement process_IN_CLOSE_WRITE or process_IN_CLOSE_NOWRITE, because these methods would have an higher precedence (see below), and would be called instead of process_IN_CLOSE.
- ALL_EVENTS isn't an event by itself, it is the aggregation of all events codes, that means that you can't implement the method process_ALL_EVENTS.
- If we need to apply the same actions whatever the kind of event or if we need to have a fall back method for unexpected events, we just need to implement process_default method.
- Processing methods lookup's order (by increasing order of priority): specialized methods (eg: process_IN_CLOSE_WRITE) first, then family methods (eg: process_IN_CLOSE), finally default method (process_default).
- One more thing: say you redefine the method process_default which contains the instruction os.ismount(my-mount-point), it would be for example a mistake having this method called for every event IN_OPEN occurred in /etc. Because, one particularity of os.ismount is to check in /etc/mtab if the partition is mounted, this would lead to an endless situation: call process_IN_OPEN, open /etc/mtab, call process_IN_OPEN, open /etc/mtab ... looping forever.
Whenever possible you should process your notifications this way:
notifier = pyinotify.Notifier(wm, MyEventHandler())
But, some watches may need a different kind of processing, you can attach them a specialized instance which will be called only on their associated watch:
wm.add_watch('/one/path', pyinotify.ALL_EVENTS, proc_fun=MyEventHandler())
Operations on WatchManager: add_watch, update_watch, rm_watch
The following table details accepted parameters and returned results of these methods. Note that a return value is always returned when quiet=True, otherwise if quiet=False an exception can be triggered if an error happens.
| Method | Parameter | Returned result | Example |
| add_watch | path (or list of paths) | {path1: wd1, path2: wd2, ...} where wdx is the watch descriptor associated to pathx, and is positive on success. | ra = notifier.add_watch('/a-dir', mask); if ra['/a-dir'] > 0: print "added" |
| update_watch | wd (or list of wds) | {wd1: success, wd2: success, ...} where success is True if the op on wdx succeeded, False otherwise. | ru = notifier.update_watch(ra['/a-dir'], new_mask); if ru['/a-dir']: print "updated" |
| rm_watch | wd (or list of wds) | {wd1: success, wd2: success, ...} where success is True if the op on wdx succeeded, False otherwise. | rr = notifier.rm_watch(ra['/a-dir']); if rr['/a-dir']: print "deleted" |
Update and remove methods take watch descriptors as arguments and return a dictionary notifying the success or failure of the operations.
In extreme case if your parameter doesn't fit the expected format, which could happen if you lost returned values of previous operations, you still can use method get_wd(a_path) and its counterpart get_path(a_wd) from WatchManager. The former method takes a path and returns its associated wd, the latter takes a wd and returns its path. Caution: in worst case get_wd(a_path) will have to iterate the whole list of watches, thus the cost can be high.
Logging messages
pyinotify use the logging module of the standard library, you can access it through 'pyinotify.log' attibute and for example set its level to log.setLevel(level) (by default the level is set to 20 when pyinotify is imported and is set to 10 when it is executed from command line.
import pyinotify # [...] # Modify logging level pyinotify.log.setLevel(10) # output debug message pyinotify.log.debug('debug msg2')
