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