Package ckbot :: Module resolver
[hide private]
[frames] | no frames]

Source Code for Module ckbot.resolver

  1  import os, time 
  2  from yaml import safe_load, safe_dump 
  3   
4 -class Resolver(object):
5 """ 6 The resolver class implements a persistent two-way mapping, 7 typically between strings. 8 9 Persistence is ensured by using a YAML file to store the mapping 10 11 Resolver maps both keys to values and values to keys. 12 13 If asked to map an unknown key or value, the code treats this 14 as a value and auto-generates a key using a hash function. 15 16 EXAMPLE OF USE: 17 >>> r = Resolver("parrot.yml") 18 >>> r.get('parrot','is dead') 19 'is dead' 20 >>> r['met'] = 'its maker' 21 >>> r['met'] 22 'its maker' 23 >>> r.save() 24 >>> r2 = Resolver("parrot.yml") 25 >>> r2['its maker'] 26 'met' 27 """
28 - def __init__(self,fn):
29 """ 30 Resolve using mapping stored in file named fn 31 32 NOTE: the file's directory must be read-write 33 Resolver will save temporary files to this directory 34 """ 35 assert type(fn)==str 36 self.fn = fn 37 self.tbl = {} 38 self.itbl = {}
39
40 - def load( self ):
41 """ 42 Load contents from file storage. 43 44 Silently returns if file is not found / inaccessible 45 """ 46 # Check that we can access the file 47 try: 48 os.stat(self.fn) 49 except OSError: 50 # if stat failed, file doesn't exist or access denied 51 return 52 # Read file and (re-)format as dictionary 53 f = open(self.fn,'r') 54 doc = safe_load(f) 55 f.close() 56 if type(doc) is not dict: 57 raise TypeError("File '%s' does not contain a YAML mapping" % self.fn) 58 self.tbl = doc 59 # Build inverse dict 60 self.itbl = {} 61 for k,v in self.tbl.iteritems(): 62 self.itbl[v] = k
63
64 - def put(self,key,val):
65 """The obvious""" 66 self.tbl[key]=val 67 self.itbl[val]=key 68 return self
69
70 - def _autokey( self, val ):
71 """Automatically generate a key for a value""" 72 return 'obj%04d' % (abs(hash(val)) % 10000) 73 return key
74
75 - def save( self ):
76 """ 77 Save mapping to a YAML file. 78 79 EXAMPLE: 80 >>> r = Resolver('spam') 81 >>> r['ham']='spam' 82 >>> r['banana']='weapon' 83 >>> r.save() 84 85 ALGORITHM: 86 File is first saved to a temporary filename, then the old file 87 is removed and the new file renamed (a.k.a. 'safe save') 88 """ 89 # Temp file name 90 nfn = self.fn + '~' 91 # Write it 92 f = open(nfn,'w') 93 f.write("# Saved at %s\n" % time.asctime() ) 94 safe_dump(self.tbl,f,default_style="!") 95 f.close() 96 # Windows is broken, as if we didn't know... 97 if os.name != 'posix': 98 print "WARNING: you are using an inferior OS that does not support posix rename semantics" 99 print " attempting a workaround..." 100 try: 101 os.remove(self.fn) 102 except OSError: 103 # File might not exist. If not writeable, rename will fail 104 pass 105 # Move new file on top of old 106 os.rename(nfn,self.fn)
107
108 - def __setitem__(self,key,value):
109 """ 110 Set up the two-way mapping key<-->value 111 NOTE: this does not automatically save ! 112 """ 113 self.put(key,value)
114
115 - def get( self, key, default=None ):
116 """ 117 Find key/value associated with key. If not found, return default 118 """ 119 r = self.tbl.get(key,default) 120 if r is default: 121 r = self.itbl.get(key,default) 122 return r
123
124 - def __getitem__(self, key):
125 return self.getWithAuto(key)
126
127 - def getWithAuto(self, key, autokey = None ):
128 """ 129 Find key/value associated with key. 130 If not found: 131 - Parameter is a value 132 - A key is generated by using autokey, or self._autokey(key) if None 133 - The mapping is saved to disk 134 """ 135 if not self.tbl: 136 self.load() 137 tag = [] 138 r = self.get(key,tag) 139 if r is tag: 140 if autokey is None: 141 r = self._autokey(key) 142 else: 143 r = autokey 144 self.put(r,key) 145 self.save() 146 return r
147