2323
2424
2525class Watcher (object ):
26- """A file watcher registery ."""
26+ """A file watcher registry ."""
2727 def __init__ (self ):
2828 self ._tasks = {}
29- self ._mtimes = {}
29+
30+ # modification time of filepaths for each task,
31+ # before and after checking for changes
32+ self ._task_mtimes = {}
33+ self ._new_mtimes = {}
3034
3135 # setting changes
3236 self ._changes = []
@@ -65,6 +69,7 @@ def watch(self, path, func=None, delay=0, ignore=None):
6569 'func' : func ,
6670 'delay' : delay ,
6771 'ignore' : ignore ,
72+ 'mtimes' : {},
6873 }
6974
7075 def start (self , callback ):
@@ -73,7 +78,10 @@ def start(self, callback):
7378 return False
7479
7580 def examine (self ):
76- """Check if there are changes, if true, run the given task."""
81+ """Check if there are changes. If so, run the given task.
82+
83+ Returns a tuple of modified filepath and reload delay.
84+ """
7785 if self ._changes :
7886 return self ._changes .pop ()
7987
@@ -82,6 +90,7 @@ def examine(self):
8290 delays = set ()
8391 for path in self ._tasks :
8492 item = self ._tasks [path ]
93+ self ._task_mtimes = item ['mtimes' ]
8594 if self .is_changed (path , item ['ignore' ]):
8695 func = item ['func' ]
8796 delay = item ['delay' ]
@@ -102,13 +111,49 @@ def examine(self):
102111 return self .filepath , delay
103112
104113 def is_changed (self , path , ignore = None ):
114+ """Check if any filepaths have been added, modified, or removed.
115+
116+ Updates filepath modification times in self._task_mtimes.
117+ """
118+ self ._new_mtimes = {}
119+ changed = False
120+
105121 if os .path .isfile (path ):
106- return self .is_file_changed (path , ignore )
122+ changed = self .is_file_changed (path , ignore )
107123 elif os .path .isdir (path ):
108- return self .is_folder_changed (path , ignore )
109- return self .is_glob_changed (path , ignore )
124+ changed = self .is_folder_changed (path , ignore )
125+ else :
126+ changed = self .is_glob_changed (path , ignore )
127+
128+ if not changed :
129+ changed = self .is_file_removed ()
130+
131+ self ._task_mtimes .update (self ._new_mtimes )
132+ return changed
133+
134+ def is_file_removed (self ):
135+ """Check if any filepaths have been removed since last check.
136+
137+ Deletes removed paths from self._task_mtimes.
138+ Sets self.filepath to one of the removed paths.
139+ """
140+ removed_paths = set (self ._task_mtimes ) - set (self ._new_mtimes )
141+ if not removed_paths :
142+ return False
143+
144+ for path in removed_paths :
145+ self ._task_mtimes .pop (path )
146+ # self.filepath seems purely informational, so setting one
147+ # of several removed files seems sufficient
148+ self .filepath = path
149+ return True
110150
111151 def is_file_changed (self , path , ignore = None ):
152+ """Check if filepath has been added or modified since last check.
153+
154+ Updates filepath modification times in self._new_mtimes.
155+ Sets self.filepath to changed path.
156+ """
112157 if not os .path .isfile (path ):
113158 return False
114159
@@ -120,20 +165,21 @@ def is_file_changed(self, path, ignore=None):
120165
121166 mtime = os .path .getmtime (path )
122167
123- if path not in self ._mtimes :
124- self ._mtimes [path ] = mtime
168+ if path not in self ._task_mtimes :
169+ self ._new_mtimes [path ] = mtime
125170 self .filepath = path
126171 return mtime > self ._start
127172
128- if self ._mtimes [path ] != mtime :
129- self ._mtimes [path ] = mtime
173+ if self ._task_mtimes [path ] != mtime :
174+ self ._new_mtimes [path ] = mtime
130175 self .filepath = path
131176 return True
132177
133- self ._mtimes [path ] = mtime
178+ self ._new_mtimes [path ] = mtime
134179 return False
135180
136181 def is_folder_changed (self , path , ignore = None ):
182+ """Check if directory path has any changed filepaths."""
137183 for root , dirs , files in os .walk (path , followlinks = True ):
138184 for d in self .ignored_dirs :
139185 if d in dirs :
@@ -145,6 +191,7 @@ def is_folder_changed(self, path, ignore=None):
145191 return False
146192
147193 def is_glob_changed (self , path , ignore = None ):
194+ """Check if glob path has any changed filepaths."""
148195 for f in glob .glob (path ):
149196 if self .is_file_changed (f , ignore ):
150197 return True
0 commit comments