1
2
3
4 """
5 This file is part of the web2py Web Framework
6 Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
7 License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
8
9 Provides:
10
11 - List; like list but returns None instead of IndexOutOfBounds
12 - Storage; like dictionary allowing also for `obj.foo` for `obj['foo']`
13 """
14
15 import cPickle
16 import portalocker
17
18 __all__ = ['List', 'Storage', 'Settings', 'Messages',
19 'StorageList', 'load_storage', 'save_storage']
20
21 DEFAULT = lambda:0
22
24 """
25 A Storage object is like a dictionary except `obj.foo` can be used
26 in addition to `obj['foo']`, and setting obj.foo = None deletes item foo.
27
28 >>> o = Storage(a=1)
29 >>> print o.a
30 1
31
32 >>> o['a']
33 1
34
35 >>> o.a = 2
36 >>> print o['a']
37 2
38
39 >>> del o.a
40 >>> print o.a
41 None
42 """
43 __slots__ = ()
44 __setattr__ = dict.__setitem__
45 __delattr__ = dict.__delitem__
46 __getitem__ = dict.get
47 __getattr__ = dict.get
48 __repr__ = lambda self: '<Storage %s>' % dict.__repr__(self)
49
50 __getstate__ = lambda self: None
51 __copy__ = lambda self: Storage(self)
52
54 """
55 Return a Storage value as a list.
56
57 If the value is a list it will be returned as-is.
58 If object is None, an empty list will be returned.
59 Otherwise, [value] will be returned.
60
61 Example output for a query string of ?x=abc&y=abc&y=def
62 >>> request = Storage()
63 >>> request.vars = Storage()
64 >>> request.vars.x = 'abc'
65 >>> request.vars.y = ['abc', 'def']
66 >>> request.vars.getlist('x')
67 ['abc']
68 >>> request.vars.getlist('y')
69 ['abc', 'def']
70 >>> request.vars.getlist('z')
71 []
72 """
73 value = self.get(key, [])
74 if value is None or isinstance(value, (list, tuple)):
75 return value
76 else:
77 return [value]
78
80 """
81 Return the first or only value when given a request.vars-style key.
82
83 If the value is a list, its first item will be returned;
84 otherwise, the value will be returned as-is.
85
86 Example output for a query string of ?x=abc&y=abc&y=def
87 >>> request = Storage()
88 >>> request.vars = Storage()
89 >>> request.vars.x = 'abc'
90 >>> request.vars.y = ['abc', 'def']
91 >>> request.vars.getfirst('x')
92 'abc'
93 >>> request.vars.getfirst('y')
94 'abc'
95 >>> request.vars.getfirst('z')
96 """
97 values = self.getlist(key)
98 return values[0] if values else default
99
100 - def getlast(self, key, default=None):
101 """
102 Returns the last or only single value when
103 given a request.vars-style key.
104
105 If the value is a list, the last item will be returned;
106 otherwise, the value will be returned as-is.
107
108 Simulated output with a query string of ?x=abc&y=abc&y=def
109 >>> request = Storage()
110 >>> request.vars = Storage()
111 >>> request.vars.x = 'abc'
112 >>> request.vars.y = ['abc', 'def']
113 >>> request.vars.getlast('x')
114 'abc'
115 >>> request.vars.getlast('y')
116 'def'
117 >>> request.vars.getlast('z')
118 """
119 values = self.getlist(key)
120 return values[-1] if values else default
121
122 PICKABLE = (str, int, long, float, bool, list, dict, tuple, set)
123
124
126 """
127 like Storage but missing elements default to [] instead of None
128 """
131
133 if key in self:
134 return getattr(self, key)
135 else:
136 r = []
137 setattr(self, key, r)
138 return r
139
140
150
151
160
161
164 if key != 'lock_keys' and self['lock_keys'] and key not in self:
165 raise SyntaxError('setting key \'%s\' does not exist' % key)
166 if key != 'lock_values' and self['lock_values']:
167 raise SyntaxError('setting value cannot be changed: %s' % key)
168 self[key] = value
169
170
174
176 value = self[key]
177 if isinstance(value, str):
178 return str(self.T(value))
179 return value
180
181
183 """
184 Eventually this should replace class Storage but causes memory leak
185 because of http://bugs.python.org/issue1469629
186
187 >>> s = FastStorage()
188 >>> s.a = 1
189 >>> s.a
190 1
191 >>> s['a']
192 1
193 >>> s.b
194 >>> s['b']
195 >>> s['b']=2
196 >>> s['b']
197 2
198 >>> s.b
199 2
200 >>> isinstance(s,dict)
201 True
202 >>> dict(s)
203 {'a': 1, 'b': 2}
204 >>> dict(FastStorage(s))
205 {'a': 1, 'b': 2}
206 >>> import pickle
207 >>> s = pickle.loads(pickle.dumps(s))
208 >>> dict(s)
209 {'a': 1, 'b': 2}
210 >>> del s.b
211 >>> del s.a
212 >>> s.a
213 >>> s.b
214 >>> s['a']
215 >>> s['b']
216 """
218 dict.__init__(self, *args, **kwargs)
219 self.__dict__ = self
220
222 return getattr(self, key) if key in self else None
223
225 return dict.get(self, key, None)
226
228 self.__dict__ = {}
229 s = FastStorage(self)
230 self.__dict__ = self
231 return s
232
234 return '<Storage %s>' % dict.__repr__(self)
235
238
240 dict.__init__(self, sdict)
241 self.__dict__ = self
242
243 - def update(self, *args, **kwargs):
244 dict.__init__(self, *args, **kwargs)
245 self.__dict__ = self
246
247
249 """
250 Like a regular python list but a[i] if i is out of bounds return None
251 instead of IndexOutOfBounds
252 """
253
255 """
256 request.args(0,default=0,cast=int,otherwise='http://error_url')
257 request.args(0,default=0,cast=int,otherwise=lambda:...)
258 """
259 n = len(self)
260 if 0 <= i < n or -n <= i < 0:
261 value = self[i]
262 elif default is DEFAULT:
263 value = None
264 else:
265 value, cast = default, False
266 if cast:
267 try:
268 value = cast(value)
269 except (ValueError, TypeError):
270 from http import HTTP, redirect
271 if otherwise is None:
272 raise HTTP(404)
273 elif isinstance(otherwise, str):
274 redirect(otherwise)
275 elif callable(otherwise):
276 return otherwise()
277 else:
278 raise RuntimeError("invalid otherwise")
279 return value
280
281
282 if __name__ == '__main__':
283 import doctest
284 doctest.testmod()
285