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