smartdict.py revision 1736
1# Copyright (c) 2005 The Regents of The University of Michigan 2# All rights reserved. 3# 4# Redistribution and use in source and binary forms, with or without 5# modification, are permitted provided that the following conditions are 6# met: redistributions of source code must retain the above copyright 7# notice, this list of conditions and the following disclaimer; 8# redistributions in binary form must reproduce the above copyright 9# notice, this list of conditions and the following disclaimer in the 10# documentation and/or other materials provided with the distribution; 11# neither the name of the copyright holders nor the names of its 12# contributors may be used to endorse or promote products derived from 13# this software without specific prior written permission. 14# 15# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 18# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 19# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 21# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 27# The SmartDict class fixes a couple of issues with using the content 28# of os.environ or similar dicts of strings as Python variables: 29# 30# 1) Undefined variables should return False rather than raising KeyError. 31# 32# 2) String values of 'False', '0', etc., should evaluate to False 33# (not just the empty string). 34# 35# #1 is solved by overriding __getitem__, and #2 is solved by using a 36# proxy class for values and overriding __nonzero__ on the proxy. 37# Everything else is just to (a) make proxies behave like normal 38# values otherwise, (b) make sure any dict operation returns a proxy 39# rather than a normal value, and (c) coerce values written to the 40# dict to be strings. 41 42 43from convert import * 44 45class Variable(str): 46 """Intelligent proxy class for SmartDict. Variable will use the 47 various convert functions to attempt to convert values to useable 48 types""" 49 def __int__(self): 50 return toInteger(str(self)) 51 def __long__(self): 52 return toLong(str(self)) 53 def __float__(self): 54 return toFloat(str(self)) 55 def __nonzero__(self): 56 return toBool(str(self)) 57 def convert(self, other): 58 t = type(other) 59 if t == bool: 60 return bool(self) 61 if t == int: 62 return int(self) 63 if t == long: 64 return long(self) 65 if t == float: 66 return float(self) 67 return str(self) 68 def __lt__(self, other): 69 return self.convert(other) < other 70 def __le__(self, other): 71 return self.convert(other) <= other 72 def __eq__(self, other): 73 return self.convert(other) == other 74 def __ne__(self, other): 75 return self.convert(other) != other 76 def __gt__(self, other): 77 return self.convert(other) > other 78 def __ge__(self, other): 79 return self.convert(other) >= other 80 81 def __add__(self, other): 82 return self.convert(other) + other 83 def __sub__(self, other): 84 return self.convert(other) - other 85 def __mul__(self, other): 86 return self.convert(other) * other 87 def __div__(self, other): 88 return self.convert(other) / other 89 def __truediv__(self, other): 90 return self.convert(other) / other 91 92 def __radd__(self, other): 93 return other + self.convert(other) 94 def __rsub__(self, other): 95 return other - self.convert(other) 96 def __rmul__(self, other): 97 return other * self.convert(other) 98 def __rdiv__(self, other): 99 return other / self.convert(other) 100 def __rtruediv__(self, other): 101 return other / self.convert(other) 102 103class UndefinedVariable(object): 104 """Placeholder class to represent undefined variables. Will 105 generally cause an exception whenever it is used, but evaluates to 106 zero for boolean truth testing such as in an if statement""" 107 def __nonzero__(self): 108 return False 109 110class SmartDict(dict): 111 """Dictionary class that holds strings, but intelligently converts 112 those strings to other types depending on their usage""" 113 114 def __getitem__(self, key): 115 """returns a Variable proxy if the values exists in the database and 116 returns an UndefinedVariable otherwise""" 117 118 if key in self: 119 return Variable(dict.get(self, key)) 120 else: 121 # Note that this does *not* change the contents of the dict, 122 # so that even after we call env['foo'] we still get a 123 # meaningful answer from "'foo' in env" (which 124 # calls dict.__contains__, which we do not override). 125 return UndefinedVariable() 126 127 def __setitem__(self, key, item): 128 """intercept the setting of any variable so that we always 129 store strings in the dict""" 130 dict.__setitem__(self, key, str(item)) 131 132 def values(self): 133 return [ Variable(v) for v in dict.values(self) ] 134 135 def itervalues(self): 136 for value in dict.itervalues(self): 137 yield Variable(value) 138 139 def items(self): 140 return [ (k, Variable(v)) for k,v in dict.items(self) ] 141 142 def iteritems(self): 143 for key,value in dict.iteritems(self): 144 yield key, Variable(value) 145 146 def get(self, key, default='False'): 147 return Variable(dict.get(self, key, str(default))) 148 149 def setdefault(self, key, default='False'): 150 return Variable(dict.setdefault(self, key, str(default))) 151 152__all__ = [ 'SmartDict' ] 153