smartdict.py revision 13697
112837Sgabeblack@google.com# Copyright (c) 2005 The Regents of The University of Michigan 212837Sgabeblack@google.com# All rights reserved. 312837Sgabeblack@google.com# 412837Sgabeblack@google.com# Redistribution and use in source and binary forms, with or without 512837Sgabeblack@google.com# modification, are permitted provided that the following conditions are 612837Sgabeblack@google.com# met: redistributions of source code must retain the above copyright 712837Sgabeblack@google.com# notice, this list of conditions and the following disclaimer; 812837Sgabeblack@google.com# redistributions in binary form must reproduce the above copyright 912837Sgabeblack@google.com# notice, this list of conditions and the following disclaimer in the 1012837Sgabeblack@google.com# documentation and/or other materials provided with the distribution; 1112837Sgabeblack@google.com# neither the name of the copyright holders nor the names of its 1212837Sgabeblack@google.com# contributors may be used to endorse or promote products derived from 1312837Sgabeblack@google.com# this software without specific prior written permission. 1412837Sgabeblack@google.com# 1512837Sgabeblack@google.com# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1612837Sgabeblack@google.com# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1712837Sgabeblack@google.com# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1812837Sgabeblack@google.com# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 1912837Sgabeblack@google.com# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2012837Sgabeblack@google.com# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 2112837Sgabeblack@google.com# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2212837Sgabeblack@google.com# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2312837Sgabeblack@google.com# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2412837Sgabeblack@google.com# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 2512837Sgabeblack@google.com# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2612837Sgabeblack@google.com# 2712837Sgabeblack@google.com# Authors: Nathan Binkert 2812837Sgabeblack@google.com 2912837Sgabeblack@google.com# The SmartDict class fixes a couple of issues with using the content 3012901Sgabeblack@google.com# of os.environ or similar dicts of strings as Python variables: 3112901Sgabeblack@google.com# 3212901Sgabeblack@google.com# 1) Undefined variables should return False rather than raising KeyError. 3312837Sgabeblack@google.com# 3412951Sgabeblack@google.com# 2) String values of 'False', '0', etc., should evaluate to False 3512953Sgabeblack@google.com# (not just the empty string). 3612837Sgabeblack@google.com# 3712951Sgabeblack@google.com# #1 is solved by overriding __getitem__, and #2 is solved by using a 3812837Sgabeblack@google.com# proxy class for values and overriding __nonzero__ on the proxy. 3912952Sgabeblack@google.com# Everything else is just to (a) make proxies behave like normal 4012952Sgabeblack@google.com# values otherwise, (b) make sure any dict operation returns a proxy 4112952Sgabeblack@google.com# rather than a normal value, and (c) coerce values written to the 4212952Sgabeblack@google.com# dict to be strings. 4312952Sgabeblack@google.com 4412952Sgabeblack@google.com 4512952Sgabeblack@google.comfrom convert import * 4612952Sgabeblack@google.comfrom attrdict import attrdict 4712952Sgabeblack@google.com 4812952Sgabeblack@google.comclass Variable(str): 4912952Sgabeblack@google.com """Intelligent proxy class for SmartDict. Variable will use the 5012952Sgabeblack@google.com various convert functions to attempt to convert values to useable 5112952Sgabeblack@google.com types""" 5212952Sgabeblack@google.com def __int__(self): 5312952Sgabeblack@google.com return toInteger(str(self)) 5412952Sgabeblack@google.com def __long__(self): 5512952Sgabeblack@google.com return toLong(str(self)) 5612952Sgabeblack@google.com def __float__(self): 5712952Sgabeblack@google.com return toFloat(str(self)) 5812952Sgabeblack@google.com def __bool__(self): 5912952Sgabeblack@google.com return toBool(str(self)) 6012952Sgabeblack@google.com # Python 2.7 uses __nonzero__ instead of __bool__ 6112952Sgabeblack@google.com __nonzero__ = __bool__ 6212837Sgabeblack@google.com def convert(self, other): 6312837Sgabeblack@google.com t = type(other) 6412837Sgabeblack@google.com if t == bool: 6512951Sgabeblack@google.com return bool(self) 6612951Sgabeblack@google.com if t == int: 6712951Sgabeblack@google.com return int(self) 6812837Sgabeblack@google.com if t == long: 6912951Sgabeblack@google.com return long(self) 7012951Sgabeblack@google.com if t == float: 7112951Sgabeblack@google.com return float(self) 7212837Sgabeblack@google.com return str(self) 7312837Sgabeblack@google.com def __lt__(self, other): 7412837Sgabeblack@google.com return self.convert(other) < other 7512951Sgabeblack@google.com def __le__(self, other): 7612837Sgabeblack@google.com return self.convert(other) <= other 7712837Sgabeblack@google.com def __eq__(self, other): 7812837Sgabeblack@google.com return self.convert(other) == other 7912837Sgabeblack@google.com def __ne__(self, other): 8012837Sgabeblack@google.com return self.convert(other) != other 8112837Sgabeblack@google.com def __gt__(self, other): 8212837Sgabeblack@google.com return self.convert(other) > other 8312837Sgabeblack@google.com def __ge__(self, other): 8412837Sgabeblack@google.com return self.convert(other) >= other 8512837Sgabeblack@google.com 8612837Sgabeblack@google.com def __add__(self, other): 8712837Sgabeblack@google.com return self.convert(other) + other 8812837Sgabeblack@google.com def __sub__(self, other): 8912837Sgabeblack@google.com return self.convert(other) - other 9012837Sgabeblack@google.com def __mul__(self, other): 9112837Sgabeblack@google.com return self.convert(other) * other 9212837Sgabeblack@google.com def __div__(self, other): 9312837Sgabeblack@google.com return self.convert(other) / other 9412837Sgabeblack@google.com def __truediv__(self, other): 9512837Sgabeblack@google.com return self.convert(other) / other 9612837Sgabeblack@google.com 9712837Sgabeblack@google.com def __radd__(self, other): 9812837Sgabeblack@google.com return other + self.convert(other) 9912837Sgabeblack@google.com def __rsub__(self, other): 10012837Sgabeblack@google.com return other - self.convert(other) 10112837Sgabeblack@google.com def __rmul__(self, other): 10212837Sgabeblack@google.com return other * self.convert(other) 10312837Sgabeblack@google.com def __rdiv__(self, other): 10412837Sgabeblack@google.com return other / self.convert(other) 10512837Sgabeblack@google.com def __rtruediv__(self, other): 10612837Sgabeblack@google.com return other / self.convert(other) 10712837Sgabeblack@google.com 10812837Sgabeblack@google.comclass UndefinedVariable(object): 10912837Sgabeblack@google.com """Placeholder class to represent undefined variables. Will 11012837Sgabeblack@google.com generally cause an exception whenever it is used, but evaluates to 11112837Sgabeblack@google.com zero for boolean truth testing such as in an if statement""" 11212837Sgabeblack@google.com def __bool__(self): 11312837Sgabeblack@google.com return False 11412837Sgabeblack@google.com 11512837Sgabeblack@google.com # Python 2.7 uses __nonzero__ instead of __bool__ 11612837Sgabeblack@google.com __nonzero__ = __bool__ 11712837Sgabeblack@google.com 11812837Sgabeblack@google.comclass SmartDict(attrdict): 11912837Sgabeblack@google.com """Dictionary class that holds strings, but intelligently converts 12012837Sgabeblack@google.com those strings to other types depending on their usage""" 12112837Sgabeblack@google.com 12212837Sgabeblack@google.com def __getitem__(self, key): 12312837Sgabeblack@google.com """returns a Variable proxy if the values exists in the database and 12412837Sgabeblack@google.com returns an UndefinedVariable otherwise""" 12512837Sgabeblack@google.com 12612837Sgabeblack@google.com if key in self: 12712837Sgabeblack@google.com return Variable(dict.get(self, key)) 12812837Sgabeblack@google.com else: 12912837Sgabeblack@google.com # Note that this does *not* change the contents of the dict, 13012837Sgabeblack@google.com # so that even after we call env['foo'] we still get a 13112837Sgabeblack@google.com # meaningful answer from "'foo' in env" (which 13212837Sgabeblack@google.com # calls dict.__contains__, which we do not override). 13312837Sgabeblack@google.com return UndefinedVariable() 13412837Sgabeblack@google.com 13512837Sgabeblack@google.com def __setitem__(self, key, item): 13612837Sgabeblack@google.com """intercept the setting of any variable so that we always 13712837Sgabeblack@google.com store strings in the dict""" 13812837Sgabeblack@google.com dict.__setitem__(self, key, str(item)) 13912837Sgabeblack@google.com 14012837Sgabeblack@google.com def values(self): 14112837Sgabeblack@google.com return [ Variable(v) for v in dict.values(self) ] 14212837Sgabeblack@google.com 14312837Sgabeblack@google.com def itervalues(self): 14412837Sgabeblack@google.com for value in dict.itervalues(self): 14512837Sgabeblack@google.com yield Variable(value) 14612837Sgabeblack@google.com 14712837Sgabeblack@google.com def items(self): 14812837Sgabeblack@google.com return [ (k, Variable(v)) for k,v in dict.items(self) ] 14912837Sgabeblack@google.com 15012837Sgabeblack@google.com def iteritems(self): 15112951Sgabeblack@google.com for key,value in dict.iteritems(self): 15212837Sgabeblack@google.com yield key, Variable(value) 15312837Sgabeblack@google.com 15412837Sgabeblack@google.com def get(self, key, default='False'): 15512837Sgabeblack@google.com return Variable(dict.get(self, key, str(default))) 15612837Sgabeblack@google.com 15712951Sgabeblack@google.com def setdefault(self, key, default='False'): 15812837Sgabeblack@google.com return Variable(dict.setdefault(self, key, str(default))) 15912837Sgabeblack@google.com 16012951Sgabeblack@google.com__all__ = [ 'SmartDict' ] 16112952Sgabeblack@google.com