From db1bda8f5b369534c6149b36ccaf281433dc90d5 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Tue, 21 Aug 2012 08:52:06 -0500 Subject: [PATCH 01/14] fixed backward compatibility issue formstyles, thanks Dominic --- VERSION | 2 +- applications/admin/controllers/default.py | 1 + gluon/sqlhtml.py | 5 +++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/VERSION b/VERSION index 3c6269ed..b193c7ac 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-08-21 07:57:05) dev +Version 2.00.0 (2012-08-21 08:52:02) dev diff --git a/applications/admin/controllers/default.py b/applications/admin/controllers/default.py index 16e09491..cec8bef9 100644 --- a/applications/admin/controllers/default.py +++ b/applications/admin/controllers/default.py @@ -1379,6 +1379,7 @@ def errors(): return dict(errors = [x[1] for x in decorated], app=app, method=method, db_ready=db_ready) + elif method == 'dbnew': errors_path = apath('%s/errors' % app, r=request) tk_db, tk_table = get_ticket_storage(app) diff --git a/gluon/sqlhtml.py b/gluon/sqlhtml.py index 91d5cfb6..09b7508e 100644 --- a/gluon/sqlhtml.py +++ b/gluon/sqlhtml.py @@ -1086,7 +1086,8 @@ class SQLFORM(FORM): if callable(self.formstyle): # backward compatibility, 4 argument function is the old style - if len(inspect.getargspec(self.formstyle)[0]) == 4: + args, varargs, keywords, defaults = inspect.getargspec(self.formstyle) + if defaults and len(args) - len(defaults) == 4 or len(args) == 4: table = TABLE() for id,a,b,c in xfields: raw_b = self.field_parent[id] = b @@ -1815,7 +1816,7 @@ class SQLFORM(FORM): filename = '.'.join(('rows', oExp.file_ext)) response.headers['Content-Type'] = oExp.content_type response.headers['Content-Disposition'] = \ - 'attachment;filename='+filename+';' + 'attachment;filename='+filename+';' raise HTTP(200, oExp.export(),**response.headers) elif request.vars.records and not isinstance( From 1f60d8f9f449bb41b7a0a5d1d8935144098ab616 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Tue, 21 Aug 2012 08:53:39 -0500 Subject: [PATCH 02/14] better bootstrap formstyle, thanks Dulakian --- VERSION | 2 +- gluon/sqlhtml.py | 38 ++++++++++++++++++++++++++++++-------- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/VERSION b/VERSION index b193c7ac..7d172e9a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-08-21 08:52:02) dev +Version 2.00.0 (2012-08-21 08:53:36) dev diff --git a/gluon/sqlhtml.py b/gluon/sqlhtml.py index 09b7508e..8b04597f 100644 --- a/gluon/sqlhtml.py +++ b/gluon/sqlhtml.py @@ -687,18 +687,40 @@ def formstyle_ul(form, fields): def formstyle_bootstrap(form, fields): ''' bootstrap format form layout ''' form['_class'] = 'form-horizontal' - table = FIELDSET() + parent = FIELDSET() for id, label, controls, help in fields: - if isinstance(controls, (INPUT, SELECT, TEXTAREA)): + # wrappers + _help = SPAN(help, _class='help-inline') + # embed _help into _controls + _controls = DIV(controls, _help, _class='controls') + # submit unflag by default + _submit = False + + if isinstance(controls, INPUT): controls['_class'] = 'input-xlarge' + if controls['_type'] == 'submit': + # flag submit button + _submit = True + controls['_class'] = 'btn btn-primary' + + if isinstance(controls, SELECT): + controls['_class'] = 'input-xlarge' + + if isinstance(controls, TEXTAREA): + controls['_class'] = 'input-xlarge' + if isinstance(label, LABEL): label['_class'] = 'control-label' - # styles - _help = DIV(help, _class='help-block') - # embed _help into _controls don't wrap label - _controls = DIV(controls, _help, _class='controls') - table.append(DIV(label, _controls, _class='control-group',_id=id)) - return table + + if _submit: + # submit button has unwrapped label and controls, different class + parent.append(DIV(label, controls, _class='form-actions')) + # unflag submit (possible side effect) + _submit = False + else: + # unwrapped label + parent.append(DIV(label, _controls, _class='control-group')) + return parent class SQLFORM(FORM): From e84ec7dd8f475aad1504b90b329c1ae657d18160 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Tue, 21 Aug 2012 08:55:41 -0500 Subject: [PATCH 03/14] more options in response.stream, thanks Alan --- VERSION | 2 +- gluon/globals.py | 28 ++++++++++++++++++++++++---- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/VERSION b/VERSION index 7d172e9a..e3399f1b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-08-21 08:53:36) dev +Version 2.00.0 (2012-08-21 08:55:37) dev diff --git a/gluon/globals.py b/gluon/globals.py index 80b312c2..f8f8abd4 100644 --- a/gluon/globals.py +++ b/gluon/globals.py @@ -274,6 +274,8 @@ class Response(Storage): stream, chunk_size = DEFAULT_CHUNK_SIZE, request=None, + attachment=False, + filename=None ): """ if a controller function:: @@ -281,7 +283,28 @@ class Response(Storage): return response.stream(file, 100) the file content will be streamed at 100 bytes at the time + + Optional kwargs: + (for custom stream calls) + attachment=True # Send as attachment. Usually creates a + # pop-up download window on browsers + filename=None # The name for the attachment + + Note: for using the stream name (filename) with attachments + the option must be explicitly set as function parameter(will + default to the last request argument otherwise) """ + + # for attachment settings and backward compatibility + keys = [item.lower() for item in self.headers] + if attachment: + if filename is None: + attname = "" + else: + attname = filename + self.headers["Content-Disposition"] = \ + "attachment;filename=%s" % attname + if not request: request = current.request if isinstance(stream, (str, unicode)): @@ -291,12 +314,9 @@ class Response(Storage): headers=self.headers) # ## the following is for backward compatibility - if hasattr(stream, 'name'): filename = stream.name - else: - filename = None - keys = [item.lower() for item in self.headers] + if filename and not 'content-type' in keys: self.headers['Content-Type'] = contenttype(filename) if filename and not 'content-length' in keys: From ed485d38abe90e2c8f6578252d69fa9ebd4b98c1 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Tue, 21 Aug 2012 09:06:21 -0500 Subject: [PATCH 04/14] timezone support in dal, assumes local time is gmt time --- VERSION | 2 +- gluon/dal.py | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/VERSION b/VERSION index e3399f1b..b7270891 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-08-21 08:55:37) dev +Version 2.00.0 (2012-08-21 09:06:18) dev diff --git a/gluon/dal.py b/gluon/dal.py index a1fdfa87..188b2550 100644 --- a/gluon/dal.py +++ b/gluon/dal.py @@ -1702,6 +1702,16 @@ class BaseAdapter(ConnectionPool): def parse_datetime(self, value, field_type): if not isinstance(value, datetime.datetime): + if '+' in value: + value,tz = value.split('+') + h,m = tz.split(':') + dt = datetime.timedelta(seconds=3600*int(h)+60*int(m)) + elif '-' in value: + value,tz = value.split('-') + h,m = tz.split(':') + dt = -datetime.timedelta(seconds=3600*int(h)+60*int(m)) + else: + dt = None date_part, time_part = ( str(value).replace('T',' ')+' ').split(' ',1) (y, m, d) = map(int,date_part.split('-')) @@ -1710,6 +1720,8 @@ class BaseAdapter(ConnectionPool): time_items = map(int,time_parts) (h, mi, s) = time_items value = datetime.datetime(y, m, d, h, mi, s) + if dt: + value = value + dt return value def parse_blob(self, value, field_type): From b5c8941f58a13f66718865a0af7d5e7bca607c43 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Tue, 21 Aug 2012 09:52:23 -0500 Subject: [PATCH 05/14] fixed common field concatenation --- VERSION | 2 +- gluon/dal.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index b7270891..9ffcfd3d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-08-21 09:06:18) dev +Version 2.00.0 (2012-08-21 09:52:19) dev diff --git a/gluon/dal.py b/gluon/dal.py index 188b2550..4be920b8 100644 --- a/gluon/dal.py +++ b/gluon/dal.py @@ -6954,7 +6954,7 @@ def index(): **args ): if self._common_fields: - fields = fields + self._common_fields + fields = list(fields) + list(self._common_fields) table_class = args.get('table_class',Table) table = table_class(self, tablename, *fields, **args) From f15f6683a27f5a1cdfbcbe9a4674f1ce0115d961 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Tue, 21 Aug 2012 11:34:55 -0500 Subject: [PATCH 06/14] new Storage class, thanks Oscar Benjamin --- VERSION | 2 +- gluon/storage.py | 145 ++++++++++++++++++++--------------------------- 2 files changed, 61 insertions(+), 86 deletions(-) diff --git a/VERSION b/VERSION index 9ffcfd3d..12d7552a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-08-21 09:52:19) dev +Version 2.00.0 (2012-08-21 11:34:52) dev diff --git a/gluon/storage.py b/gluon/storage.py index b4b73c56..15568aef 100644 --- a/gluon/storage.py +++ b/gluon/storage.py @@ -25,7 +25,11 @@ class List(list): instead of IndexOutOfBounds """ - def __call__(self, i, default=None, cast=None, url_onerror=None): + 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>> del o.a >>> print o.a - None - + """ - - 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 __init__(self, *args, **kwargs): + self.__dict__ = self + dict.__init__(self, *args, **kwargs) + def __getattr__(self,key): + return getattr(self,key) if key in self else None + def __getitem__(self,key): + return dict.get(self,key,None) + def copy(self): + return Storage(self) def __repr__(self): - return '' - - def __getstate__(self): - return dict(self) - - def __setstate__(self, value): - for (k, v) in value.items(): - self[k] = v - - def getlist(self, key): - """Return a Storage value as a list. + return '' % self.__dict__ + def getlist(self,key): + """ + Return a Storage value as a list. If the value is a list it will be returned as-is. If object is None, an empty list will be returned. @@ -112,17 +100,13 @@ class Storage(dict): ['abc', 'def'] >>> request.vars.getlist('z') [] - """ - value = self.get(key, None) - if isinstance(value, (list, tuple)): - return value - elif value is None: - return [] - return [value] - - def getfirst(self, key): - """Return the first or only value when given a request.vars-style key. + value = getattr(self,key,[]) + return value if not value else \ + value if isinstance(value,(list,tuple)) else [value] + def getfirst(self,key,default=None): + """ + Return the first or only value when given a request.vars-style key. If the value is a list, its first item will be returned; otherwise, the value will be returned as-is. @@ -137,15 +121,13 @@ class Storage(dict): >>> request.vars.getfirst('y') 'abc' >>> request.vars.getfirst('z') - """ - value = self.getlist(key) - if len(value): - return value[0] - return None - - def getlast(self, key): - """Returns the last or only single value when given a request.vars-style key. + values = self.getlist(default) + return values[0] if values else default + def getlast(self,key,default=None): + """ + Returns the last or only single value when + given a request.vars-style key. If the value is a list, the last item will be returned; otherwise, the value will be returned as-is. @@ -160,33 +142,30 @@ class Storage(dict): >>> request.vars.getlast('y') 'def' >>> request.vars.getlast('z') - """ - value = self.getlist(key) - if len(value): - return value[-1] - return None + values = self.getlist(default) + return values[0] if values else default - def __getinitargs__(self): - return () + +# import _abcoll +# _abcoll.MutableMapping.register(Storage) - def __getnewargs__(self): - return () PICKABLE = (str,int,long,float,bool,list,dict,tuple,set) -def PickleableStorage(data): - return Storage(dict((k,v) for (k,v) in data.items() if isinstance(v,PICKABLE))) class StorageList(Storage): """ like Storage but missing elements default to [] instead of None """ + def __getitem__(self,key): + return self.__gteattr__(key) def __getattr__(self, key): if key in self: - return self[key] + return getattr(self,key) else: - self[key] = [] - return self[key] + r = [] + setattr(self,key,r) + return r def load_storage(filename): fp = None @@ -197,7 +176,6 @@ def load_storage(filename): if fp: fp.close() return Storage(storage) - def save_storage(storage, filename): fp = None try: @@ -207,35 +185,31 @@ def save_storage(storage, filename): if fp: fp.close() class Settings(Storage): - def __setattr__(self, key, value): - if key != 'lock_keys' and self.get('lock_keys', None)\ - and not key in self: + 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.get('lock_values', None): + if key != 'lock_values' and self.lock_values: raise SyntaxError, 'setting value cannot be changed: %s' % key - self[key] = value - + Storage.__setattr__(self,key,value) class Messages(Storage): - def __init__(self, T): - self['T'] = T + self.T = T def __setattr__(self, key, value): - if key != 'lock_keys' and self.get('lock_keys', None)\ - and not key in self: + 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.get('lock_values', None): + if key != 'lock_values' and self.lock_values: raise SyntaxError, 'setting value cannot be changed: %s' % key - self[key] = value + Storage.__setattr__(self,key,value) def __getattr__(self, key): - value = self[key] + value = Storage.__getattr__(self,key) if isinstance(value, str): - return str(self['T'](value)) + return str(self.T(value)) return value + if __name__ == '__main__': import doctest doctest.testmod() @@ -244,3 +218,4 @@ if __name__ == '__main__': + From a64b95f12eabd0cf99d1937a59dc069cd2760975 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Tue, 21 Aug 2012 11:39:27 -0500 Subject: [PATCH 07/14] fixed unwanted membershin in wiki, thanks Alan --- VERSION | 2 +- gluon/tools.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index 12d7552a..ebb7c609 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-08-21 11:34:52) dev +Version 2.00.0 (2012-08-21 11:39:24) dev diff --git a/gluon/tools.py b/gluon/tools.py index 7ebeae8e..a3cd5a0b 100644 --- a/gluon/tools.py +++ b/gluon/tools.py @@ -4519,7 +4519,7 @@ class Wiki(object): if tag: db.wiki_tag.insert(name=tag,wiki_page=page.id) db.wiki_page._after_insert.append(update_tags_insert) db.wiki_page._after_update.append(update_tags_update) - if check_credentials(current.request) and \ + if auth.user and check_credentials(current.request) and \ not 'wiki_editor' in auth.user_groups.values(): group = db.auth_group(role='wiki_editor') gid = group.id if group else db.auth_group.insert(role='wiki_editor') From ffc98f26be7ebee4fd036ee17f70f93066d24ee5 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Tue, 21 Aug 2012 12:42:04 -0500 Subject: [PATCH 08/14] Fixed issue 834, thanks Paolo --- VERSION | 2 +- applications/welcome/static/css/web2py.css | 2 +- gluon/sqlhtml.py | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/VERSION b/VERSION index ebb7c609..31569821 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-08-21 11:39:24) dev +Version 2.00.0 (2012-08-21 12:41:59) dev diff --git a/applications/welcome/static/css/web2py.css b/applications/welcome/static/css/web2py.css index 13f24237..2ac5c115 100644 --- a/applications/welcome/static/css/web2py.css +++ b/applications/welcome/static/css/web2py.css @@ -294,7 +294,7 @@ div.error { margin-bottom:18px; } -.web2py_breadcrumbs ul li { +li.w2p_grid_breadcrumb_elem { display:inline-block; } diff --git a/gluon/sqlhtml.py b/gluon/sqlhtml.py index 8b04597f..a8a709c3 100644 --- a/gluon/sqlhtml.py +++ b/gluon/sqlhtml.py @@ -2182,12 +2182,12 @@ class SQLFORM(FORM): LI(A(T(db[referee]._plural), _class=trap_class(), _href=url()), - SPAN(divider,_class='divider'))) + SPAN(divider,_class='divider'),_class='w2p_grid_breadcrumb_elem')) if kwargs.get('details',True): breadcrumbs.append( LI(A(name,_class=trap_class(), _href=url(args=['view',referee,id])), - SPAN(divider,_class='divider'))) + SPAN(divider,_class='divider'),_class='w2p_grid_breadcrumb_elem')) nargs+=2 else: break @@ -2235,7 +2235,7 @@ class SQLFORM(FORM): if isinstance(grid,DIV): header = table._plural + (field and ' for '+field.name or '') breadcrumbs.append(LI(A(T(header),_class=trap_class(), - _href=url()),_class='active')) + _href=url()),_class='active w2p_grid_breadcrumb_elem')) grid.insert(0,DIV(UL(*breadcrumbs, **{'_class':breadcrumbs_class}), _class='web2py_breadcrumbs')) return grid From c6c417af725368d1e48dbb73c863f0389915084e Mon Sep 17 00:00:00 2001 From: mdipierro Date: Tue, 21 Aug 2012 13:38:27 -0500 Subject: [PATCH 09/14] reverted Storage again. :-( --- VERSION | 2 +- gluon/storage.py | 145 +++++++++++++++++++++++++++-------------------- 2 files changed, 86 insertions(+), 61 deletions(-) diff --git a/VERSION b/VERSION index 31569821..d800c35b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-08-21 12:41:59) dev +Version 2.00.0 (2012-08-21 13:38:21) dev diff --git a/gluon/storage.py b/gluon/storage.py index 15568aef..b4b73c56 100644 --- a/gluon/storage.py +++ b/gluon/storage.py @@ -25,11 +25,7 @@ class List(list): 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:...) - """ + def __call__(self, i, default=None, cast=None, url_onerror=None): n = len(self) if 0<=i>> del o.a >>> print o.a - + None + """ - def __init__(self, *args, **kwargs): - self.__dict__ = self - dict.__init__(self, *args, **kwargs) - def __getattr__(self,key): - return getattr(self,key) if key in self else None - def __getitem__(self,key): - return dict.get(self,key,None) - def copy(self): - return Storage(self) + + 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 '' % self.__dict__ - def getlist(self,key): - """ - Return a Storage value as a list. + return '' + + def __getstate__(self): + return dict(self) + + def __setstate__(self, value): + for (k, v) in value.items(): + self[k] = v + + def getlist(self, key): + """Return a Storage value as a list. If the value is a list it will be returned as-is. If object is None, an empty list will be returned. @@ -100,13 +112,17 @@ class Storage(dict): ['abc', 'def'] >>> request.vars.getlist('z') [] + """ - value = getattr(self,key,[]) - return value if not value else \ - value if isinstance(value,(list,tuple)) else [value] - def getfirst(self,key,default=None): - """ - Return the first or only value when given a request.vars-style key. + value = self.get(key, None) + if isinstance(value, (list, tuple)): + return value + elif value is None: + return [] + return [value] + + def getfirst(self, key): + """Return the first or only value when given a request.vars-style key. If the value is a list, its first item will be returned; otherwise, the value will be returned as-is. @@ -121,13 +137,15 @@ class Storage(dict): >>> request.vars.getfirst('y') 'abc' >>> request.vars.getfirst('z') + """ - values = self.getlist(default) - return values[0] if values else default - def getlast(self,key,default=None): - """ - Returns the last or only single value when - given a request.vars-style key. + value = self.getlist(key) + if len(value): + return value[0] + return None + + def getlast(self, key): + """Returns the last or only single value when given a request.vars-style key. If the value is a list, the last item will be returned; otherwise, the value will be returned as-is. @@ -142,30 +160,33 @@ class Storage(dict): >>> request.vars.getlast('y') 'def' >>> request.vars.getlast('z') + """ - values = self.getlist(default) - return values[0] if values else default + value = self.getlist(key) + if len(value): + return value[-1] + return None - -# import _abcoll -# _abcoll.MutableMapping.register(Storage) + def __getinitargs__(self): + return () + def __getnewargs__(self): + return () PICKABLE = (str,int,long,float,bool,list,dict,tuple,set) +def PickleableStorage(data): + return Storage(dict((k,v) for (k,v) in data.items() if isinstance(v,PICKABLE))) class StorageList(Storage): """ like Storage but missing elements default to [] instead of None """ - def __getitem__(self,key): - return self.__gteattr__(key) def __getattr__(self, key): if key in self: - return getattr(self,key) + return self[key] else: - r = [] - setattr(self,key,r) - return r + self[key] = [] + return self[key] def load_storage(filename): fp = None @@ -176,6 +197,7 @@ def load_storage(filename): if fp: fp.close() return Storage(storage) + def save_storage(storage, filename): fp = None try: @@ -185,31 +207,35 @@ def save_storage(storage, filename): if fp: fp.close() class Settings(Storage): + def __setattr__(self, key, value): - if key != 'lock_keys' and self.lock_keys and not key in self: + if key != 'lock_keys' and self.get('lock_keys', None)\ + and not key in self: raise SyntaxError, 'setting key \'%s\' does not exist' % key - if key != 'lock_values' and self.lock_values: + if key != 'lock_values' and self.get('lock_values', None): raise SyntaxError, 'setting value cannot be changed: %s' % key - Storage.__setattr__(self,key,value) + self[key] = value + class Messages(Storage): + def __init__(self, T): - self.T = T + self['T'] = T def __setattr__(self, key, value): - if key != 'lock_keys' and self.lock_keys and not key in self: + if key != 'lock_keys' and self.get('lock_keys', None)\ + and not key in self: raise SyntaxError, 'setting key \'%s\' does not exist' % key - if key != 'lock_values' and self.lock_values: + if key != 'lock_values' and self.get('lock_values', None): raise SyntaxError, 'setting value cannot be changed: %s' % key - Storage.__setattr__(self,key,value) + self[key] = value def __getattr__(self, key): - value = Storage.__getattr__(self,key) + value = self[key] if isinstance(value, str): - return str(self.T(value)) + return str(self['T'](value)) return value - if __name__ == '__main__': import doctest doctest.testmod() @@ -218,4 +244,3 @@ if __name__ == '__main__': - From 61a1fb1d58630656d85f485f5195fbd9cd5572cf Mon Sep 17 00:00:00 2001 From: mdipierro Date: Tue, 21 Aug 2012 14:30:02 -0500 Subject: [PATCH 10/14] another attempt at new Storage, thanks Jonathan --- VERSION | 2 +- gluon/globals.py | 4 +- gluon/main.py | 5 +- gluon/restricted.py | 1 + gluon/storage.py | 152 +++++++++++++++++++------------------------- 5 files changed, 73 insertions(+), 91 deletions(-) diff --git a/VERSION b/VERSION index d800c35b..9cafffbc 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-08-21 13:38:21) dev +Version 2.00.0 (2012-08-21 14:29:57) dev diff --git a/gluon/globals.py b/gluon/globals.py index f8f8abd4..23723731 100644 --- a/gluon/globals.py +++ b/gluon/globals.py @@ -83,6 +83,7 @@ class Request(Storage): """ def __init__(self): + Storage.__init__(self) self.wsgi = Storage() # hooks to environ and start_response self.env = Storage() self.cookies = Cookie.SimpleCookie() @@ -100,7 +101,7 @@ class Request(Storage): self.is_https = False self.is_local = False self.global_settings = settings.global_settings - + def compute_uuid(self): self.uuid = '%s/%s.%s.%s' % ( self.application, @@ -165,6 +166,7 @@ class Response(Storage): """ def __init__(self): + Storage.__init__(self) self.status = 200 self.headers = dict() self.headers['X-Powered-By'] = 'web2py' diff --git a/gluon/main.py b/gluon/main.py index 6b4d4b20..5aa0f15a 100644 --- a/gluon/main.py +++ b/gluon/main.py @@ -446,8 +446,9 @@ def wsgibase(environ, responder): elif not request.is_local and \ os.path.exists(os.path.join(request.folder,'DISABLED')): raise HTTP(503, "

