Package sabayon :: Module dirmonitor
[hide private]
[frames] | no frames]

Source Code for Module sabayon.dirmonitor

  1  # 
  2  # Copyright (C) 2005 Red Hat, Inc. 
  3  # 
  4  # This program is free software; you can redistribute it and/or modify 
  5  # it under the terms of the GNU General Public License as published by 
  6  # the Free Software Foundation; either version 2 of the License, or 
  7  # (at your option) any later version. 
  8  # 
  9  # This program is distributed in the hope that it will be useful, 
 10  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 11  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 12  # GNU General Public License for more details. 
 13  # 
 14  # You should have received a copy of the GNU General Public License 
 15  # along with this program; if not, write to the Free Software 
 16  # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
 17  # 
 18   
 19  import os 
 20  import os.path 
 21  import gobject 
 22  import gio 
 23  import util 
 24  import debuglog 
 25   
 26  N_WATCHES_LIMIT = 200 
 27   
 28  CHANGED = gio.FILE_MONITOR_EVENT_CHANGED 
 29  DELETED = gio.FILE_MONITOR_EVENT_DELETED 
 30  CREATED = gio.FILE_MONITOR_EVENT_CREATED 
 31   
32 -def dprint (fmt, *args):
33 debuglog.debug_log (False, debuglog.DEBUG_LOG_DOMAIN_DIR_MONITOR, fmt % args)
34
35 -def event_to_string (event):
36 if event == CHANGED: 37 return "changed" 38 elif event == DELETED: 39 return "deleted" 40 elif event == CREATED: 41 return "created" 42 else: 43 return "invalid"
44
45 -class DirectoryMonitor:
46 - def __init__ (self, directory, callback, data = None):
47 self.directory = directory 48 self.callback = callback 49 self.data = data 50 self.watches = {} # maps filename => FileMonitor handle 51 self.too_many_watches = False 52 self.dirs_to_ignore = [] 53 self.files_to_ignore = []
54
55 - def set_directories_to_ignore (self, dirs):
56 assert len (self.watches) == 0 57 self.dirs_to_ignore = dirs 58 dprint ("Ignoring directories %s", self.dirs_to_ignore)
59
60 - def set_files_to_ignore (self, files):
61 assert len (self.watches) == 0 62 self.files_to_ignore = files 63 dprint ("Ignoring files %s", self.files_to_ignore)
64 65 # 66 # call the user level processing 67 #
68 - def __invoke_callback (self, path, event):
69 dprint ("Invoking callback for %s - %s", path, event_to_string (event)) 70 if self.data: 71 self.callback (path, event, self.data) 72 else: 73 self.callback (path, event)
74 75 # 76 # Processing of a file_monitor callback 77 #
78 - def __handle_file_monitor_event (self, monitor, file, other_file, event_type):
79 if event_type == CHANGED or event_type == DELETED or event_type == CREATED: 80 path = file.get_path () 81 82 # Strip trailing '/'. 83 path = os.path.normpath (path) 84 85 dprint ("Got file_monitor event '%s' on '%s'", event_to_string (event_type), path) 86 87 if not self.__should_ignore_dir (path) and \ 88 not self.__should_ignore_file (path): 89 self.__invoke_callback (path, event_type) 90 91 if event_type == CREATED and os.path.isdir (path): 92 self.__monitor_dir_recurse (path, True) 93 elif event_type == DELETED: 94 if path != self.directory and self.watches.has_key (path): 95 dprint ("Deleting watch for '%s' since it got deleted", path) 96 self.watches [path].cancel () 97 del self.watches[path] 98 if len (self.watches) < N_WATCHES_LIMIT: 99 self.too_many_watches = False 100 else: 101 dprint ("Not calling callback for '%s' nor recursing in it since it is an ignored dir/file", path)
102
103 - def __should_ignore_dir (self, dir):
104 return util.should_ignore_dir (self.directory, self.dirs_to_ignore, dir)
105
106 - def __should_ignore_file (self, file):
107 return util.should_ignore_file (self.directory, self.dirs_to_ignore, self.files_to_ignore, file)
108
109 - def __monitor_dir (self, dir):
110 dir = os.path.normpath (dir) 111 112 if len (self.watches) >= N_WATCHES_LIMIT: 113 if not self.too_many_watches: 114 print "Too many directories to watch on %s" % (self.directory) 115 self.too_many_watches = True 116 return 117 118 try: 119 gfile = gio.File (dir) 120 self.watches [dir] = gfile.monitor_directory () 121 self.watches [dir].connect ("changed", self.__handle_file_monitor_event) 122 dprint ("Added directory watch for '%s'", dir) 123 except: 124 print ("Failed to add monitor for %s") % (dir) 125 util.print_exception ()
126
127 - def __monitor_dir_recurse (self, dir, new_dir = False):
128 if self.too_many_watches: 129 dprint ("Skipping recursion on '%s', as there are already too many watches", dir) 130 return 131 132 if self.__should_ignore_dir (dir): 133 dprint ("Skipping recursion on '%s'; it is an ignored directory", dir) 134 return 135 136 if dir != self.directory: 137 self.__monitor_dir (dir) 138 139 for entry in os.listdir (dir): 140 path = os.path.join (dir, entry) 141 if self.__should_ignore_dir (path) or \ 142 self.__should_ignore_file (path): 143 dprint ("Skipping callback and recursion on '%s'; it is an ignored file/dir", path) 144 continue 145 146 self.__invoke_callback (path, CREATED) 147 148 if os.path.isdir (path): 149 self.__monitor_dir_recurse (path, new_dir)
150
151 - def start (self):
152 dprint ("Starting to recursively monitor '%s'", self.directory) 153 self.__monitor_dir (self.directory) 154 self.__monitor_dir_recurse (self.directory) 155 dprint ("Ending recursive scan of '%s'; all monitors are in place now", self.directory)
156
157 - def stop (self):
158 dprint ("Stopping recursive monitoring of '%s'", self.directory) 159 160 for path in self.watches: 161 self.watches [path].cancel ()
162
163 -def run_unit_tests ():
164 import tempfile 165 import shutil 166 167 temp_path = tempfile.mkdtemp (prefix = "test-monitor-") 168 169 def handle_change (path, event, data): 170 (expected, main_loop) = data 171 if len (expected) > 0: 172 i = 0 173 for (expected_path, expected_event) in expected: 174 if expected_path == path and expected_event == event: 175 break 176 i += 1 177 if i < len (expected): 178 del expected[i] 179 if len (expected) == 0: 180 main_loop.quit ()
181 182 def expect (expected, path, event): 183 expected.append ((path, event)) 184 185 main_loop = gobject.MainLoop () 186 187 expected = [] 188 def should_not_be_reached (expected): 189 for (path, event) in expected: 190 print ("Expected event: %s %s") % (path, event_to_string (event)) 191 assert False 192 return True 193 timeout = gobject.timeout_add_seconds (5, should_not_be_reached, expected) 194 195 monitor = DirectoryMonitor (temp_path, handle_change, (expected, main_loop)) 196 monitor.set_directories_to_ignore (["bar"]) 197 monitor.set_files_to_ignore (["foobar/foo/foo.txt"]) 198 monitor.start () 199 200 expect (expected, os.path.join (temp_path, "foo.txt"), CREATED) 201 f = file (os.path.join (temp_path, "foo.txt"), "w") 202 f.close () 203 204 # ignored 205 # expect (expected, os.path.join (temp_path, "bar"), CREATED) 206 os.mkdir (os.path.join (temp_path, "bar")) 207 208 expect (expected, os.path.join (temp_path, "foobar"), CREATED) 209 expect (expected, os.path.join (temp_path, "foobar/foo"), CREATED) 210 expect (expected, os.path.join (temp_path, "foobar/foo/bar"), CREATED) 211 expect (expected, os.path.join (temp_path, "foobar/foo/bar/foo"), CREATED) 212 expect (expected, os.path.join (temp_path, "foobar/foo/bar/foo/bar"), CREATED) 213 os.makedirs (os.path.join (temp_path, "foobar/foo/bar/foo/bar")) 214 215 # ignored: 216 # expect (expected, os.path.join (temp_path, "foobar/foo/foo.txt"), CREATED) 217 f = file (os.path.join (temp_path, "foobar/foo/foo.txt"), "w") 218 f.close () 219 220 main_loop.run () 221 222 expect (expected, os.path.join (temp_path, "foobar/foo/bar/foo/bar"), DELETED) 223 expect (expected, os.path.join (temp_path, "foobar/foo/bar/foo"), DELETED) 224 expect (expected, os.path.join (temp_path, "foobar/foo/bar"), DELETED) 225 # ignored: 226 # expect (expected, os.path.join (temp_path, "foobar/foo/foo.txt"), DELETED) 227 expect (expected, os.path.join (temp_path, "foobar/foo"), DELETED) 228 expect (expected, os.path.join (temp_path, "foobar"), DELETED) 229 230 # FIXME: we should be getting this event, but we don't seem to be 231 expect (expected, os.path.join (temp_path, "foo.txt"), DELETED) 232 233 # ignore: 234 # expect (expected, os.path.join (temp_path, "bar"), DELETED) 235 expect (expected, temp_path, DELETED) 236 237 shutil.rmtree (temp_path, True) 238 239 main_loop.run () 240 241 gobject.source_remove (timeout) 242 243 monitor.stop () 244