smartdict.py revision 13714:35636064b7a1
13395Shsul@eecs.umich.edu# Copyright (c) 2005 The Regents of The University of Michigan
23395Shsul@eecs.umich.edu# All rights reserved.
33395Shsul@eecs.umich.edu#
43395Shsul@eecs.umich.edu# Redistribution and use in source and binary forms, with or without
53395Shsul@eecs.umich.edu# modification, are permitted provided that the following conditions are
63395Shsul@eecs.umich.edu# met: redistributions of source code must retain the above copyright
73395Shsul@eecs.umich.edu# notice, this list of conditions and the following disclaimer;
83395Shsul@eecs.umich.edu# redistributions in binary form must reproduce the above copyright
93395Shsul@eecs.umich.edu# notice, this list of conditions and the following disclaimer in the
103395Shsul@eecs.umich.edu# documentation and/or other materials provided with the distribution;
113395Shsul@eecs.umich.edu# neither the name of the copyright holders nor the names of its
123395Shsul@eecs.umich.edu# contributors may be used to endorse or promote products derived from
133395Shsul@eecs.umich.edu# this software without specific prior written permission.
143395Shsul@eecs.umich.edu#
153395Shsul@eecs.umich.edu# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
163395Shsul@eecs.umich.edu# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
173395Shsul@eecs.umich.edu# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
183395Shsul@eecs.umich.edu# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
193395Shsul@eecs.umich.edu# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
203395Shsul@eecs.umich.edu# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
213395Shsul@eecs.umich.edu# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
223395Shsul@eecs.umich.edu# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
233395Shsul@eecs.umich.edu# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
243395Shsul@eecs.umich.edu# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
253395Shsul@eecs.umich.edu# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
263395Shsul@eecs.umich.edu#
273395Shsul@eecs.umich.edu# Authors: Nathan Binkert
283395Shsul@eecs.umich.edu
293395Shsul@eecs.umich.edu# The SmartDict class fixes a couple of issues with using the content
303509Shsul@eecs.umich.edu# of os.environ or similar dicts of strings as Python variables:
313395Shsul@eecs.umich.edu#
323395Shsul@eecs.umich.edu# 1) Undefined variables should return False rather than raising KeyError.
333395Shsul@eecs.umich.edu#
343448Shsul@eecs.umich.edu# 2) String values of 'False', '0', etc., should evaluate to False
353395Shsul@eecs.umich.edu#    (not just the empty string).
363481Shsul@eecs.umich.edu#
373481Shsul@eecs.umich.edu# #1 is solved by overriding __getitem__, and #2 is solved by using a
383481Shsul@eecs.umich.edu# proxy class for values and overriding __nonzero__ on the proxy.
393481Shsul@eecs.umich.edu# Everything else is just to (a) make proxies behave like normal
403481Shsul@eecs.umich.edu# values otherwise, (b) make sure any dict operation returns a proxy
413481Shsul@eecs.umich.edu# rather than a normal value, and (c) coerce values written to the
423681Sktlim@umich.edu# dict to be strings.
433681Sktlim@umich.edu
443681Sktlim@umich.edufrom __future__ import print_function
453481Shsul@eecs.umich.edufrom __future__ import absolute_import
463481Shsul@eecs.umich.edu
473481Shsul@eecs.umich.edufrom .convert import *
483481Shsul@eecs.umich.edufrom .attrdict import attrdict
493481Shsul@eecs.umich.edu
503481Shsul@eecs.umich.educlass Variable(str):
513481Shsul@eecs.umich.edu    """Intelligent proxy class for SmartDict.  Variable will use the
523481Shsul@eecs.umich.edu    various convert functions to attempt to convert values to useable
533481Shsul@eecs.umich.edu    types"""
543481Shsul@eecs.umich.edu    def __int__(self):
553481Shsul@eecs.umich.edu        return toInteger(str(self))
563481Shsul@eecs.umich.edu    def __long__(self):
573481Shsul@eecs.umich.edu        return toLong(str(self))
583481Shsul@eecs.umich.edu    def __float__(self):
593481Shsul@eecs.umich.edu        return toFloat(str(self))
603481Shsul@eecs.umich.edu    def __bool__(self):
613481Shsul@eecs.umich.edu        return toBool(str(self))
623481Shsul@eecs.umich.edu    # Python 2.7 uses __nonzero__ instead of __bool__
633481Shsul@eecs.umich.edu    __nonzero__ = __bool__
643395Shsul@eecs.umich.edu    def convert(self, other):
653395Shsul@eecs.umich.edu        t = type(other)
663395Shsul@eecs.umich.edu        if t == bool:
673395Shsul@eecs.umich.edu            return bool(self)
683395Shsul@eecs.umich.edu        if t == int:
693395Shsul@eecs.umich.edu            return int(self)
703395Shsul@eecs.umich.edu        if t == long:
713511Shsul@eecs.umich.edu            return long(self)
723395Shsul@eecs.umich.edu        if t == float:
733395Shsul@eecs.umich.edu            return float(self)
743395Shsul@eecs.umich.edu        return str(self)
753395Shsul@eecs.umich.edu    def __lt__(self, other):
763395Shsul@eecs.umich.edu        return self.convert(other) < other
773395Shsul@eecs.umich.edu    def __le__(self, other):
783395Shsul@eecs.umich.edu        return self.convert(other) <= other
793395Shsul@eecs.umich.edu    def __eq__(self, other):
803481Shsul@eecs.umich.edu        return self.convert(other) == other
813481Shsul@eecs.umich.edu    def __ne__(self, other):
823481Shsul@eecs.umich.edu        return self.convert(other) != other
833481Shsul@eecs.umich.edu    def __gt__(self, other):
843481Shsul@eecs.umich.edu        return self.convert(other) > other
853481Shsul@eecs.umich.edu    def __ge__(self, other):
863481Shsul@eecs.umich.edu        return self.convert(other) >= other
873481Shsul@eecs.umich.edu
883481Shsul@eecs.umich.edu    def __add__(self, other):
893481Shsul@eecs.umich.edu        return self.convert(other) + other
903481Shsul@eecs.umich.edu    def __sub__(self, other):
913481Shsul@eecs.umich.edu        return self.convert(other) - other
923481Shsul@eecs.umich.edu    def __mul__(self, other):
933481Shsul@eecs.umich.edu        return self.convert(other) * other
943395Shsul@eecs.umich.edu    def __div__(self, other):
953395Shsul@eecs.umich.edu        return self.convert(other) / other
963395Shsul@eecs.umich.edu    def __truediv__(self, other):
973395Shsul@eecs.umich.edu        return self.convert(other) / other
983478Shsul@eecs.umich.edu
993395Shsul@eecs.umich.edu    def __radd__(self, other):
1003478Shsul@eecs.umich.edu        return other + self.convert(other)
1013395Shsul@eecs.umich.edu    def __rsub__(self, other):
1023395Shsul@eecs.umich.edu        return other - self.convert(other)
1033478Shsul@eecs.umich.edu    def __rmul__(self, other):
1043395Shsul@eecs.umich.edu        return other * self.convert(other)
1053395Shsul@eecs.umich.edu    def __rdiv__(self, other):
1063478Shsul@eecs.umich.edu        return other / self.convert(other)
1073395Shsul@eecs.umich.edu    def __rtruediv__(self, other):
1083478Shsul@eecs.umich.edu        return other / self.convert(other)
1093480Shsul@eecs.umich.edu
1103514Sktlim@umich.educlass UndefinedVariable(object):
1113481Shsul@eecs.umich.edu    """Placeholder class to represent undefined variables.  Will
1123480Shsul@eecs.umich.edu    generally cause an exception whenever it is used, but evaluates to
1133480Shsul@eecs.umich.edu    zero for boolean truth testing such as in an if statement"""
1143480Shsul@eecs.umich.edu    def __bool__(self):
1153395Shsul@eecs.umich.edu        return False
1163478Shsul@eecs.umich.edu
1173514Sktlim@umich.edu    # Python 2.7 uses __nonzero__ instead of __bool__
1183514Sktlim@umich.edu    __nonzero__ = __bool__
1193395Shsul@eecs.umich.edu
1203478Shsul@eecs.umich.educlass SmartDict(attrdict):
1213395Shsul@eecs.umich.edu    """Dictionary class that holds strings, but intelligently converts
1223395Shsul@eecs.umich.edu    those strings to other types depending on their usage"""
1233395Shsul@eecs.umich.edu
1243395Shsul@eecs.umich.edu    def __getitem__(self, key):
1253395Shsul@eecs.umich.edu        """returns a Variable proxy if the values exists in the database and
1263395Shsul@eecs.umich.edu        returns an UndefinedVariable otherwise"""
1273395Shsul@eecs.umich.edu
1283395Shsul@eecs.umich.edu        if key in self:
1293395Shsul@eecs.umich.edu            return Variable(dict.get(self, key))
1303395Shsul@eecs.umich.edu        else:
1313395Shsul@eecs.umich.edu            # Note that this does *not* change the contents of the dict,
1323395Shsul@eecs.umich.edu            # so that even after we call env['foo'] we still get a
1333395Shsul@eecs.umich.edu            # meaningful answer from "'foo' in env" (which
1343395Shsul@eecs.umich.edu            # calls dict.__contains__, which we do not override).
1353395Shsul@eecs.umich.edu            return UndefinedVariable()
1363395Shsul@eecs.umich.edu
1373395Shsul@eecs.umich.edu    def __setitem__(self, key, item):
1383395Shsul@eecs.umich.edu        """intercept the setting of any variable so that we always
1393395Shsul@eecs.umich.edu        store strings in the dict"""
1403395Shsul@eecs.umich.edu        dict.__setitem__(self, key, str(item))
1413395Shsul@eecs.umich.edu
1423395Shsul@eecs.umich.edu    def values(self):
1433395Shsul@eecs.umich.edu        for value in dict.values(self):
1443395Shsul@eecs.umich.edu            yield Variable(value)
1453395Shsul@eecs.umich.edu
1463395Shsul@eecs.umich.edu    def items(self):
1473395Shsul@eecs.umich.edu        for key,value in dict.items(self):
1483509Shsul@eecs.umich.edu            yield key, Variable(value)
1493395Shsul@eecs.umich.edu
1503481Shsul@eecs.umich.edu    def get(self, key, default='False'):
1513395Shsul@eecs.umich.edu        return Variable(dict.get(self, key, str(default)))
1523395Shsul@eecs.umich.edu
1533395Shsul@eecs.umich.edu    def setdefault(self, key, default='False'):
1543395Shsul@eecs.umich.edu        return Variable(dict.setdefault(self, key, str(default)))
1553395Shsul@eecs.umich.edu
1563395Shsul@eecs.umich.edu__all__ = [ 'SmartDict' ]
1573395Shsul@eecs.umich.edu