python pyinotify to monitor the specified suffix files in a dir

3.2k views Asked by At

I want to monitor a dir , and the dir has sub dirs and in subdir there are somes files with .md. (maybe there are some other files, such as *.swp...)

I only want to monitor the .md files, I have read the doc, and there is only a ExcludeFilter, and in the issue : https://github.com/seb-m/pyinotify/issues/31 says, only dir can be filter but not files.

Now what I do is to filter in process_* functions to check the event.name by fnmatch.

So if I only want to monitor the specified suffix files, is there a better way? Thanks.

This is the main code I have written:

!/usr/bin/env python                                                                                                                                
# -*- coding: utf-8 -*-

import pyinotify                                                                    
import fnmatch                                                                      

def suffix_filter(fn):                                                              
    suffixes = ["*.md", "*.markdown"]                                                                                                                
    for suffix in suffixes:                                                         
        if fnmatch.fnmatch(fn, suffix):                                             
            return False                                                            
    return True                                                                     

class EventHandler(pyinotify.ProcessEvent):                                         
    def process_IN_CREATE(self, event):                                             
        if not suffix_filter(event.name):                                           
            print "Creating:", event.pathname                                       

    def process_IN_DELETE(self, event):                                             
        if not suffix_filter(event.name):                                           
            print "Removing:", event.pathname                                       

    def process_IN_MODIFY(self, event):                                             
        if not suffix_filter(event.name):                                           
            print "Modifing:", event.pathname                                       

    def process_default(self, event):                                               
        print "Default:", event.pathname
3

There are 3 answers

3
martineau On BEST ANSWER

I think you basically have the right idea, but that it could be implemented more easily.

The ProcessEvent class in the pyinotify module already has a hook you can use to filter the processing of events. It's specified via an optional pevent keyword argument given on the call to the constructor and is saved in the instance's self.pevent attribute. The default value is None. It's value is used in the class' __call__() method as shown in the following snippet from the pyinotify.py source file:

def __call__(self, event):
    stop_chaining = False
    if self.pevent is not None:
        # By default methods return None so we set as guideline
        # that methods asking for stop chaining must explicitly
        # return non None or non False values, otherwise the default
        # behavior will be to accept chain call to the corresponding
        # local method.
        stop_chaining = self.pevent(event)
    if not stop_chaining:
        return _ProcessEvent.__call__(self, event)

So you could use it only allow events for files with certain suffixes (aka extensions) with something like this:

SUFFIXES = {".md", ".markdown"}

def suffix_filter(event):
    # return True to stop processing of event (to "stop chaining")
    return os.path.splitext(event.name)[1] not in SUFFIXES

processevent = ProcessEvent(pevent=suffix_filter)
1
Nick Bastin On

There's nothing particularly wrong with your solution, but you want your inotify handler to be as fast as possible, so there are a few optimizations you can make.

You should move your match suffixes out of your function, so the compiler only builds them once:

EXTS = set([".md", ".markdown"])

I made them a set so you can do a more efficient match:

def suffix_filter(fn):
  ext = os.path.splitext(fn)[1]
  if ext in EXTS:
    return False
  return True

I'm only presuming that os.path.splitext and a set search are faster than an iterative fnmatch, but this may not be true for your really small list of extensions - you should test it.

(Note: I've mirrored your code above where you return False when you make a match, but I'm not convinced that's what you want - it is at the very least not very clear to someone reading your code)

0
Paulo Almeida On

You can use the __call__ method of ProcessEvent to centralize the call to suffix_filter:

class EventHandler(pyinotify.ProcessEvent):
    def __call__(self, event):
        if not suffix_filter(event.name):
            super(EventHandler, self).__call__(event)

    def process_IN_CREATE(self, event):
        print "Creating:", event.pathname

    def process_IN_DELETE(self, event):
        print "Removing:", event.pathname

    def process_IN_MODIFY(self, event):
        print "Modifying:", event.pathname