Package gluon :: Module storage
[hide private]
[frames] | no frames]

Source Code for Module gluon.storage

  1  #!/usr/bin/env python 
  2  # -*- coding: utf-8 -*- 
  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   
23 -class Storage(dict):
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 # http://stackoverflow.com/questions/5247250/why-does-pickle-getstate-accept-as-a-return-value-the-very-instance-it-requi 50 __getstate__ = lambda self: None 51 __copy__ = lambda self: Storage(self) 52
53 - def getlist(self, key):
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
79 - def getfirst(self, key, default=None):
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
125 -class StorageList(Storage):
126 """ 127 like Storage but missing elements default to [] instead of None 128 """
129 - def __getitem__(self, key):
130 return self.__getattr__(key)
131
132 - def __getattr__(self, key):
133 if key in self: 134 return getattr(self, key) 135 else: 136 r = [] 137 setattr(self, key, r) 138 return r
139 140
141 -def load_storage(filename):
142 fp = None 143 try: 144 fp = portalocker.LockedFile(filename, 'rb') 145 storage = cPickle.load(fp) 146 finally: 147 if fp: 148 fp.close() 149 return Storage(storage)
150 151
152 -def save_storage(storage, filename):
153 fp = None 154 try: 155 fp = portalocker.LockedFile(filename, 'wb') 156 cPickle.dump(dict(storage), fp) 157 finally: 158 if fp: 159 fp.close()
160 161
162 -class Settings(Storage):
163 - def __setattr__(self, key, value):
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
171 -class Messages(Settings):
172 - def __init__(self, T):
173 Storage.__init__(self, T=T)
174
175 - def __getattr__(self, key):
176 value = self[key] 177 if isinstance(value, str): 178 return str(self.T(value)) 179 return value
180 181
182 -class FastStorage(dict):
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 """
217 - def __init__(self, *args, **kwargs):
218 dict.__init__(self, *args, **kwargs) 219 self.__dict__ = self
220
221 - def __getattr__(self, key):
222 return getattr(self, key) if key in self else None
223
224 - def __getitem__(self, key):
225 return dict.get(self, key, None)
226
227 - def copy(self):
228 self.__dict__ = {} 229 s = FastStorage(self) 230 self.__dict__ = self 231 return s
232
233 - def __repr__(self):
234 return '<Storage %s>' % dict.__repr__(self)
235
236 - def __getstate__(self):
237 return dict(self)
238
239 - def __setstate__(self, sdict):
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
248 -class List(list):
249 """ 250 Like a regular python list but a[i] if i is out of bounds return None 251 instead of IndexOutOfBounds 252 """ 253
254 - def __call__(self, i, default=DEFAULT, cast=None, otherwise=None):
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