diff --git a/gluon/_compat.py b/gluon/_compat.py index f7562b12..a61ddc51 100644 --- a/gluon/_compat.py +++ b/gluon/_compat.py @@ -63,7 +63,7 @@ if PY2: return None if isinstance(obj, (bytes, bytearray, buffer)): return bytes(obj) - if isinstance(obj, unicode): + if hasattr(obj, 'encode'): return obj.encode(charset, errors) raise TypeError('Expected bytes') @@ -122,7 +122,7 @@ else: return None if isinstance(obj, (bytes, bytearray, memoryview)): return bytes(obj) - if isinstance(obj, str): + if hasattr(obj, 'encode'): return obj.encode(charset, errors) raise TypeError('Expected bytes') @@ -151,7 +151,7 @@ def with_metaclass(meta, *bases): def to_unicode(obj, charset='utf-8', errors='strict'): if obj is None: return None - if not isinstance(obj, bytes): + if not hasattr(obj, 'decode'): return text_type(obj) return obj.decode(charset, errors) diff --git a/gluon/compileapp.py b/gluon/compileapp.py index ac9cc491..a59e96d1 100644 --- a/gluon/compileapp.py +++ b/gluon/compileapp.py @@ -205,7 +205,7 @@ def LOAD(c=None, f='index', args=None, vars=None, other_response = Response() other_request.env.path_info = '/' + \ '/'.join([request.application, c, f] + - map(str, other_request.args)) + [str(a) for a in other_request.args]) other_request.env.query_string = \ vars and URL(vars=vars).split('?')[1] or '' other_request.env.http_web2py_component_location = \ @@ -288,7 +288,7 @@ class LoadFactory(object): other_response = globals.Response() other_request.env.path_info = '/' + \ '/'.join([request.application, c, f] + - map(str, other_request.args)) + [str(a) for a in other_request.args]) other_request.env.query_string = \ vars and html.URL(vars=vars).split('?')[1] or '' other_request.env.http_web2py_component_location = \ @@ -678,7 +678,7 @@ def run_view_in(environment): layer = None scode = None if patterns: - regex = re_compile('|'.join(map(fnmatch.translate, patterns))) + regex = re_compile('|'.join(fnmatch.translate(p) for p in patterns)) short_action = '%(controller)s/%(function)s.%(extension)s' % request allow_generic = regex.search(short_action) else: @@ -709,23 +709,22 @@ def run_view_in(environment): ccode = getcfs(compiled, compiled, lambda: read_pyc(compiled)) layer = compiled break - if not os.path.exists(filename) and allow_generic: - view = 'generic.' + request.extension - filename = pjoin(folder, 'views', view) - if not os.path.exists(filename): - raise HTTP(404, - rewrite.THREAD_LOCAL.routes.error_message % badv, - web2py_error=badv) - # if the view is not compiled if not layer: + if not os.path.exists(filename) and allow_generic: + view = 'generic.' + request.extension + filename = pjoin(folder, 'views', view) + if not os.path.exists(filename): + raise HTTP(404, + rewrite.THREAD_LOCAL.routes.error_message % badv, + web2py_error=badv) # Parse template scode = parse_template(view, pjoin(folder, 'views'), context=environment) # Compile template ccode = compile2(scode, filename) - layer = filename + layer = filename restricted(ccode, environment, layer=layer, scode=scode) # parse_template saves everything in response body return environment['response'].body.getvalue() diff --git a/gluon/globals.py b/gluon/globals.py index f9f5013b..cb52d331 100644 --- a/gluon/globals.py +++ b/gluon/globals.py @@ -331,7 +331,7 @@ class Request(Storage): user_agent = session._user_agent if user_agent: return user_agent - http_user_agent = self.env.http_user_agent + http_user_agent = self.env.http_user_agent or '' user_agent = user_agent_parser.detect(http_user_agent) for key, value in user_agent.items(): if isinstance(value, dict): diff --git a/gluon/languages.py b/gluon/languages.py index b626457c..aff3db14 100644 --- a/gluon/languages.py +++ b/gluon/languages.py @@ -18,10 +18,11 @@ import pkgutil import logging from cgi import escape from threading import RLock -from gluon.utf8 import Utf8 + from gluon.utils import local_html_escape from gluon._compat import copyreg, PY2, maketrans, iterkeys, unicodeT, to_unicode, to_bytes, iteritems, to_native, pjoin + from pydal.contrib.portalocker import read_locked, LockedFile from gluon.fileutils import listdir @@ -49,8 +50,10 @@ DEFAULT_CONSTRUCT_PLURAL_FORM = lambda word, plural_id: word if PY2: NUMBERS = (int, long, float) + from gluon.utf8 import Utf8 else: NUMBERS = (int, float) + Utf8 = str # pattern to find T(blah blah blah) expressions PY_STRING_LITERAL_RE = r'(?<=[^\w]T\()(?P'\ diff --git a/gluon/sqlhtml.py b/gluon/sqlhtml.py index 0653c4c7..c4095e44 100644 --- a/gluon/sqlhtml.py +++ b/gluon/sqlhtml.py @@ -17,6 +17,7 @@ Holds: import datetime import urllib import re +import copy import os from gluon._compat import StringIO, unichr, urllib_quote, iteritems, basestring, long, unicodeT, to_native, to_unicode @@ -682,7 +683,7 @@ class AutocompleteWidget(object): else: self.is_reference = False if hasattr(request, 'application'): - urlvars = request.vars + urlvars = copy.copy(request.vars) urlvars[default_var] = 1 self.url = URL(args=request.args, vars=urlvars, user_signature=user_signature, hash_vars=hash_vars) @@ -3570,7 +3571,9 @@ class ExportClass(object): if not self.rows.db._adapter.REGEX_TABLE_DOT_FIELD.match(col): row.append(record._extra[col]) else: - (t, f) = col.split('.') + # The grid code modifies rows.colnames, adding double quotes + # around the table and field names -- so they must be removed here. + (t, f) = [name.strip('"') for name in col.split('.')] field = self.rows.db[t][f] if isinstance(record.get(t, None), (Row, dict)): value = record[t][f] diff --git a/gluon/tests/test_validators.py b/gluon/tests/test_validators.py index e534fbd8..36bf454e 100644 --- a/gluon/tests/test_validators.py +++ b/gluon/tests/test_validators.py @@ -444,6 +444,8 @@ class TestValidators(unittest.TestCase): self.assertEqual(rtn, (None, 'Enter a value')) rtn = IS_NOT_EMPTY()('') self.assertEqual(rtn, ('', 'Enter a value')) + rtn = IS_NOT_EMPTY()(b'') + self.assertEqual(rtn, (b'', 'Enter a value')) rtn = IS_NOT_EMPTY()(' ') self.assertEqual(rtn, (' ', 'Enter a value')) rtn = IS_NOT_EMPTY()(' \n\t') diff --git a/gluon/validators.py b/gluon/validators.py index 1d9dddcf..21de1ce2 100644 --- a/gluon/validators.py +++ b/gluon/validators.py @@ -996,7 +996,7 @@ def is_empty(value, empty_regex=None): value = value.strip() if empty_regex is not None and empty_regex.match(value): value = '' - if value is None or value == '' or value == []: + if value is None or value == '' or value == b'' or value == []: return (_value, True) return (_value, False)