smartdict.py revision 1606
1# The SmartDict class fixes a couple of issues with using the content 2# of os.environ or similar dicts of strings as Python variables: 3# 4# 1) Undefined variables should return False rather than raising KeyError. 5# 6# 2) String values of 'False', '0', etc., should evaluate to False 7# (not just the empty string). 8# 9# #1 is solved by overriding __getitem__, and #2 is solved by using a 10# proxy class for values and overriding __nonzero__ on the proxy. 11# Everything else is just to (a) make proxies behave like normal 12# values otherwise, (b) make sure any dict operation returns a proxy 13# rather than a normal value, and (c) coerce values written to the 14# dict to be strings. 15 16 17from convert import * 18 19class Variable(str): 20 """Intelligent proxy class for SmartDict. Variable will use the 21 various convert functions to attempt to convert values to useable 22 types""" 23 def __int__(self): 24 return toInteger(str(self)) 25 def __long__(self): 26 return toLong(str(self)) 27 def __float__(self): 28 return toFloat(str(self)) 29 def __nonzero__(self): 30 return toBool(str(self)) 31 def convert(self, other): 32 t = type(other) 33 if t == bool: 34 return bool(self) 35 if t == int: 36 return int(self) 37 if t == long: 38 return long(self) 39 if t == float: 40 return float(self) 41 return str(self) 42 def __lt__(self, other): 43 return self.convert(other) < other 44 def __le__(self, other): 45 return self.convert(other) <= other 46 def __eq__(self, other): 47 return self.convert(other) == other 48 def __ne__(self, other): 49 return self.convert(other) != other 50 def __gt__(self, other): 51 return self.convert(other) > other 52 def __ge__(self, other): 53 return self.convert(other) >= other 54 55 def __add__(self, other): 56 return self.convert(other) + other 57 def __sub__(self, other): 58 return self.convert(other) - other 59 def __mul__(self, other): 60 return self.convert(other) * other 61 def __div__(self, other): 62 return self.convert(other) / other 63 def __truediv__(self, other): 64 return self.convert(other) / other 65 66 def __radd__(self, other): 67 return other + self.convert(other) 68 def __rsub__(self, other): 69 return other - self.convert(other) 70 def __rmul__(self, other): 71 return other * self.convert(other) 72 def __rdiv__(self, other): 73 return other / self.convert(other) 74 def __rtruediv__(self, other): 75 return other / self.convert(other) 76 77class UndefinedVariable(object): 78 """Placeholder class to represent undefined variables. Will 79 generally cause an exception whenever it is used, but evaluates to 80 zero for boolean truth testing such as in an if statement""" 81 def __nonzero__(self): 82 return False 83 84class SmartDict(dict): 85 """Dictionary class that holds strings, but intelligently converts 86 those strings to other types depending on their usage""" 87 88 def __getitem__(self, key): 89 """returns a Variable proxy if the values exists in the database and 90 returns an UndefinedVariable otherwise""" 91 92 if key in self: 93 return Variable(dict.get(self, key)) 94 else: 95 # Note that this does *not* change the contents of the dict, 96 # so that even after we call env['foo'] we still get a 97 # meaningful answer from "'foo' in env" (which 98 # calls dict.__contains__, which we do not override). 99 return UndefinedVariable() 100 101 def __setitem__(self, key, item): 102 """intercept the setting of any variable so that we always 103 store strings in the dict""" 104 dict.__setitem__(self, key, str(item)) 105 106 def values(self): 107 return [ Variable(v) for v in dict.values(self) ] 108 109 def itervalues(self): 110 for value in dict.itervalues(self): 111 yield Variable(value) 112 113 def items(self): 114 return [ (k, Variable(v)) for k,v in dict.items(self) ] 115 116 def iteritems(self): 117 for key,value in dict.iteritems(self): 118 yield key, Variable(value) 119 120 def get(self, key, default='False'): 121 return Variable(dict.get(self, key, str(default))) 122 123 def setdefault(self, key, default='False'): 124 return Variable(dict.setdefault(self, key, str(default))) 125 126__all__ = [ 'SmartDict' ] 127