bug detection for Storage, reverted improvements in dal :-(

This commit is contained in:
mdipierro
2012-08-22 10:48:17 -05:00
parent 180ae1027f
commit b7623af28c
3 changed files with 129 additions and 74 deletions
+1 -1
View File
@@ -1 +1 @@
Version 2.00.0 (2012-08-22 08:40:52) dev Version 2.00.0 (2012-08-22 10:48:10) dev
+23 -9
View File
@@ -6281,9 +6281,16 @@ class Row(dict):
this is only used to store a Row this is only used to store a Row
""" """
def __init__(self,*args,**kwargs): # IF NOT HAVE BUG http://bugs.python.org/issue1469629 uncommend these lines and comment __getattr__, __setattr__
dict.__init__(self,*args,**kwargs) # def __init__(self,*args,**kwargs):
self.__dict__ = self # dict.__init__(self,*args,**kwargs)
# self.__dict__ = self
def __getattr__(self, key):
return self[key]
def __setattr__(self, key, value):
self[key] = value
def __getitem__(self, key): def __getitem__(self, key):
key=str(key) key=str(key)
@@ -6572,7 +6579,7 @@ class DAL(dict):
:fake_migrate_all (defaults to False). If sets to True fake migrates ALL tables :fake_migrate_all (defaults to False). If sets to True fake migrates ALL tables
:attempts (defaults to 5). Number of times to attempt connecting :attempts (defaults to 5). Number of times to attempt connecting
""" """
self.__dict__ = self # self.__dict__ = self # http://bugs.python.org/issue1469629
if not decode_credentials: if not decode_credentials:
credential_decoder = lambda cred: cred credential_decoder = lambda cred: cred
else: else:
@@ -6983,8 +6990,7 @@ def index():
yield self[tablename] yield self[tablename]
def __getitem__(self, key): def __getitem__(self, key):
key = str(key) return self.__getattr__(str(key))
return dict.__getitem__(self,key)
def __getattr__(self, key): def __getattr__(self, key):
if not key is '_LAZY_TABLES' and key in self._LAZY_TABLES: if not key is '_LAZY_TABLES' and key in self._LAZY_TABLES:
@@ -6999,7 +7005,9 @@ def index():
if key[:1]!='_' and key in self: if key[:1]!='_' and key in self:
raise SyntaxError, \ raise SyntaxError, \
'Object %s exists and cannot be redefined' % key 'Object %s exists and cannot be redefined' % key
dict.__setattr__(self,key,value) dict.__setitem__(self,key,value)
# replace above line with below if not have bug http://bugs.python.org/issue1469629
# dict.__setattr__(self,key,value)
def __repr__(self): def __repr__(self):
return '<DAL ' + dict.__repr__(self) + '>' return '<DAL ' + dict.__repr__(self) + '>'
@@ -7201,7 +7209,7 @@ class Table(dict):
:raises SyntaxError: when a supplied field is of incorrect type. :raises SyntaxError: when a supplied field is of incorrect type.
""" """
self.__dict__ = self # self.__dict__ = self # http://bugs.python.org/issue1469629
self._actual = False # set to True by define_table() self._actual = False # set to True by define_table()
self._tablename = tablename self._tablename = tablename
self._sequence_name = args.get('sequence_name',None) or \ self._sequence_name = args.get('sequence_name',None) or \
@@ -7463,6 +7471,10 @@ class Table(dict):
'value must be a dictionary: %s' % value 'value must be a dictionary: %s' % value
dict.__setitem__(self, str(key), value) dict.__setitem__(self, str(key), value)
# comment if not have bug http://bugs.python.org/issue1469629
def __getattr__(self, key):
return self[key]
def __delitem__(self, key): def __delitem__(self, key):
if isinstance(key, dict): if isinstance(key, dict):
query = self._build_query(key) query = self._build_query(key)
@@ -7474,7 +7486,9 @@ class Table(dict):
def __setattr__(self, key, value): def __setattr__(self, key, value):
if key[:1]!='_' and key in self: if key[:1]!='_' and key in self:
raise SyntaxError, 'Object exists and cannot be redefined: %s' % key raise SyntaxError, 'Object exists and cannot be redefined: %s' % key
dict.__setattr__(self,key,value) self[key] = value
# replace with line below if have bug http://bugs.python.org/issue1469629
# dict.__setattr__(self,key,value)
def __iter__(self): def __iter__(self):
for fieldname in self.fields: for fieldname in self.fields:
+105 -64
View File
@@ -18,58 +18,25 @@ import portalocker
__all__ = ['List', 'Storage', 'Settings', 'Messages', __all__ = ['List', 'Storage', 'Settings', 'Messages',
'StorageList', 'load_storage', 'save_storage'] 'StorageList', 'load_storage', 'save_storage']
def have_python_bug_1469629():
class List(list):
""" """
Like a regular python list but a[i] if i is out of bounds return None http://bugs.python.org/issue1469629
instead of IndexOutOfBounds
because of this bug class Storage is slower on Python < 2.7.3
""" """
import weakref
class Test(dict):
def __init__(self):
self.__dict__ = self
s = Test()
w = weakref.ref(s)
del s
return w() is not None
def __call__(self, i, default=None, cast=None, otherwise=None): HAVE_PYTHON_BUG_1469629 = have_python_bug_1469629()
"""
request.args(0,default=0,cast=int,otherwise='http://error_url')
request.args(0,default=0,cast=int,otherwise=lambda:...)
"""
n = len(self)
if 0<=i<n or -n<=i<0:
value = self[i]
else:
value = default
if cast:
try:
value = cast(value)
except (ValueError, TypeError):
from http import HTTP, redirect
if otherwise is None:
raise HTTP(404)
elif isinstance(otherwise,str):
redirect(otherwise)
elif callable(otherwise):
return otherwise()
else:
raise RuntimeError, "invalid otherwise"
return value
class Storage(dict):
"""
A Storage object is like a dictionary except `obj.foo` can be used
in addition to `obj['foo']`, and setting obj.foo = None deletes item foo.
>>> o = Storage(a=1) class NewStorage(dict):
>>> print o.a
1
>>> o['a']
1
>>> o.a = 2
>>> print o['a']
2
>>> del o.a
>>> print o.a
"""
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
dict.__init__(self, *args, **kwargs) dict.__init__(self, *args, **kwargs)
self.__dict__ = self self.__dict__ = self
@@ -92,6 +59,54 @@ class Storage(dict):
def update(self, *args, **kwargs): def update(self, *args, **kwargs):
dict.__init__(self, *args, **kwargs) dict.__init__(self, *args, **kwargs)
self.__dict__ = self self.__dict__ = self
class OldStorage(dict):
def __getattr__(self, key):
return dict.get(self, key, None)
def __setattr__(self, key, value):
if value is None:
if key in self:
del self[key]
else:
self[key] = value
def __delattr__(self, key):
if key in self:
del self[key]
else:
raise AttributeError, "missing key=%s" % key
def __getitem__(self, key):
return dict.get(self, key, None)
def __repr__(self):
return '<Storage %s>' + dict.__repr__(self)
def __getstate__(self):
return dict(self)
def __setstate__(self, value):
for (k, v) in value.items():
self[k] = v
class Storage(OldStorage if HAVE_PYTHON_BUG_1469629 else NewStorage):
"""
A Storage object is like a dictionary except `obj.foo` can be used
in addition to `obj['foo']`, and setting obj.foo = None deletes item foo.
>>> o = Storage(a=1)
>>> print o.a
1
>>> o['a']
1
>>> o.a = 2
>>> print o['a']
2
>>> del o.a
>>> print o.a
None
"""
def getlist(self,key): def getlist(self,key):
""" """
Return a Storage value as a list. Return a Storage value as a list.
@@ -112,7 +127,7 @@ class Storage(dict):
>>> request.vars.getlist('z') >>> request.vars.getlist('z')
[] []
""" """
value = getattr(self,key,[]) value = self.get(key,[])
return value if not value else \ return value if not value else \
value if isinstance(value,(list,tuple)) else [value] value if isinstance(value,(list,tuple)) else [value]
def getfirst(self,key,default=None): def getfirst(self,key,default=None):
@@ -133,7 +148,7 @@ class Storage(dict):
'abc' 'abc'
>>> request.vars.getfirst('z') >>> request.vars.getfirst('z')
""" """
values = self.getlist(default) values = self.getlist(key)
return values[0] if values else default return values[0] if values else default
def getlast(self,key,default=None): def getlast(self,key,default=None):
""" """
@@ -154,8 +169,8 @@ class Storage(dict):
'def' 'def'
>>> request.vars.getlast('z') >>> request.vars.getlast('z')
""" """
values = self.getlist(default) values = self.getlist(key)
return values[0] if values else default return values[-1] if values else default
PICKABLE = (str,int,long,float,bool,list,dict,tuple,set) PICKABLE = (str,int,long,float,bool,list,dict,tuple,set)
@@ -190,31 +205,57 @@ def save_storage(storage, filename):
finally: finally:
if fp: fp.close() if fp: fp.close()
setter = Storage.__setitem__ if HAVE_PYTHON_BUG_1469629 else Storage.__setattr__
getter = Storage.__getitem__ if HAVE_PYTHON_BUG_1469629 else Storage.__getattr__
class Settings(Storage): class Settings(Storage):
def __setattr__(self, key, value): def __setattr__(self, key, value):
if key != 'lock_keys' and self.lock_keys and not key in self: if key != 'lock_keys' and 'lock_keys' in self and not key in self:
raise SyntaxError, 'setting key \'%s\' does not exist' % key raise SyntaxError, 'setting key \'%s\' does not exist' % key
if key != 'lock_values' and self.lock_values: if key != 'lock_values' and 'lock_values' in self:
raise SyntaxError, 'setting value cannot be changed: %s' % key raise SyntaxError, 'setting value cannot be changed: %s' % key
Storage.__setattr__(self,key,value) setter(self,key,value)
class Messages(Storage): class Messages(Settings):
def __init__(self, T): def __init__(self, T):
Storage.__init__(self,T=T) Storage.__init__(self,T=T)
def __setattr__(self, key, value):
if key != 'lock_keys' and self.lock_keys and not key in self:
raise SyntaxError, 'setting key \'%s\' does not exist' % key
if key != 'lock_values' and self.lock_values:
raise SyntaxError, 'setting value cannot be changed: %s' % key
Storage.__setattr__(self,key,value)
def __getattr__(self, key): def __getattr__(self, key):
value = Storage.__getattr__(self,key) value = getter(self,key)
if isinstance(value, str): if isinstance(value, str):
return str(self.T(value)) return str(self.T(value))
return value return value
class List(list):
"""
Like a regular python list but a[i] if i is out of bounds return None
instead of IndexOutOfBounds
"""
def __call__(self, i, default=None, cast=None, otherwise=None):
"""
request.args(0,default=0,cast=int,otherwise='http://error_url')
request.args(0,default=0,cast=int,otherwise=lambda:...)
"""
n = len(self)
if 0<=i<n or -n<=i<0:
value = self[i]
else:
value = default
if cast:
try:
value = cast(value)
except (ValueError, TypeError):
from http import HTTP, redirect
if otherwise is None:
raise HTTP(404)
elif isinstance(otherwise,str):
redirect(otherwise)
elif callable(otherwise):
return otherwise()
else:
raise RuntimeError, "invalid otherwise"
return value
if __name__ == '__main__': if __name__ == '__main__':
import doctest import doctest