smartdict.py (13714:35636064b7a1) smartdict.py (13719:74853963ddcf)
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# Authors: Nathan Binkert
28
29# The SmartDict class fixes a couple of issues with using the content
30# of os.environ or similar dicts of strings as Python variables:
31#
32# 1) Undefined variables should return False rather than raising KeyError.
33#
34# 2) String values of 'False', '0', etc., should evaluate to False
35# (not just the empty string).
36#
37# #1 is solved by overriding __getitem__, and #2 is solved by using a
38# proxy class for values and overriding __nonzero__ on the proxy.
39# Everything else is just to (a) make proxies behave like normal
40# values otherwise, (b) make sure any dict operation returns a proxy
41# rather than a normal value, and (c) coerce values written to the
42# dict to be strings.
43
44from __future__ import print_function
45from __future__ import absolute_import
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# Authors: Nathan Binkert
28
29# The SmartDict class fixes a couple of issues with using the content
30# of os.environ or similar dicts of strings as Python variables:
31#
32# 1) Undefined variables should return False rather than raising KeyError.
33#
34# 2) String values of 'False', '0', etc., should evaluate to False
35# (not just the empty string).
36#
37# #1 is solved by overriding __getitem__, and #2 is solved by using a
38# proxy class for values and overriding __nonzero__ on the proxy.
39# Everything else is just to (a) make proxies behave like normal
40# values otherwise, (b) make sure any dict operation returns a proxy
41# rather than a normal value, and (c) coerce values written to the
42# dict to be strings.
43
44from __future__ import print_function
45from __future__ import absolute_import
46import six
47if six.PY3:
48 long = int
46
47from .convert import *
48from .attrdict import attrdict
49
50class Variable(str):
51 """Intelligent proxy class for SmartDict. Variable will use the
52 various convert functions to attempt to convert values to useable
53 types"""
54 def __int__(self):
55 return toInteger(str(self))
56 def __long__(self):
57 return toLong(str(self))
58 def __float__(self):
59 return toFloat(str(self))
60 def __bool__(self):
61 return toBool(str(self))
62 # Python 2.7 uses __nonzero__ instead of __bool__
63 __nonzero__ = __bool__
64 def convert(self, other):
65 t = type(other)
66 if t == bool:
67 return bool(self)
68 if t == int:
69 return int(self)
70 if t == long:
71 return long(self)
72 if t == float:
73 return float(self)
74 return str(self)
75 def __lt__(self, other):
76 return self.convert(other) < other
77 def __le__(self, other):
78 return self.convert(other) <= other
79 def __eq__(self, other):
80 return self.convert(other) == other
81 def __ne__(self, other):
82 return self.convert(other) != other
83 def __gt__(self, other):
84 return self.convert(other) > other
85 def __ge__(self, other):
86 return self.convert(other) >= other
87
88 def __add__(self, other):
89 return self.convert(other) + other
90 def __sub__(self, other):
91 return self.convert(other) - other
92 def __mul__(self, other):
93 return self.convert(other) * other
94 def __div__(self, other):
95 return self.convert(other) / other
96 def __truediv__(self, other):
97 return self.convert(other) / other
98
99 def __radd__(self, other):
100 return other + self.convert(other)
101 def __rsub__(self, other):
102 return other - self.convert(other)
103 def __rmul__(self, other):
104 return other * self.convert(other)
105 def __rdiv__(self, other):
106 return other / self.convert(other)
107 def __rtruediv__(self, other):
108 return other / self.convert(other)
109
110class UndefinedVariable(object):
111 """Placeholder class to represent undefined variables. Will
112 generally cause an exception whenever it is used, but evaluates to
113 zero for boolean truth testing such as in an if statement"""
114 def __bool__(self):
115 return False
116
117 # Python 2.7 uses __nonzero__ instead of __bool__
118 __nonzero__ = __bool__
119
120class SmartDict(attrdict):
121 """Dictionary class that holds strings, but intelligently converts
122 those strings to other types depending on their usage"""
123
124 def __getitem__(self, key):
125 """returns a Variable proxy if the values exists in the database and
126 returns an UndefinedVariable otherwise"""
127
128 if key in self:
129 return Variable(dict.get(self, key))
130 else:
131 # Note that this does *not* change the contents of the dict,
132 # so that even after we call env['foo'] we still get a
133 # meaningful answer from "'foo' in env" (which
134 # calls dict.__contains__, which we do not override).
135 return UndefinedVariable()
136
137 def __setitem__(self, key, item):
138 """intercept the setting of any variable so that we always
139 store strings in the dict"""
140 dict.__setitem__(self, key, str(item))
141
142 def values(self):
143 for value in dict.values(self):
144 yield Variable(value)
145
146 def items(self):
147 for key,value in dict.items(self):
148 yield key, Variable(value)
149
150 def get(self, key, default='False'):
151 return Variable(dict.get(self, key, str(default)))
152
153 def setdefault(self, key, default='False'):
154 return Variable(dict.setdefault(self, key, str(default)))
155
156__all__ = [ 'SmartDict' ]
49
50from .convert import *
51from .attrdict import attrdict
52
53class Variable(str):
54 """Intelligent proxy class for SmartDict. Variable will use the
55 various convert functions to attempt to convert values to useable
56 types"""
57 def __int__(self):
58 return toInteger(str(self))
59 def __long__(self):
60 return toLong(str(self))
61 def __float__(self):
62 return toFloat(str(self))
63 def __bool__(self):
64 return toBool(str(self))
65 # Python 2.7 uses __nonzero__ instead of __bool__
66 __nonzero__ = __bool__
67 def convert(self, other):
68 t = type(other)
69 if t == bool:
70 return bool(self)
71 if t == int:
72 return int(self)
73 if t == long:
74 return long(self)
75 if t == float:
76 return float(self)
77 return str(self)
78 def __lt__(self, other):
79 return self.convert(other) < other
80 def __le__(self, other):
81 return self.convert(other) <= other
82 def __eq__(self, other):
83 return self.convert(other) == other
84 def __ne__(self, other):
85 return self.convert(other) != other
86 def __gt__(self, other):
87 return self.convert(other) > other
88 def __ge__(self, other):
89 return self.convert(other) >= other
90
91 def __add__(self, other):
92 return self.convert(other) + other
93 def __sub__(self, other):
94 return self.convert(other) - other
95 def __mul__(self, other):
96 return self.convert(other) * other
97 def __div__(self, other):
98 return self.convert(other) / other
99 def __truediv__(self, other):
100 return self.convert(other) / other
101
102 def __radd__(self, other):
103 return other + self.convert(other)
104 def __rsub__(self, other):
105 return other - self.convert(other)
106 def __rmul__(self, other):
107 return other * self.convert(other)
108 def __rdiv__(self, other):
109 return other / self.convert(other)
110 def __rtruediv__(self, other):
111 return other / self.convert(other)
112
113class UndefinedVariable(object):
114 """Placeholder class to represent undefined variables. Will
115 generally cause an exception whenever it is used, but evaluates to
116 zero for boolean truth testing such as in an if statement"""
117 def __bool__(self):
118 return False
119
120 # Python 2.7 uses __nonzero__ instead of __bool__
121 __nonzero__ = __bool__
122
123class SmartDict(attrdict):
124 """Dictionary class that holds strings, but intelligently converts
125 those strings to other types depending on their usage"""
126
127 def __getitem__(self, key):
128 """returns a Variable proxy if the values exists in the database and
129 returns an UndefinedVariable otherwise"""
130
131 if key in self:
132 return Variable(dict.get(self, key))
133 else:
134 # Note that this does *not* change the contents of the dict,
135 # so that even after we call env['foo'] we still get a
136 # meaningful answer from "'foo' in env" (which
137 # calls dict.__contains__, which we do not override).
138 return UndefinedVariable()
139
140 def __setitem__(self, key, item):
141 """intercept the setting of any variable so that we always
142 store strings in the dict"""
143 dict.__setitem__(self, key, str(item))
144
145 def values(self):
146 for value in dict.values(self):
147 yield Variable(value)
148
149 def items(self):
150 for key,value in dict.items(self):
151 yield key, Variable(value)
152
153 def get(self, key, default='False'):
154 return Variable(dict.get(self, key, str(default)))
155
156 def setdefault(self, key, default='False'):
157 return Variable(dict.setdefault(self, key, str(default)))
158
159__all__ = [ 'SmartDict' ]