1#!/usr/bin/env python 2 3import _gamin 4import os.path 5 6has_debug_api = 0 7if _gamin.__dict__.has_key("MonitorDebug"): 8 has_debug_api = 1 9 10# 11# the type of events provided in the callbacks. 12# 13GAMChanged=1 14GAMDeleted=2 15GAMStartExecuting=3 16GAMStopExecuting=4 17GAMCreated=5 18GAMMoved=6 19GAMAcknowledge=7 20GAMExists=8 21GAMEndExist=9 22 23# 24# The Gamin Errno values 25GAM_OK = 0 26GAM_ARG= 1 # Bad arguments 27GAM_FILE= 2 # Bad filename 28GAM_CONNECT= 3 # Connection failure 29GAM_AUTH= 4 # Authentication failure 30GAM_MEM= 5 # Memory allocation 31GAM_UNIMPLEM=6 # Unimplemented 32GAM_INTR= 7 # Interrupted system call 33 34def GaminErrno(): 35 return _gamin.Errno() 36 37def GaminErrmsg(err = None): 38 if err == None: 39 err = _gamin.Errno() 40 if err == GAM_ARG: 41 msg = "bad argument error" 42 elif err == GAM_FILE: 43 msg = "filename error" 44 elif err == GAM_CONNECT: 45 msg = "connection error" 46 elif err == GAM_AUTH: 47 msg = "authentication error" 48 elif err == GAM_MEM: 49 msg = "memory allocation error" 50 elif err == GAM_UNIMPLEM: 51 msg = "unimplemented part error" 52 elif err == GAM_INTR: 53 msg = "interrupted system call" 54 else: 55 msg = "" 56 return msg 57 58class GaminException(Exception): 59 def __init__(self, value): 60 Exception.__init__(self) 61 self.value = value 62 self.errno = GaminErrno() 63 64 def __str__(self): 65 str = GaminErrmsg(self.errno) 66 if str != "": 67 return repr(self.value) + ': ' + str 68 return repr(self.value) 69 70class WatchMonitor: 71 """This is a wrapper for a FAM connection. It uses a single connection 72 to the gamin server, over a socket. Use get_fd() to get the file 73 descriptor which allows to plug it in an usual event loop. The 74 watch_directory(), watch_file() and stop_watch() are direct mapping 75 to the FAM API. The event raised are also a direct mapping of the 76 FAM API events.""" 77 78 class WatchObject: 79 def __init__ (self, monitor, mon_no, path, dir, callback, data=None): 80 self.monitor = monitor 81 self.callback = callback 82 self.data = data 83 self.path = path 84 self.__mon_no = mon_no 85 if dir == 1: 86 ret = _gamin.MonitorDirectory(self.__mon_no, path, self); 87 if ret < 0: 88 raise(GaminException("Failed to monitor directory %s" % 89 (path))) 90 elif dir == 0: 91 ret = _gamin.MonitorFile(self.__mon_no, path, self); 92 if ret < 0: 93 raise(GaminException("Failed to monitor file %s" % 94 (path))) 95 elif dir == -1: 96 ret = _gamin.MonitorDebug(self.__mon_no, path, self); 97 if ret < 0: 98 raise(GaminException("Failed to debug %s" % 99 (path))) 100 self.__req_no = ret 101 102 def _internal_callback(self, path, event): 103 # it is very important here to catch all exception which may 104 # arise in the client callback code. 105 try: 106 if self.data != None: 107 self.callback (path, event, self.data) 108 else: 109 self.callback (path, event) 110 except: 111 import traceback 112 traceback.print_exc() 113 114 if event == GAMAcknowledge: 115 try: 116 self.monitor.cancelled.remove(self) 117 except: 118 print "gamin failed to remove from cancelled" 119 pass 120 121 def cancel(self): 122 ret = _gamin.MonitorCancel(self.__mon_no, self.__req_no); 123 if ret < 0: 124 raise(GaminException("Failed to stop monitor on %s" % 125 (self.path))) 126 try: 127 self.monitor.cancelled.append(self) 128 except: 129 print "gamin cancel() failed to add to cancelled" 130 131 def __init__ (self): 132 self.__no = _gamin.MonitorConnect() 133 if self.__no < 0: 134 raise(GaminException("Failed to connect to gam_server")) 135 self.objects = {} 136 self.__fd = _gamin.GetFd(self.__no) 137 if self.__fd < 0: 138 _gamin.MonitorClose(self.__no) 139 raise(GaminException("Failed to get file descriptor")) 140 self.cancelled = [] 141 142 def __del__ (self): 143 self.disconnect() 144 145 def __raise_disconnected(): 146 raise(GaminException("Already disconnected")) 147 148 def _debug_object(self, value, callback, data = None): 149 if has_debug_api == 0: 150 return; 151 152 if (self.__no < 0): 153 self.__raise_disconnected(); 154 obj = self.WatchObject(self, self.__no, value, -1, callback, data) 155 # persistency need to be insured 156 self.objects["debug"] = obj 157 return obj 158 159 def disconnect(self): 160 if (self.__no >= 0): 161 _gamin.MonitorClose(self.__no) 162 self.__no = -1; 163 164 def watch_directory(self, directory, callback, data = None): 165 if (self.__no < 0): 166 self.__raise_disconnected(); 167 directory = os.path.abspath(directory) 168 169 obj = self.WatchObject(self, self.__no, directory, 1, callback, data) 170 if self.objects.has_key(directory): 171 self.objects[directory].append(obj) 172 else: 173 self.objects[directory] = [obj] 174 return obj 175 176 def watch_file(self, file, callback, data = None): 177 if (self.__no < 0): 178 self.__raise_disconnected(); 179 file = os.path.abspath(file) 180 181 obj = self.WatchObject(self, self.__no, file, 0, callback, data) 182 if self.objects.has_key(file): 183 self.objects[file].append(obj) 184 else: 185 self.objects[file] = [obj] 186 return obj 187 188 def no_exists(self): 189 if (self.__no < 0): 190 return 191 ret = _gamin.MonitorNoExists(self.__no) 192 return ret 193 194 def stop_watch(self, path): 195 if (self.__no < 0): 196 return 197 path = os.path.abspath(path) 198 try: 199 list = self.objects[path] 200 except: 201 raise(GaminException("Resource %s is not monitored" % (path))) 202 for obj in list: 203 obj.cancel() 204 self.objects[path] = [] 205 206 def get_fd(self): 207 if (self.__no < 0): 208 self.__raise_disconnected(); 209 return self.__fd 210 211 def event_pending(self): 212 if (self.__no < 0): 213 self.__raise_disconnected(); 214 ret = _gamin.EventPending(self.__no); 215 if ret < 0: 216 raise(GaminException("Failed to check pending events")) 217 return ret 218 219 def handle_one_event(self): 220 if (self.__no < 0): 221 self.__raise_disconnected(); 222 ret = _gamin.ProcessOneEvent(self.__no); 223 if ret < 0: 224 raise(GaminException("Failed to process one event")) 225 return ret 226 227 def handle_events(self): 228 if (self.__no < 0): 229 self.__raise_disconnected(); 230 ret = _gamin.ProcessEvents(self.__no); 231 if ret < 0: 232 raise(GaminException("Failed to process events")) 233 return ret 234 235def run_unit_tests(): 236 def callback(path, event): 237 print "Got callback: %s, %s" % (path, event) 238 mon = WatchMonitor() 239 print "watching current directory" 240 mon.watch_directory(".", callback) 241 import time 242 time.sleep(1) 243 print "fd: ", mon.get_fd() 244 ret = mon.event_pending() 245 print "pending: ", ret 246 if ret > 0: 247 ret = mon.handle_one_event() 248 print "processed %d event" % (ret) 249 ret = mon.handle_events() 250 print "processed %d remaining events" % (ret) 251 print "stop watching current directory" 252 mon.stop_watch(".") 253 print "disconnecting" 254 del mon 255 256if __name__ == '__main__': 257 run_unit_tests() 258