1179860b2SJed Brown'''A remote dictionary server 2179860b2SJed Brown 3179860b2SJed Brown RDict is a typed, hierarchical, persistent dictionary intended to manage 4179860b2SJed Brown all arguments or options for a program. The interface remains exactly the 5179860b2SJed Brown same as dict, but the storage is more complicated. 6179860b2SJed Brown 7179860b2SJed Brown Argument typing is handled by wrapping all values stored in the dictionary 8179860b2SJed Brown with nargs.Arg or a subclass. A user can call setType() to set the type of 9179860b2SJed Brown an argument without any value being present. Whenever __getitem__() or 10179860b2SJed Brown __setitem__() is called, values are extracted or replaced in the wrapper. 11179860b2SJed Brown These wrappers can be accessed directly using getType(), setType(), and 12179860b2SJed Brown types(). 13179860b2SJed Brown 14179860b2SJed Brown Hierarchy is allowed using a single "parent" dictionary. All operations 15179860b2SJed Brown cascade to the parent. For instance, the length of the dictionary is the 16179860b2SJed Brown number of local keys plus the number of keys in the parent, and its 17179860b2SJed Brown parent, etc. Also, a dictionary need not have a parent. If a key does not 18*d5b43468SJose E. Roman appear in the local dictionary, the call if passed to the parent. However, 19179860b2SJed Brown in this case we see that local keys can shadow those in a parent. 20179860b2SJed Brown Communication with the parent is handled using sockets, with the parent 21179860b2SJed Brown being a server and the interactive dictionary a client. 22179860b2SJed Brown 23179860b2SJed Brown The default persistence mechanism is a pickle file, RDict.db, written 24179860b2SJed Brown whenever an argument is changed locally. A timer thread is created after 25179860b2SJed Brown an initial change, so that many rapid changes do not cause many writes. 26179860b2SJed Brown Each dictionary only saves its local entries, so all parents also 27179860b2SJed Brown separately save data in different RDict.db files. Each time a dictionary 28179860b2SJed Brown is created, the current directory is searched for an RDict.db file, and 29179860b2SJed Brown if found the contents are loaded into the dictionary. 30179860b2SJed Brown 31179860b2SJed Brown This script also provides some default actions: 32179860b2SJed Brown 33179860b2SJed Brown - server [parent] 34179860b2SJed Brown Starts a server in the current directory with an optional parent. This 35179860b2SJed Brown server will accept socket connections from other dictionaries and act 36179860b2SJed Brown as a parent. 37179860b2SJed Brown 38179860b2SJed Brown - client [parent] 39179860b2SJed Brown Creates a dictionary in the current directory with an optional parent 40179860b2SJed Brown and lists the contents. Notice that the contents may come from either 41179860b2SJed Brown an RDict.db file in the current directory, or from the parent. 42179860b2SJed Brown 43179860b2SJed Brown - clear [parent] 44179860b2SJed Brown Creates a dictionary in the current directory with an optional parent 45179860b2SJed Brown and clears the contents. Notice that this will also clear the parent. 46179860b2SJed Brown 47179860b2SJed Brown - insert <parent> <key> <value> 48179860b2SJed Brown Creates a dictionary in the current directory with a parent, and inserts 49179860b2SJed Brown the key-value pair. If "parent" is "None", no parent is assigned. 50179860b2SJed Brown 51179860b2SJed Brown - remove <parent> <key> 52179860b2SJed Brown Creates a dictionary in the current directory with a parent, and removes 53179860b2SJed Brown the given key. If "parent" is "None", no parent is assigned. 54179860b2SJed Brown''' 555b6bfdb9SJed Brownfrom __future__ import print_function 565b6bfdb9SJed Brownfrom __future__ import absolute_import 57179860b2SJed Browntry: 58179860b2SJed Brown import project # This is necessary for us to create Project objects on load 59179860b2SJed Brown import build.buildGraph # This is necessary for us to create BuildGraph objects on load 60179860b2SJed Brownexcept ImportError: 61179860b2SJed Brown pass 62179860b2SJed Brownimport nargs 63179860b2SJed Brown 64492432c8SJed Brownimport pickle 65179860b2SJed Brownimport os 66179860b2SJed Brownimport sys 67179860b2SJed BrownuseThreads = nargs.Arg.findArgument('useThreads', sys.argv[1:]) 68179860b2SJed Brownif useThreads is None: 6992626d4aSBarry Smith useThreads = 0 # workaround issue with parallel configure 7092626d4aSBarry Smithelif useThreads == 'no' or useThreads == '0': 7192626d4aSBarry Smith useThreads = 0 7292626d4aSBarry Smithelif useThreads == 'yes' or useThreads == '1': 7331de54b5SMatthew G. Knepley useThreads = 1 74179860b2SJed Brownelse: 7592626d4aSBarry Smith raise RuntimeError('Unknown option value for --useThreads ',useThreads) 76179860b2SJed Brown 77179860b2SJed Brownclass RDict(dict): 78179860b2SJed Brown '''An RDict is a typed dictionary, which may be hierarchically composed. All elements derive from the 79179860b2SJed BrownArg class, which wraps the usual value.''' 80179860b2SJed Brown # The server will self-shutdown after this many seconds 81179860b2SJed Brown shutdownDelay = 60*60*5 82179860b2SJed Brown 83179860b2SJed Brown def __init__(self, parentAddr = None, parentDirectory = None, load = 1, autoShutdown = 1, readonly = False): 84179860b2SJed Brown import atexit 85179860b2SJed Brown import time 86179860b2SJed Brown import xdrlib 87179860b2SJed Brown 88179860b2SJed Brown self.logFile = None 89179860b2SJed Brown self.setupLogFile() 90179860b2SJed Brown self.target = ['default'] 91179860b2SJed Brown self.parent = None 92179860b2SJed Brown self.saveTimer = None 93179860b2SJed Brown self.shutdownTimer = None 94179860b2SJed Brown self.lastAccess = time.time() 95179860b2SJed Brown self.saveFilename = 'RDict.db' 96179860b2SJed Brown self.addrFilename = 'RDict.loc' 97179860b2SJed Brown self.parentAddr = parentAddr 98179860b2SJed Brown self.isServer = 0 99179860b2SJed Brown self.readonly = readonly 100179860b2SJed Brown self.parentDirectory = parentDirectory 101179860b2SJed Brown self.packer = xdrlib.Packer() 102179860b2SJed Brown self.unpacker = xdrlib.Unpacker('') 103492432c8SJed Brown self.stopCmd = pickle.dumps(('stop',)) 104179860b2SJed Brown self.writeLogLine('Greetings') 105179860b2SJed Brown self.connectParent(self.parentAddr, self.parentDirectory) 106179860b2SJed Brown if load: self.load() 107179860b2SJed Brown if autoShutdown and useThreads: 108179860b2SJed Brown atexit.register(self.shutdown) 109179860b2SJed Brown self.writeLogLine('SERVER: Last access '+str(self.lastAccess)) 110179860b2SJed Brown return 111179860b2SJed Brown 112179860b2SJed Brown def __getstate__(self): 113179860b2SJed Brown '''Remove any parent socket object, the XDR translators, and the log file from the dictionary before pickling''' 114179860b2SJed Brown self.writeLogLine('Pickling RDict') 115179860b2SJed Brown d = self.__dict__.copy() 116179860b2SJed Brown if 'parent' in d: del d['parent'] 117179860b2SJed Brown if 'saveTimer' in d: del d['saveTimer'] 118179860b2SJed Brown if '_setCommandLine' in d: del d['_setCommandLine'] 119179860b2SJed Brown del d['packer'] 120179860b2SJed Brown del d['unpacker'] 121179860b2SJed Brown del d['logFile'] 122179860b2SJed Brown return d 123179860b2SJed Brown 124179860b2SJed Brown def __setstate__(self, d): 125179860b2SJed Brown '''Reconnect the parent socket object, recreate the XDR translators and reopen the log file after unpickling''' 126c6ef1b5bSJed Brown self.logFile = open('RDict.log', 'a') 127179860b2SJed Brown self.writeLogLine('Unpickling RDict') 128179860b2SJed Brown self.__dict__.update(d) 129179860b2SJed Brown import xdrlib 130179860b2SJed Brown self.packer = xdrlib.Packer() 131179860b2SJed Brown self.unpacker = xdrlib.Unpacker('') 132179860b2SJed Brown self.connectParent(self.parentAddr, self.parentDirectory) 133179860b2SJed Brown return 134179860b2SJed Brown 135179860b2SJed Brown def setupLogFile(self, filename = 'RDict.log'): 136179860b2SJed Brown if not self.logFile is None: 137179860b2SJed Brown self.logFile.close() 138179860b2SJed Brown if os.path.isfile(filename) and os.stat(filename).st_size > 10*1024*1024: 139179860b2SJed Brown if os.path.isfile(filename+'.bkp'): 140179860b2SJed Brown os.remove(filename+'.bkp') 141179860b2SJed Brown os.rename(filename, filename+'.bkp') 142c6ef1b5bSJed Brown self.logFile = open(filename, 'w') 143179860b2SJed Brown else: 144c6ef1b5bSJed Brown self.logFile = open(filename, 'a') 145179860b2SJed Brown return 146179860b2SJed Brown 147179860b2SJed Brown def writeLogLine(self, message): 148179860b2SJed Brown '''Writes the message to the log along with the current time''' 149179860b2SJed Brown import time 150179860b2SJed Brown self.logFile.write('('+str(os.getpid())+')('+str(id(self))+')'+message+' ['+time.asctime(time.localtime())+']\n') 151179860b2SJed Brown self.logFile.flush() 152179860b2SJed Brown return 153179860b2SJed Brown 154179860b2SJed Brown def __len__(self): 155179860b2SJed Brown '''Returns the length of both the local and parent dictionaries''' 156179860b2SJed Brown length = dict.__len__(self) 157179860b2SJed Brown if not self.parent is None: 158179860b2SJed Brown length = length + self.send() 159179860b2SJed Brown return length 160179860b2SJed Brown 161179860b2SJed Brown def getType(self, key): 162179860b2SJed Brown '''Checks for the key locally, and if not found consults the parent. Returns the Arg object or None if not found.''' 1633b049ba2SJed Brown try: 1643b049ba2SJed Brown value = dict.__getitem__(self, key) 1653b049ba2SJed Brown self.writeLogLine('getType: Getting local type for '+key+' '+str(value)) 1663b049ba2SJed Brown return value 1673b049ba2SJed Brown except KeyError: 1683b049ba2SJed Brown pass 1693b049ba2SJed Brown if self.parent: 170179860b2SJed Brown return self.send(key) 171179860b2SJed Brown return None 172179860b2SJed Brown 1733b049ba2SJed Brown def dict_has_key(self, key): 1743b049ba2SJed Brown """Utility to check whether the key is present in the dictionary without RDict side-effects.""" 1753b049ba2SJed Brown return key in dict(self) 1763b049ba2SJed Brown 177179860b2SJed Brown def __getitem__(self, key): 178179860b2SJed Brown '''Checks for the key locally, and if not found consults the parent. Returns the value of the Arg. 179179860b2SJed Brown - If the value has not been set, the user will be prompted for input''' 1803b049ba2SJed Brown if self.dict_has_key(key): 181179860b2SJed Brown self.writeLogLine('__getitem__: '+key+' has local type') 182179860b2SJed Brown pass 183179860b2SJed Brown elif not self.parent is None: 184179860b2SJed Brown self.writeLogLine('__getitem__: Checking parent value') 185179860b2SJed Brown if self.send(key, operation = 'has_key'): 186179860b2SJed Brown self.writeLogLine('__getitem__: Parent has value') 187179860b2SJed Brown return self.send(key) 188179860b2SJed Brown else: 189179860b2SJed Brown self.writeLogLine('__getitem__: Checking parent type') 190179860b2SJed Brown arg = self.send(key, operation = 'getType') 191179860b2SJed Brown if not arg: 192179860b2SJed Brown self.writeLogLine('__getitem__: Parent has no type') 193179860b2SJed Brown arg = nargs.Arg(key) 194179860b2SJed Brown try: 195179860b2SJed Brown value = arg.getValue() 1965b6bfdb9SJed Brown except AttributeError as e: 197179860b2SJed Brown self.writeLogLine('__getitem__: Parent had invalid entry: '+str(e)) 198179860b2SJed Brown arg = nargs.Arg(key) 199179860b2SJed Brown value = arg.getValue() 200179860b2SJed Brown self.writeLogLine('__getitem__: Setting parent value '+str(value)) 201179860b2SJed Brown self.send(key, value, operation = '__setitem__') 202179860b2SJed Brown return value 203179860b2SJed Brown else: 204179860b2SJed Brown self.writeLogLine('__getitem__: Setting local type for '+key) 205179860b2SJed Brown dict.__setitem__(self, key, nargs.Arg(key)) 20631f4da61SSatish Balay #self.save() 207179860b2SJed Brown self.writeLogLine('__getitem__: Setting local value for '+key) 208179860b2SJed Brown return dict.__getitem__(self, key).getValue() 209179860b2SJed Brown 210179860b2SJed Brown def setType(self, key, value, forceLocal = 0): 211179860b2SJed Brown '''Checks for the key locally, and if not found consults the parent. Sets the type for this key. 212179860b2SJed Brown - If a value for the key already exists, it is converted to the new type''' 213179860b2SJed Brown if not isinstance(value, nargs.Arg): 214179860b2SJed Brown raise TypeError('An argument type must be a subclass of Arg') 215179860b2SJed Brown value.setKey(key) 2163b049ba2SJed Brown if forceLocal or self.parent is None or self.dict_has_key(key): 2173b049ba2SJed Brown if self.dict_has_key(key): 218179860b2SJed Brown v = dict.__getitem__(self, key) 219179860b2SJed Brown if v.isValueSet(): 220179860b2SJed Brown try: 221179860b2SJed Brown value.setValue(v.getValue()) 222a5737712SSatish Balay except TypeError: 2235b6bfdb9SJed Brown print(value.__class__.__name__[3:]) 2245b6bfdb9SJed Brown print('-----------------------------------------------------------------------') 2255b6bfdb9SJed Brown print('Warning! Incorrect argument type specified: -'+str(key)+'='+str(v.getValue())+' - expecting type '+value.__class__.__name__[3:]+'.') 2265b6bfdb9SJed Brown print('-----------------------------------------------------------------------') 227a5737712SSatish Balay pass 228179860b2SJed Brown dict.__setitem__(self, key, value) 22931f4da61SSatish Balay #self.save() 230179860b2SJed Brown else: 231179860b2SJed Brown return self.send(key, value) 232179860b2SJed Brown return 233179860b2SJed Brown 234179860b2SJed Brown def __setitem__(self, key, value): 235179860b2SJed Brown '''Checks for the key locally, and if not found consults the parent. Sets the value of the Arg.''' 2363b049ba2SJed Brown if not self.dict_has_key(key): 237179860b2SJed Brown if not self.parent is None: 238179860b2SJed Brown return self.send(key, value) 239179860b2SJed Brown else: 240179860b2SJed Brown dict.__setitem__(self, key, nargs.Arg(key)) 241179860b2SJed Brown dict.__getitem__(self, key).setValue(value) 242179860b2SJed Brown self.writeLogLine('__setitem__: Set value for '+key+' to '+str(dict.__getitem__(self, key))) 24331f4da61SSatish Balay #self.save() 244179860b2SJed Brown return 245179860b2SJed Brown 246179860b2SJed Brown def __delitem__(self, key): 247179860b2SJed Brown '''Checks for the key locally, and if not found consults the parent. Deletes the Arg completely.''' 2483b049ba2SJed Brown if self.dict_has_key(key): 249179860b2SJed Brown dict.__delitem__(self, key) 25031f4da61SSatish Balay #self.save() 251179860b2SJed Brown elif not self.parent is None: 252179860b2SJed Brown self.send(key) 253179860b2SJed Brown return 254179860b2SJed Brown 255179860b2SJed Brown def clear(self): 256179860b2SJed Brown '''Clears both the local and parent dictionaries''' 257179860b2SJed Brown if dict.__len__(self): 258179860b2SJed Brown dict.clear(self) 25931f4da61SSatish Balay #self.save() 260179860b2SJed Brown if not self.parent is None: 261179860b2SJed Brown self.send() 262179860b2SJed Brown return 263179860b2SJed Brown 264179860b2SJed Brown def __contains__(self, key): 265179860b2SJed Brown '''Checks for the key locally, and if not found consults the parent. Then checks whether the value has been set''' 2663b049ba2SJed Brown if self.dict_has_key(key): 267179860b2SJed Brown if dict.__getitem__(self, key).isValueSet(): 268179860b2SJed Brown self.writeLogLine('has_key: Have value for '+key) 269179860b2SJed Brown else: 270179860b2SJed Brown self.writeLogLine('has_key: Do not have value for '+key) 271179860b2SJed Brown return dict.__getitem__(self, key).isValueSet() 272179860b2SJed Brown elif not self.parent is None: 273179860b2SJed Brown return self.send(key) 274179860b2SJed Brown return 0 275179860b2SJed Brown 276179860b2SJed Brown def get(self, key, default=None): 2775b6bfdb9SJed Brown if key in self: 278179860b2SJed Brown return self.__getitem__(key) 279179860b2SJed Brown else: 280179860b2SJed Brown return default 281179860b2SJed Brown 282179860b2SJed Brown def hasType(self, key): 283179860b2SJed Brown '''Checks for the key locally, and if not found consults the parent. Then checks whether the type has been set''' 2843b049ba2SJed Brown if self.dict_has_key(key): 285179860b2SJed Brown return 1 286179860b2SJed Brown elif not self.parent is None: 287179860b2SJed Brown return self.send(key) 288179860b2SJed Brown return 0 289179860b2SJed Brown 290179860b2SJed Brown def items(self): 291179860b2SJed Brown '''Return a list of all accessible items, as (key, value) pairs.''' 292179860b2SJed Brown l = dict.items(self) 293179860b2SJed Brown if not self.parent is None: 294179860b2SJed Brown l.extend(self.send()) 295179860b2SJed Brown return l 296179860b2SJed Brown 297179860b2SJed Brown def localitems(self): 298179860b2SJed Brown '''Return a list of all the items stored locally, as (key, value) pairs.''' 299179860b2SJed Brown return dict.items(self) 300179860b2SJed Brown 301179860b2SJed Brown def keys(self): 302179860b2SJed Brown '''Returns the list of keys in both the local and parent dictionaries''' 303bb3dd2f6SJed Brown keyList = [key for key in dict.keys(self) if dict.__getitem__(self, key).isValueSet()] 304179860b2SJed Brown if not self.parent is None: 305179860b2SJed Brown keyList.extend(self.send()) 306179860b2SJed Brown return keyList 307179860b2SJed Brown 308179860b2SJed Brown def types(self): 309179860b2SJed Brown '''Returns the list of keys for which types are defined in both the local and parent dictionaries''' 310179860b2SJed Brown keyList = dict.keys(self) 311179860b2SJed Brown if not self.parent is None: 312179860b2SJed Brown keyList.extend(self.send()) 313179860b2SJed Brown return keyList 314179860b2SJed Brown 315179860b2SJed Brown def update(self, d): 316179860b2SJed Brown '''Update the dictionary with the contents of d''' 317179860b2SJed Brown for k in d: 318179860b2SJed Brown self[k] = d[k] 319179860b2SJed Brown return 320179860b2SJed Brown 321179860b2SJed Brown def updateTypes(self, d): 322179860b2SJed Brown '''Update types locally, which is equivalent to the dict.update() method''' 323179860b2SJed Brown return dict.update(self, d) 324179860b2SJed Brown 325179860b2SJed Brown def insertArg(self, key, value, arg): 326179860b2SJed Brown '''Insert a (key, value) pair into the dictionary. If key is None, arg is put into the target list.''' 327179860b2SJed Brown if not key is None: 328179860b2SJed Brown self[key] = value 329179860b2SJed Brown else: 330179860b2SJed Brown if not self.target == ['default']: 331179860b2SJed Brown self.target.append(arg) 332179860b2SJed Brown else: 333179860b2SJed Brown self.target = [arg] 334179860b2SJed Brown return 335179860b2SJed Brown 336179860b2SJed Brown def insertArgs(self, args): 337179860b2SJed Brown '''Insert some text arguments into the dictionary (list and dictionaries are recognized)''' 338179860b2SJed Brown 339179860b2SJed Brown if isinstance(args, list): 340179860b2SJed Brown for arg in args: 341179860b2SJed Brown (key, value) = nargs.Arg.parseArgument(arg) 342179860b2SJed Brown self.insertArg(key, value, arg) 3436998aabbSVaclav Hapla elif hasattr(args, 'keys'): 344179860b2SJed Brown for key in args.keys(): 345179860b2SJed Brown if isinstance(args[key], str): 346179860b2SJed Brown value = nargs.Arg.parseValue(args[key]) 347179860b2SJed Brown else: 348179860b2SJed Brown value = args[key] 349179860b2SJed Brown self.insertArg(key, value, None) 350179860b2SJed Brown elif isinstance(args, str): 351179860b2SJed Brown (key, value) = nargs.Arg.parseArgument(args) 352179860b2SJed Brown self.insertArg(key, value, args) 353179860b2SJed Brown return 354179860b2SJed Brown 355179860b2SJed Brown def hasParent(self): 356179860b2SJed Brown '''Return True if this RDict has a parent dictionary''' 357179860b2SJed Brown return not self.parent is None 358179860b2SJed Brown 359179860b2SJed Brown def getServerAddr(self, dir): 360179860b2SJed Brown '''Read the server socket address (in pickled form) from a file, usually RDict.loc 361179860b2SJed Brown - If we fail to connect to the server specified in the file, we spawn it using startServer()''' 362179860b2SJed Brown filename = os.path.join(dir, self.addrFilename) 363179860b2SJed Brown if not os.path.exists(filename): 364179860b2SJed Brown self.startServer(filename) 365179860b2SJed Brown if not os.path.exists(filename): 366179860b2SJed Brown raise RuntimeError('Server address file does not exist: '+filename) 367179860b2SJed Brown try: 368179860b2SJed Brown f = open(filename, 'r') 369492432c8SJed Brown addr = pickle.load(f) 370179860b2SJed Brown f.close() 371179860b2SJed Brown return addr 3725b6bfdb9SJed Brown except Exception as e: 373179860b2SJed Brown self.writeLogLine('CLIENT: Exception during server address determination: '+str(e.__class__)+': '+str(e)) 374179860b2SJed Brown raise RuntimeError('Could not get server address in '+filename) 375179860b2SJed Brown 376179860b2SJed Brown def writeServerAddr(self, server): 377179860b2SJed Brown '''Write the server socket address (in pickled form) to a file, usually RDict.loc.''' 378c6ef1b5bSJed Brown f = open(self.addrFilename, 'w') 379492432c8SJed Brown pickle.dump(server.server_address, f) 380179860b2SJed Brown f.close() 381179860b2SJed Brown self.writeLogLine('SERVER: Wrote lock file '+os.path.abspath(self.addrFilename)) 382179860b2SJed Brown return 383179860b2SJed Brown 384179860b2SJed Brown def startServer(self, addrFilename): 385179860b2SJed Brown '''Spawn a new RDict server in the parent directory''' 386179860b2SJed Brown import RDict # Need this to locate server script 387179860b2SJed Brown import sys 388179860b2SJed Brown import time 389becf0a19SJed Brown import sysconfig 390179860b2SJed Brown 391179860b2SJed Brown self.writeLogLine('CLIENT: Spawning a new server with lock file '+os.path.abspath(addrFilename)) 392179860b2SJed Brown if os.path.exists(addrFilename): 393179860b2SJed Brown os.remove(addrFilename) 394179860b2SJed Brown oldDir = os.getcwd() 395179860b2SJed Brown source = os.path.join(os.path.dirname(os.path.abspath(sys.modules['RDict'].__file__)), 'RDict.py') 396becf0a19SJed Brown interpreter = os.path.join(sysconfig.get_config_var('BINDIR'), sysconfig.get_config_var('PYTHON')) 397179860b2SJed Brown if not os.path.isfile(interpreter): 398179860b2SJed Brown interpreter = 'python' 399179860b2SJed Brown os.chdir(os.path.dirname(addrFilename)) 400179860b2SJed Brown self.writeLogLine('CLIENT: Executing '+interpreter+' '+source+' server"') 401179860b2SJed Brown try: 402179860b2SJed Brown os.spawnvp(os.P_NOWAIT, interpreter, [interpreter, source, 'server']) 403179860b2SJed Brown except: 404179860b2SJed Brown self.writeLogLine('CLIENT: os.spawnvp failed.\n \ 405179860b2SJed Brown This is a typical problem on CYGWIN systems. If you are using CYGWIN,\n \ 406179860b2SJed Brown you can fix this problem by running /bin/rebaseall. If you do not have\n \ 407179860b2SJed Brown this program, you can install it with the CYGWIN installer in the package\n \ 408179860b2SJed Brown Rebase, under the category System. You must run /bin/rebaseall after\n \ 409179860b2SJed Brown turning off all cygwin services -- in particular sshd, if any such services\n \ 410179860b2SJed Brown are running. For more information about rebase, go to http://www.cygwin.com') 4115b6bfdb9SJed Brown print('\n \ 412179860b2SJed Brown This is a typical problem on CYGWIN systems. If you are using CYGWIN,\n \ 413179860b2SJed Brown you can fix this problem by running /bin/rebaseall. If you do not have\n \ 414179860b2SJed Brown this program, you can install it with the CYGWIN installer in the package\n \ 415179860b2SJed Brown Rebase, under the category System. You must run /bin/rebaseall after\n \ 416179860b2SJed Brown turning off all cygwin services -- in particular sshd, if any such services\n \ 4175b6bfdb9SJed Brown are running. For more information about rebase, go to http://www.cygwin.com\n') 418179860b2SJed Brown raise 419179860b2SJed Brown os.chdir(oldDir) 420179860b2SJed Brown timeout = 1 421179860b2SJed Brown for i in range(10): 422179860b2SJed Brown time.sleep(timeout) 423179860b2SJed Brown timeout *= 2 424179860b2SJed Brown if timeout > 100: timeout = 100 425179860b2SJed Brown if os.path.exists(addrFilename): return 426179860b2SJed Brown self.writeLogLine('CLIENT: Could not start server') 427179860b2SJed Brown return 428179860b2SJed Brown 429179860b2SJed Brown def connectParent(self, addr, dir): 430179860b2SJed Brown '''Try to connect to a parent RDict server 431179860b2SJed Brown - If addr and dir are both None, this operation fails 432179860b2SJed Brown - If addr is None, check for an address file in dir''' 433179860b2SJed Brown if addr is None: 434179860b2SJed Brown if dir is None: return 0 435179860b2SJed Brown addr = self.getServerAddr(dir) 436179860b2SJed Brown 437179860b2SJed Brown import socket 438179860b2SJed Brown import errno 439179860b2SJed Brown connected = 0 440179860b2SJed Brown s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 441179860b2SJed Brown timeout = 1 442179860b2SJed Brown for i in range(10): 443179860b2SJed Brown try: 444179860b2SJed Brown self.writeLogLine('CLIENT: Trying to connect to '+str(addr)) 445179860b2SJed Brown s.connect(addr) 446179860b2SJed Brown connected = 1 447179860b2SJed Brown break 4485b6bfdb9SJed Brown except socket.error as e: 449179860b2SJed Brown self.writeLogLine('CLIENT: Failed to connect: '+str(e)) 450179860b2SJed Brown if e[0] == errno.ECONNREFUSED: 451179860b2SJed Brown try: 452179860b2SJed Brown import time 453179860b2SJed Brown time.sleep(timeout) 454179860b2SJed Brown timeout *= 2 455179860b2SJed Brown if timeout > 100: timeout = 100 456179860b2SJed Brown except KeyboardInterrupt: 457179860b2SJed Brown break 458179860b2SJed Brown # Try to spawn parent 459179860b2SJed Brown if dir: 460179860b2SJed Brown filename = os.path.join(dir, self.addrFilename) 461179860b2SJed Brown if os.path.isfile(filename): 462179860b2SJed Brown os.remove(filename) 463179860b2SJed Brown self.startServer(filename) 4645b6bfdb9SJed Brown except Exception as e: 465179860b2SJed Brown self.writeLogLine('CLIENT: Failed to connect: '+str(e.__class__)+': '+str(e)) 466179860b2SJed Brown if not connected: 467179860b2SJed Brown self.writeLogLine('CLIENT: Failed to connect to parent') 468179860b2SJed Brown return 0 469179860b2SJed Brown self.parent = s 470179860b2SJed Brown self.writeLogLine('CLIENT: Connected to '+str(self.parent)) 471179860b2SJed Brown return 1 472179860b2SJed Brown 473179860b2SJed Brown def sendPacket(self, s, packet, source = 'Unknown', isPickled = 0): 474179860b2SJed Brown '''Pickle the input packet. Send first the size of the pickled string in 32-bit integer, and then the string itself''' 475179860b2SJed Brown self.writeLogLine(source+': Sending packet '+str(packet)) 476179860b2SJed Brown if isPickled: 477179860b2SJed Brown p = packet 478179860b2SJed Brown else: 479492432c8SJed Brown p = pickle.dumps(packet) 480179860b2SJed Brown self.packer.reset() 481179860b2SJed Brown self.packer.pack_uint(len(p)) 482179860b2SJed Brown if hasattr(s, 'write'): 483179860b2SJed Brown s.write(self.packer.get_buffer()) 484179860b2SJed Brown s.write(p) 485179860b2SJed Brown else: 486179860b2SJed Brown s.sendall(self.packer.get_buffer()) 487179860b2SJed Brown s.sendall(p) 488179860b2SJed Brown self.writeLogLine(source+': Sent packet') 489179860b2SJed Brown return 490179860b2SJed Brown 491179860b2SJed Brown def recvPacket(self, s, source = 'Unknown'): 492179860b2SJed Brown '''Receive first the size of the pickled string in a 32-bit integer, and then the string itself. Return the unpickled object''' 493179860b2SJed Brown self.writeLogLine(source+': Receiving packet') 494179860b2SJed Brown if hasattr(s, 'read'): 495179860b2SJed Brown s.read(4) 496492432c8SJed Brown value = pickle.load(s) 497179860b2SJed Brown else: 498179860b2SJed Brown # I probably need to check that it actually read these 4 bytes 499179860b2SJed Brown self.unpacker.reset(s.recv(4)) 500179860b2SJed Brown length = self.unpacker.unpack_uint() 501179860b2SJed Brown objString = '' 502179860b2SJed Brown while len(objString) < length: 503179860b2SJed Brown objString += s.recv(length - len(objString)) 504492432c8SJed Brown value = pickle.loads(objString) 505179860b2SJed Brown self.writeLogLine(source+': Received packet '+str(value)) 506179860b2SJed Brown return value 507179860b2SJed Brown 508179860b2SJed Brown def send(self, key = None, value = None, operation = None): 509179860b2SJed Brown '''Send a request to the parent''' 510179860b2SJed Brown import inspect 511179860b2SJed Brown 512179860b2SJed Brown objString = '' 513179860b2SJed Brown for i in range(3): 514179860b2SJed Brown try: 515179860b2SJed Brown packet = [] 516179860b2SJed Brown if operation is None: 517179860b2SJed Brown operation = inspect.stack()[1][3] 518179860b2SJed Brown packet.append(operation) 519179860b2SJed Brown if not key is None: 520179860b2SJed Brown packet.append(key) 521179860b2SJed Brown if not value is None: 522179860b2SJed Brown packet.append(value) 523179860b2SJed Brown self.sendPacket(self.parent, tuple(packet), source = 'CLIENT') 524179860b2SJed Brown response = self.recvPacket(self.parent, source = 'CLIENT') 525179860b2SJed Brown break 5265b6bfdb9SJed Brown except IOError as e: 527179860b2SJed Brown self.writeLogLine('CLIENT: IOError '+str(e)) 528179860b2SJed Brown if e.errno == 32: 529179860b2SJed Brown self.connectParent(self.parentAddr, self.parentDirectory) 5305b6bfdb9SJed Brown except Exception as e: 531179860b2SJed Brown self.writeLogLine('CLIENT: Exception '+str(e)+' '+str(e.__class__)) 532179860b2SJed Brown try: 533179860b2SJed Brown if isinstance(response, Exception): 534179860b2SJed Brown self.writeLogLine('CLIENT: Got an exception '+str(response)) 535179860b2SJed Brown raise response 536179860b2SJed Brown else: 537179860b2SJed Brown self.writeLogLine('CLIENT: Received value '+str(response)+' '+str(type(response))) 538179860b2SJed Brown except UnboundLocalError: 539179860b2SJed Brown self.writeLogLine('CLIENT: Could not unpickle response') 540179860b2SJed Brown response = None 541179860b2SJed Brown return response 542179860b2SJed Brown 543179860b2SJed Brown def serve(self): 544179860b2SJed Brown '''Start a server''' 545179860b2SJed Brown import socket 5468f450857SSatish Balay import SocketServer # novermin 547179860b2SJed Brown 548179860b2SJed Brown if not useThreads: 549179860b2SJed Brown raise RuntimeError('Cannot run a server if threads are disabled') 550179860b2SJed Brown 551179860b2SJed Brown class ProcessHandler(SocketServer.StreamRequestHandler): 552179860b2SJed Brown def handle(self): 553179860b2SJed Brown import time 554179860b2SJed Brown 555179860b2SJed Brown self.server.rdict.lastAccess = time.time() 556179860b2SJed Brown self.server.rdict.writeLogLine('SERVER: Started new handler') 557179860b2SJed Brown while 1: 558179860b2SJed Brown try: 559179860b2SJed Brown value = self.server.rdict.recvPacket(self.rfile, source = 'SERVER') 5605b6bfdb9SJed Brown except EOFError as e: 561179860b2SJed Brown self.server.rdict.writeLogLine('SERVER: EOFError receiving packet '+str(e)+' '+str(e.__class__)) 562179860b2SJed Brown return 5635b6bfdb9SJed Brown except Exception as e: 564179860b2SJed Brown self.server.rdict.writeLogLine('SERVER: Error receiving packet '+str(e)+' '+str(e.__class__)) 565179860b2SJed Brown self.server.rdict.sendPacket(self.wfile, e, source = 'SERVER') 566179860b2SJed Brown continue 567179860b2SJed Brown if value[0] == 'stop': break 568179860b2SJed Brown try: 569179860b2SJed Brown response = getattr(self.server.rdict, value[0])(*value[1:]) 5705b6bfdb9SJed Brown except Exception as e: 571179860b2SJed Brown self.server.rdict.writeLogLine('SERVER: Error executing operation '+str(e)+' '+str(e.__class__)) 572179860b2SJed Brown self.server.rdict.sendPacket(self.wfile, e, source = 'SERVER') 573179860b2SJed Brown else: 574179860b2SJed Brown self.server.rdict.sendPacket(self.wfile, response, source = 'SERVER') 575179860b2SJed Brown return 576179860b2SJed Brown 577179860b2SJed Brown # check if server is running 578179860b2SJed Brown if os.path.exists(self.addrFilename): 579179860b2SJed Brown rdict = RDict(parentDirectory = '.') 580179860b2SJed Brown hasParent = rdict.hasParent() 581179860b2SJed Brown del rdict 582179860b2SJed Brown if hasParent: 583179860b2SJed Brown self.writeLogLine('SERVER: Another server is already running') 584179860b2SJed Brown raise RuntimeError('Server already running') 585179860b2SJed Brown 586179860b2SJed Brown # Daemonize server 587179860b2SJed Brown self.writeLogLine('SERVER: Daemonizing server') 588179860b2SJed Brown if os.fork(): # Launch child 589179860b2SJed Brown os._exit(0) # Kill off parent, so we are not a process group leader and get a new PID 590179860b2SJed Brown os.setsid() # Set session ID, so that we have no controlling terminal 591179860b2SJed Brown # We choose to leave cwd at RDict.py: os.chdir('/') # Make sure root directory is not on a mounted drive 5925b6bfdb9SJed Brown os.umask(0o77) # Fix creation mask 593179860b2SJed Brown for i in range(3): # Crappy stopgap for closing descriptors 594179860b2SJed Brown try: 595179860b2SJed Brown os.close(i) 5965b6bfdb9SJed Brown except OSError as e: 597179860b2SJed Brown if e.errno != errno.EBADF: 598179860b2SJed Brown raise RuntimeError('Could not close default descriptor '+str(i)) 599179860b2SJed Brown 600179860b2SJed Brown # wish there was a better way to get a usable socket 601179860b2SJed Brown self.writeLogLine('SERVER: Establishing socket server') 602179860b2SJed Brown basePort = 8000 603179860b2SJed Brown flag = 'nosocket' 604179860b2SJed Brown p = 1 605179860b2SJed Brown while p < 1000 and flag == 'nosocket': 606179860b2SJed Brown try: 607179860b2SJed Brown server = SocketServer.ThreadingTCPServer((socket.gethostname(), basePort+p), ProcessHandler) 608179860b2SJed Brown flag = 'socket' 6095b6bfdb9SJed Brown except Exception as e: 610179860b2SJed Brown p = p + 1 611179860b2SJed Brown if flag == 'nosocket': 612179860b2SJed Brown p = 1 613179860b2SJed Brown while p < 1000 and flag == 'nosocket': 614179860b2SJed Brown try: 615179860b2SJed Brown server = SocketServer.ThreadingTCPServer(('localhost', basePort+p), ProcessHandler) 616179860b2SJed Brown flag = 'socket' 6175b6bfdb9SJed Brown except Exception as e: 618179860b2SJed Brown p = p + 1 619179860b2SJed Brown if flag == 'nosocket': 620179860b2SJed Brown self.writeLogLine('SERVER: Could not established socket server on port '+str(basePort+p)) 6215b6bfdb9SJed Brown raise RuntimeError('Cannot get available socket') 622179860b2SJed Brown self.writeLogLine('SERVER: Established socket server on port '+str(basePort+p)) 623179860b2SJed Brown 624179860b2SJed Brown self.isServer = 1 625179860b2SJed Brown self.writeServerAddr(server) 626179860b2SJed Brown self.serverShutdown(os.getpid()) 627179860b2SJed Brown 628179860b2SJed Brown server.rdict = self 629179860b2SJed Brown self.writeLogLine('SERVER: Started server') 630179860b2SJed Brown server.serve_forever() 631179860b2SJed Brown return 632179860b2SJed Brown 633179860b2SJed Brown def load(self): 634179860b2SJed Brown '''Load the saved dictionary''' 635179860b2SJed Brown if not self.parentDirectory is None and os.path.samefile(os.getcwd(), self.parentDirectory): 636179860b2SJed Brown return 637179860b2SJed Brown self.saveFilename = os.path.abspath(self.saveFilename) 638179860b2SJed Brown if os.path.exists(self.saveFilename): 639179860b2SJed Brown try: 640d2acaa84SFrancesco Ballarin dbFile = open(self.saveFilename, 'rb') 641492432c8SJed Brown data = pickle.load(dbFile) 642179860b2SJed Brown self.updateTypes(data) 643179860b2SJed Brown dbFile.close() 644179860b2SJed Brown self.writeLogLine('Loaded dictionary from '+self.saveFilename) 6455b6bfdb9SJed Brown except Exception as e: 646179860b2SJed Brown self.writeLogLine('Problem loading dictionary from '+self.saveFilename+'\n--> '+str(e)) 647179860b2SJed Brown else: 648179860b2SJed Brown self.writeLogLine('No dictionary to load in this file: '+self.saveFilename) 649179860b2SJed Brown return 650179860b2SJed Brown 651603e7a67SSatish Balay def save(self, force = 1): 652179860b2SJed Brown '''Save the dictionary after 5 seconds, ignoring all subsequent calls until the save 653179860b2SJed Brown - Giving force = True will cause an immediate save''' 654179860b2SJed Brown if self.readonly: return 65508eb64ffSMatthew G. Knepley if force: 656179860b2SJed Brown self.saveTimer = None 657179860b2SJed Brown # This should be a critical section 658b8b3d021SJed Brown dbFile = open(self.saveFilename, 'wb') 659bb3dd2f6SJed Brown data = dict([i for i in self.localitems() if not i[1].getTemporary()]) 660492432c8SJed Brown pickle.dump(data, dbFile) 661179860b2SJed Brown dbFile.close() 662179860b2SJed Brown self.writeLogLine('Saved local dictionary to '+os.path.abspath(self.saveFilename)) 663179860b2SJed Brown elif not self.saveTimer: 664179860b2SJed Brown import threading 665179860b2SJed Brown self.saveTimer = threading.Timer(5, self.save, [], {'force': 1}) 666179860b2SJed Brown self.saveTimer.setDaemon(1) 667179860b2SJed Brown self.saveTimer.start() 668179860b2SJed Brown return 669179860b2SJed Brown 670179860b2SJed Brown def shutdown(self): 671179860b2SJed Brown '''Shutdown the dictionary, writing out changes and notifying parent''' 672179860b2SJed Brown if self.saveTimer: 673179860b2SJed Brown self.saveTimer.cancel() 674179860b2SJed Brown self.save(force = 1) 675179860b2SJed Brown if self.isServer and os.path.isfile(self.addrFilename): 676179860b2SJed Brown os.remove(self.addrFilename) 677179860b2SJed Brown if not self.parent is None: 678179860b2SJed Brown self.sendPacket(self.parent, self.stopCmd, isPickled = 1) 679179860b2SJed Brown self.parent.close() 680179860b2SJed Brown self.parent = None 681179860b2SJed Brown self.writeLogLine('Shutting down') 682179860b2SJed Brown self.logFile.close() 683179860b2SJed Brown return 684179860b2SJed Brown 685179860b2SJed Brown def serverShutdown(self, pid, delay = shutdownDelay): 686179860b2SJed Brown if self.shutdownTimer is None: 687179860b2SJed Brown import threading 688179860b2SJed Brown 689179860b2SJed Brown self.shutdownTimer = threading.Timer(delay, self.serverShutdown, [pid], {'delay': 0}) 690179860b2SJed Brown self.shutdownTimer.setDaemon(1) 691179860b2SJed Brown self.shutdownTimer.start() 692179860b2SJed Brown self.writeLogLine('SERVER: Set shutdown timer for process '+str(pid)+' at '+str(delay)+' seconds') 693179860b2SJed Brown else: 694179860b2SJed Brown try: 695179860b2SJed Brown import signal 696179860b2SJed Brown import time 697179860b2SJed Brown 698179860b2SJed Brown idleTime = time.time() - self.lastAccess 699179860b2SJed Brown self.writeLogLine('SERVER: Last access '+str(self.lastAccess)) 700179860b2SJed Brown self.writeLogLine('SERVER: Idle time '+str(idleTime)) 701179860b2SJed Brown if idleTime < RDict.shutdownDelay: 702179860b2SJed Brown self.writeLogLine('SERVER: Extending shutdown timer for '+str(pid)+' by '+str(RDict.shutdownDelay - idleTime)+' seconds') 703179860b2SJed Brown self.shutdownTimer = None 704179860b2SJed Brown self.serverShutdown(pid, RDict.shutdownDelay - idleTime) 705179860b2SJed Brown else: 706179860b2SJed Brown self.writeLogLine('SERVER: Killing server '+str(pid)) 707179860b2SJed Brown os.kill(pid, signal.SIGTERM) 7085b6bfdb9SJed Brown except Exception as e: 709179860b2SJed Brown self.writeLogLine('SERVER: Exception killing server: '+str(e)) 710179860b2SJed Brown return 711179860b2SJed Brown 712179860b2SJed Brownif __name__ == '__main__': 713179860b2SJed Brown import sys 714179860b2SJed Brown try: 715179860b2SJed Brown if len(sys.argv) < 2: 7165b6bfdb9SJed Brown print('RDict.py [server | client | clear | insert | remove] [parent]') 717179860b2SJed Brown else: 718179860b2SJed Brown action = sys.argv[1] 719179860b2SJed Brown parent = None 720179860b2SJed Brown if len(sys.argv) > 2: 721179860b2SJed Brown if not sys.argv[2] == 'None': parent = sys.argv[2] 722179860b2SJed Brown if action == 'server': 723179860b2SJed Brown RDict(parentDirectory = parent).serve() 724179860b2SJed Brown elif action == 'client': 7255b6bfdb9SJed Brown print('Entries in server dictionary') 726179860b2SJed Brown rdict = RDict(parentDirectory = parent) 727179860b2SJed Brown for key in rdict.types(): 728179860b2SJed Brown if not key.startswith('cacheKey') and not key.startswith('stamp-'): 7295b6bfdb9SJed Brown print(str(key)+' '+str(rdict.getType(key))) 730179860b2SJed Brown elif action == 'cacheClient': 7315b6bfdb9SJed Brown print('Cache entries in server dictionary') 732179860b2SJed Brown rdict = RDict(parentDirectory = parent) 733179860b2SJed Brown for key in rdict.types(): 734179860b2SJed Brown if key.startswith('cacheKey'): 7355b6bfdb9SJed Brown print(str(key)+' '+str(rdict.getType(key))) 736179860b2SJed Brown elif action == 'stampClient': 7375b6bfdb9SJed Brown print('Stamp entries in server dictionary') 738179860b2SJed Brown rdict = RDict(parentDirectory = parent) 739179860b2SJed Brown for key in rdict.types(): 740179860b2SJed Brown if key.startswith('stamp-'): 7415b6bfdb9SJed Brown print(str(key)+' '+str(rdict.getType(key))) 742179860b2SJed Brown elif action == 'clear': 7435b6bfdb9SJed Brown print('Clearing all dictionaries') 744179860b2SJed Brown RDict(parentDirectory = parent).clear() 745179860b2SJed Brown elif action == 'insert': 746179860b2SJed Brown rdict = RDict(parentDirectory = parent) 747179860b2SJed Brown rdict[sys.argv[3]] = sys.argv[4] 748179860b2SJed Brown elif action == 'remove': 749179860b2SJed Brown rdict = RDict(parentDirectory = parent) 750179860b2SJed Brown del rdict[sys.argv[3]] 751179860b2SJed Brown else: 752179860b2SJed Brown sys.exit('Unknown action: '+action) 7535b6bfdb9SJed Brown except Exception as e: 754179860b2SJed Brown import traceback 7555b6bfdb9SJed Brown print(traceback.print_tb(sys.exc_info()[2])) 756179860b2SJed Brown sys.exit(str(e)) 757179860b2SJed Brown sys.exit(0) 758