Search code examples
pythonfilewatchpython-watchdog

How do I watch a file, not a directory for changes using Python?


The question: How do I watch a file for changes using Python? suggests using watchdog, but I found it was only able to watch a directory, not a file. watchdog-test.py is watchdog's sample script:

$ python watchdog-test.py ab_test_res.sh &
[1] 30628
fbt@fbt64:~/laike9m$ Traceback (most recent call last):
  File "watchdog-test.py", line 15, in <module>
    observer.start()
  File "/usr/local/lib/python2.7/dist-packages/watchdog/observers/api.py", line 255, in start
    emitter.start()
  File "/usr/local/lib/python2.7/dist-packages/watchdog/utils/__init__.py", line 111, in start
    self.on_thread_start()
  File "/usr/local/lib/python2.7/dist-packages/watchdog/observers/inotify.py", line 121, in on_thread_start
    self._inotify = InotifyBuffer(path, self.watch.is_recursive)
  File "/usr/local/lib/python2.7/dist-packages/watchdog/observers/inotify_buffer.py", line 35, in __init__
    self._inotify = Inotify(path, recursive)
  File "/usr/local/lib/python2.7/dist-packages/watchdog/observers/inotify_c.py", line 187, in __init__
    self._add_dir_watch(path, recursive, event_mask)
  File "/usr/local/lib/python2.7/dist-packages/watchdog/observers/inotify_c.py", line 363, in _add_dir_watch
    raise OSError('Path is not a directory')
OSError: Path is not a directory

So what's the best solution? I'm using Linux(Ubuntu 12.04). BTW I don't want to use polling.


Solution

  • You can watch a file with watchdog by watching the directory that the file is in and only responding to change events that effect your file. Something like this would do it for you:

    from watchdog.observers import Observer
    from watchdog.events import FileSystemEventHandler
    
    class FileModifiedHandler(FileSystemEventHandler):
    
        def __init__(self, path, file_name, callback):
            self.file_name = file_name
            self.callback = callback
    
            # set observer to watch for changes in the directory
            self.observer = Observer()
            self.observer.schedule(self, path, recursive=False)
            self.observer.start()
            self.observer.join()
    
        def on_modified(self, event): 
            # only act on the change that we're looking for
            if not event.is_directory and event.src_path.endswith(self.file_name):
                self.observer.stop() # stop watching
                self.callback() # call callback
    
    
    from sys import argv, exit
    
    if __name__ == '__main__':
    
        if not len(argv) == 2:
            print("No file specified")
            exit(1)
    
        def callback():
            print("FILE WAS MODIFED")
    
        FileModifiedHandler('.', argv[1], callback)
    

    I was only able to test this on windows, but it should be os agnostic.