Temporarily down for maintenance

") - request.url = Url(r=request, args=request.args, - extension=request.raw_extension) + request.url = Url(r=request, + args=request.args, + extension=request.raw_extension) # ################################################## # build missing folders diff --git a/gluon/restricted.py b/gluon/restricted.py index fee54841..770fe48e 100644 --- a/gluon/restricted.py +++ b/gluon/restricted.py @@ -33,6 +33,7 @@ class TicketStorage(Storage): db=None, tablename='web2py_ticket' ): + Storage.__init__(self) self.db = db self.tablename = tablename diff --git a/gluon/storage.py b/gluon/storage.py index b4b73c56..afa32ca6 100644 --- a/gluon/storage.py +++ b/gluon/storage.py @@ -13,9 +13,8 @@ Provides: """ import cPickle -import portalocker -__all__ = ['List', 'Storage', 'Settings', 'Messages', 'PickleableStorage', +__all__ = ['List', 'Storage', 'Settings', 'Messages', 'StorageList', 'load_storage', 'save_storage'] @@ -25,7 +24,11 @@ class List(list): instead of IndexOutOfBounds """ - def __call__(self, i, default=None, cast=None, url_onerror=None): + 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>> del o.a >>> print o.a - None - + """ - - 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 __init__(self, *args, **kwargs): + self.__dict__ = self + dict.__init__(self, *args, **kwargs) + def __getattr__(self,key): + return getattr(self,key) if key in self else None + def __getitem__(self,key): + return dict.get(self,key,None) + def copy(self): + self.__dict__ = {} + s = Storage(self) + self.__dict__ = self + return s def __repr__(self): - return '' - + return '' % dict.__repr__(self) def __getstate__(self): return dict(self) - - def __setstate__(self, value): - for (k, v) in value.items(): - self[k] = v - - def getlist(self, key): - """Return a Storage value as a list. + def __setstate__(self, sdict): + dict.__init__(self, sdict) + def update(self, *args, **kwargs): + dict.__init__(self, *args, **kwargs) + def getlist(self,key): + """ + Return a Storage value as a list. If the value is a list it will be returned as-is. If object is None, an empty list will be returned. @@ -112,17 +108,13 @@ class Storage(dict): ['abc', 'def'] >>> request.vars.getlist('z') [] - """ - value = self.get(key, None) - if isinstance(value, (list, tuple)): - return value - elif value is None: - return [] - return [value] - - def getfirst(self, key): - """Return the first or only value when given a request.vars-style key. + value = getattr(self,key,[]) + return value if not value else \ + value if isinstance(value,(list,tuple)) else [value] + def getfirst(self,key,default=None): + """ + Return the first or only value when given a request.vars-style key. If the value is a list, its first item will be returned; otherwise, the value will be returned as-is. @@ -137,15 +129,13 @@ class Storage(dict): >>> request.vars.getfirst('y') 'abc' >>> request.vars.getfirst('z') - """ - value = self.getlist(key) - if len(value): - return value[0] - return None - - def getlast(self, key): - """Returns the last or only single value when given a request.vars-style key. + values = self.getlist(default) + return values[0] if values else default + def getlast(self,key,default=None): + """ + Returns the last or only single value when + given a request.vars-style key. If the value is a list, the last item will be returned; otherwise, the value will be returned as-is. @@ -160,33 +150,25 @@ class Storage(dict): >>> request.vars.getlast('y') 'def' >>> request.vars.getlast('z') - """ - value = self.getlist(key) - if len(value): - return value[-1] - return None - - def __getinitargs__(self): - return () - - def __getnewargs__(self): - return () + values = self.getlist(default) + return values[0] if values else default PICKABLE = (str,int,long,float,bool,list,dict,tuple,set) -def PickleableStorage(data): - return Storage(dict((k,v) for (k,v) in data.items() if isinstance(v,PICKABLE))) class StorageList(Storage): """ like Storage but missing elements default to [] instead of None """ + def __getitem__(self,key): + return self.__gteattr__(key) def __getattr__(self, key): if key in self: - return self[key] + return getattr(self,key) else: - self[key] = [] - return self[key] + r = [] + setattr(self,key,r) + return r def load_storage(filename): fp = None @@ -197,7 +179,6 @@ def load_storage(filename): if fp: fp.close() return Storage(storage) - def save_storage(storage, filename): fp = None try: @@ -207,35 +188,31 @@ def save_storage(storage, filename): if fp: fp.close() class Settings(Storage): - def __setattr__(self, key, value): - if key != 'lock_keys' and self.get('lock_keys', None)\ - and not key in self: + 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.get('lock_values', None): + if key != 'lock_values' and self.lock_values: raise SyntaxError, 'setting value cannot be changed: %s' % key - self[key] = value - + Storage.__setattr__(self,key,value) class Messages(Storage): - def __init__(self, T): - self['T'] = T + Storage.__init__(self,T=T) def __setattr__(self, key, value): - if key != 'lock_keys' and self.get('lock_keys', None)\ - and not key in self: + 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.get('lock_values', None): + if key != 'lock_values' and self.lock_values: raise SyntaxError, 'setting value cannot be changed: %s' % key - self[key] = value + Storage.__setattr__(self,key,value) def __getattr__(self, key): - value = self[key] + value = Storage.__getattr__(self,key) if isinstance(value, str): - return str(self['T'](value)) + return str(self.T(value)) return value + if __name__ == '__main__': import doctest doctest.testmod() @@ -244,3 +221,4 @@ if __name__ == '__main__': + From 83dcaf6313bb711c43b0b909e0b3762a2e192687 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Tue, 21 Aug 2012 14:41:43 -0500 Subject: [PATCH 11/14] new Storage passes all tests --- VERSION | 2 +- gluon/storage.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index 9cafffbc..15d5dd07 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-08-21 14:29:57) dev +Version 2.00.0 (2012-08-21 14:41:40) dev diff --git a/gluon/storage.py b/gluon/storage.py index afa32ca6..9ec86b37 100644 --- a/gluon/storage.py +++ b/gluon/storage.py @@ -70,8 +70,8 @@ class Storage(dict): """ def __init__(self, *args, **kwargs): - self.__dict__ = self dict.__init__(self, *args, **kwargs) + self.__dict__ = self def __getattr__(self,key): return getattr(self,key) if key in self else None def __getitem__(self,key): @@ -87,8 +87,10 @@ class Storage(dict): return dict(self) def __setstate__(self, sdict): dict.__init__(self, sdict) + self.__dict__ = self def update(self, *args, **kwargs): dict.__init__(self, *args, **kwargs) + self.__dict__ = self def getlist(self,key): """ Return a Storage value as a list. From 4f0daa5479513fa7d549e737a97747744721c418 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Tue, 21 Aug 2012 15:56:53 -0500 Subject: [PATCH 12/14] Table, DAL and Row use self.__dict__=self --- VERSION | 2 +- gluon/dal.py | 24 ++++++++---------------- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/VERSION b/VERSION index 15d5dd07..34d6208e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-08-21 14:41:40) dev +Version 2.00.0 (2012-08-21 15:56:50) dev diff --git a/gluon/dal.py b/gluon/dal.py index 4be920b8..a6248424 100644 --- a/gluon/dal.py +++ b/gluon/dal.py @@ -6281,6 +6281,9 @@ class Row(dict): this is only used to store a Row """ + def __init__(self): + self.__dict__ = self + def __getitem__(self, key): key=str(key) m = regex_table_field.match(key) @@ -6299,12 +6302,6 @@ class Row(dict): def __setitem__(self, key, value): dict.__setitem__(self, str(key), value) - def __getattr__(self, key): - return self[key] - - def __setattr__(self, key, value): - self[key] = value - def __str__(self): ### this could be made smarter return '' @@ -6574,6 +6571,7 @@ class DAL(dict): :fake_migrate_all (defaults to False). If sets to True fake migrates ALL tables :attempts (defaults to 5). Number of times to attempt connecting """ + self.__dict__ = self if not decode_credentials: credential_decoder = lambda cred: cred else: @@ -6993,14 +6991,11 @@ def index(): def __setitem__(self, key, value): dict.__setitem__(self, str(key), value) - def __getattr__(self, key): - return self[key] - def __setattr__(self, key, value): if key[:1]!='_' and key in self: raise SyntaxError, \ 'Object %s exists and cannot be redefined' % key - self[key] = value + dict.__setattr__(self,key,value) def __repr__(self): return '' @@ -7202,7 +7197,7 @@ class Table(dict): :raises SyntaxError: when a supplied field is of incorrect type. """ - + self.__dict__ = self self._actual = False # set to True by define_table() self._tablename = tablename self._sequence_name = args.get('sequence_name',None) or \ @@ -7273,7 +7268,7 @@ class Table(dict): tmp = field.uploadfield = '%s_blob' % field.name if isinstance(field.uploadfield,str) and \ not [f for f in fields if f.name==field.uploadfield]: - fields.append(self._db.Field(field.uploadfield,'blob',default='')) + fields.append(Field(field.uploadfield,'blob',default='')) lower_fieldnames = set() reserved = dir(Table) + ['fields'] @@ -7472,13 +7467,10 @@ class Table(dict): elif not str(key).isdigit() or not self._db(self._id == key).delete(): raise SyntaxError, 'No such record: %s' % key - def __getattr__(self, key): - return self[key] - def __setattr__(self, key, value): if key[:1]!='_' and key in self: raise SyntaxError, 'Object exists and cannot be redefined: %s' % key - self[key] = value + dict.__setattr__(self,key,value) def __iter__(self): for fieldname in self.fields: From 2c08f4396bee22491ffee336ad1117e1d828bf79 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Tue, 21 Aug 2012 17:42:01 -0500 Subject: [PATCH 13/14] fixed a problem with lazy tables and self.__dict__=self magic --- VERSION | 2 +- gluon/dal.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 34d6208e..3918e707 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-08-21 15:56:50) dev +Version 2.00.0 (2012-08-21 17:41:57) dev diff --git a/gluon/dal.py b/gluon/dal.py index a6248424..1b503353 100644 --- a/gluon/dal.py +++ b/gluon/dal.py @@ -6983,6 +6983,9 @@ def index(): def __getitem__(self, key): key = str(key) + return dict.__getitem__(self,key) + + def __getattr__(self, key): if not key is '_LAZY_TABLES' and key in self._LAZY_TABLES: tablename, fields, args = self._LAZY_TABLES.pop(key) return self.lazy_define_table(tablename,*fields,**args) From 88326a9b4ded64cb09a322028c25d4a6afd7d0cf Mon Sep 17 00:00:00 2001 From: mdipierro Date: Tue, 21 Aug 2012 18:20:07 -0500 Subject: [PATCH 14/14] fixed a bug in Row, recently introduced --- VERSION | 2 +- gluon/dal.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index 3918e707..e786178e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-08-21 17:41:57) dev +Version 2.00.0 (2012-08-21 18:20:02) dev diff --git a/gluon/dal.py b/gluon/dal.py index 1b503353..a8d9c296 100644 --- a/gluon/dal.py +++ b/gluon/dal.py @@ -6281,7 +6281,8 @@ class Row(dict): this is only used to store a Row """ - def __init__(self): + def __init__(self,*args,**kwargs): + dict.__init__(self,*args,**kwargs) self.__dict__ = self def __getitem__(self, key):