cache (and compile) parse_template in run_view_in, fix #1474

This commit is contained in:
ilvalle
2016-09-25 10:08:26 +02:00
parent 0e4ff55306
commit 576aaf668d
5 changed files with 33 additions and 68 deletions
+1 -1
View File
@@ -409,7 +409,7 @@ def ccache():
import copy import copy
import time import time
import math import math
from gluon import portalocker from pydal.contrib import portalocker
ram = { ram = {
'entries': 0, 'entries': 0,
+18 -50
View File
@@ -430,13 +430,10 @@ def build_environment(request, response, session, store_current=True):
current.T = t current.T = t
current.cache = c current.cache = c
global __builtins__
if is_jython: # jython hack if is_jython: # jython hack
global __builtins__
__builtins__ = mybuiltin() __builtins__ = mybuiltin()
elif is_pypy: # apply the same hack to pypy too
__builtins__ = mybuiltin()
elif PY2:
__builtins__['__import__'] = builtin.__import__ # WHY?
environment['request'] = request environment['request'] = request
environment['response'] = response environment['response'] = response
environment['session'] = session environment['session'] = session
@@ -487,7 +484,7 @@ def compile_views(folder, skip_failed_views=False):
else: else:
raise Exception("%s in %s" % (e, fname)) raise Exception("%s in %s" % (e, fname))
else: else:
filename = ('views/%s.py' % fname).replace('/', '_').replace('\\', '_') filename = 'views.%s.py' % fname.replace(os.path.sep, '.')
filename = pjoin(folder, 'compiled', filename) filename = pjoin(folder, 'compiled', filename)
write_file(filename, data) write_file(filename, data)
save_pyc(filename) save_pyc(filename)
@@ -503,7 +500,7 @@ def compile_models(folder):
path = pjoin(folder, 'models') path = pjoin(folder, 'models')
for fname in listdir(path, '.+\.py$'): for fname in listdir(path, '.+\.py$'):
data = read_file(pjoin(path, fname)) data = read_file(pjoin(path, fname))
modelfile = 'models.'+fname.replace(os.path.sep,'.') modelfile = 'models.'+fname.replace(os.path.sep, '.')
filename = pjoin(folder, 'compiled', modelfile) filename = pjoin(folder, 'compiled', modelfile)
mktree(filename) mktree(filename)
write_file(filename, data) write_file(filename, data)
@@ -582,11 +579,10 @@ def run_models_in(environment):
continue continue
elif compiled: elif compiled:
code = getcfs(model, model, lambda: read_pyc(model)) code = getcfs(model, model, lambda: read_pyc(model))
elif is_gae: else:
code = getcfs(model, model, code = getcfs(model, model,
lambda: compile2(read_file(model), model)) lambda: compile2(read_file(model), model))
else:
code = getcfs(model, model, None)
restricted(code, environment, layer=model) restricted(code, environment, layer=model)
@@ -610,7 +606,6 @@ def run_controller_in(controller, function, environment):
rewrite.THREAD_LOCAL.routes.error_message % badf, rewrite.THREAD_LOCAL.routes.error_message % badf,
web2py_error=badf) web2py_error=badf)
code = getcfs(filename, filename, lambda: read_pyc(filename)) code = getcfs(filename, filename, lambda: read_pyc(filename))
restricted(code, environment, layer=filename)
elif function == '_TEST': elif function == '_TEST':
# TESTING: adjust the path to include site packages # TESTING: adjust the path to include site packages
from gluon.settings import global_settings from gluon.settings import global_settings
@@ -629,7 +624,6 @@ def run_controller_in(controller, function, environment):
environment['__symbols__'] = environment.keys() environment['__symbols__'] = environment.keys()
code = read_file(filename) code = read_file(filename)
code += TEST_CODE code += TEST_CODE
restricted(code, environment, layer=filename)
else: else:
filename = pjoin(folder, 'controllers/%s.py' filename = pjoin(folder, 'controllers/%s.py'
% controller) % controller)
@@ -644,10 +638,10 @@ def run_controller_in(controller, function, environment):
rewrite.THREAD_LOCAL.routes.error_message % badf, rewrite.THREAD_LOCAL.routes.error_message % badf,
web2py_error=badf) web2py_error=badf)
code = "%s\nresponse._vars=response._caller(%s)\n" % (code, function) code = "%s\nresponse._vars=response._caller(%s)\n" % (code, function)
if is_gae: layer = filename + ':' + function
layer = filename + ':' + function code = getcfs(layer, filename, lambda: compile2(code, layer))
code = getcfs(layer, filename, lambda: compile2(code, layer))
restricted(code, environment, filename) restricted(code, environment, layer=filename)
response = current.response response = current.response
vars = response._vars vars = response._vars
if response.postprocessing: if response.postprocessing:
@@ -682,7 +676,7 @@ def run_view_in(environment):
if not isinstance(view, str): if not isinstance(view, str):
ccode = parse_template(view, pjoin(folder, 'views'), ccode = parse_template(view, pjoin(folder, 'views'),
context=environment) context=environment)
restricted(ccode, environment, 'file stream') layer = 'file stream'
else: else:
filename = pjoin(folder, 'views', view) filename = pjoin(folder, 'views', view)
if os.path.exists(path): # compiled views if os.path.exists(path): # compiled views
@@ -713,16 +707,13 @@ def run_view_in(environment):
rewrite.THREAD_LOCAL.routes.error_message % badv, rewrite.THREAD_LOCAL.routes.error_message % badv,
web2py_error=badv) web2py_error=badv)
layer = filename layer = filename
if is_gae: # Cache the compiled template
ccode = getcfs(layer, filename, ccode = getcfs(layer, filename,
lambda: compile2(parse_template(view, lambda: compile2(parse_template(view,
pjoin(folder, 'views'), pjoin(folder, 'views'),
context=environment), layer)) context=environment),
else: layer))
ccode = parse_template(view, restricted(ccode, environment, layer=layer)
pjoin(folder, 'views'),
context=environment)
restricted(ccode, environment, layer)
def remove_compiled_application(folder): def remove_compiled_application(folder):
@@ -748,26 +739,3 @@ def compile_application(folder, skip_failed_views=False):
compile_controllers(folder) compile_controllers(folder)
failed_views = compile_views(folder, skip_failed_views) failed_views = compile_views(folder, skip_failed_views)
return failed_views return failed_views
def test():
"""
Example::
>>> import traceback, types
>>> environment={'x':1}
>>> open('a.py', 'w').write('print 1/x')
>>> save_pyc('a.py')
>>> os.unlink('a.py')
>>> if type(read_pyc('a.pyc'))==types.CodeType: print 'code'
code
>>> exec read_pyc('a.pyc') in environment
1
"""
return
if __name__ == '__main__':
import doctest
doctest.testmod()
+2 -6
View File
@@ -202,7 +202,7 @@ def compile2(code, layer):
return compile(code, layer, 'exec') return compile(code, layer, 'exec')
def restricted(code, environment=None, layer='Unknown'): def restricted(ccode, environment=None, layer='Unknown'):
""" """
Runs code in environment and returns the output. If an exception occurs Runs code in environment and returns the output. If an exception occurs
in code it raises a RestrictedError containing the traceback. Layer is in code it raises a RestrictedError containing the traceback. Layer is
@@ -213,10 +213,6 @@ def restricted(code, environment=None, layer='Unknown'):
environment['__file__'] = layer environment['__file__'] = layer
environment['__name__'] = '__restricted__' environment['__name__'] = '__restricted__'
try: try:
if isinstance(code, types.CodeType):
ccode = code
else:
ccode = compile2(code, layer)
exec(ccode, environment) exec(ccode, environment)
except HTTP: except HTTP:
raise raise
@@ -231,7 +227,7 @@ def restricted(code, environment=None, layer='Unknown'):
sys.excepthook(etype, evalue, tb) sys.excepthook(etype, evalue, tb)
del tb del tb
output = "%s %s" % (etype, evalue) output = "%s %s" % (etype, evalue)
raise RestrictedError(layer, code, output, environment) raise RestrictedError(layer, ccode, output, environment)
def snapshot(info=None, context=5, code=None, environment=None): def snapshot(info=None, context=5, code=None, environment=None):
+5 -11
View File
@@ -24,9 +24,6 @@ try:
# have web2py # have web2py
from gluon.restricted import RestrictedError from gluon.restricted import RestrictedError
from gluon.globals import current from gluon.globals import current
from gluon.cfs import getcfs
from gluon.fileutils import read_file
HAS_CFS = True
except ImportError: except ImportError:
# do not have web2py # do not have web2py
current = None current = None
@@ -783,14 +780,11 @@ def parse_template(filename,
# First, if we have a str try to open the file # First, if we have a str try to open the file
if isinstance(filename, str): if isinstance(filename, str):
fname = os.path.join(path, filename) fname = os.path.join(path, filename)
if HAS_CFS: try:
text = getcfs(fname, fname, lambda: read_file(fname)) with open(fname, 'rb') as fp:
else: text = fp.read()
try: except IOError:
with open(fname, 'rb') as fp: raise RestrictedError(filename, '', 'Unable to find the file')
text = fp.read()
except IOError:
raise RestrictedError(filename, '', 'Unable to find the file')
else: else:
text = filename.read() text = filename.read()
text = to_native(text) text = to_native(text)
+7
View File
@@ -16,6 +16,7 @@ from gluon.storage import Storage, List
from gluon import fileutils from gluon import fileutils
from gluon.dal import DAL, Field, Table from gluon.dal import DAL, Field, Table
from gluon.http import HTTP from gluon.http import HTTP
from gluon.fileutils import open_file
DEFAULT_URI = os.getenv('DB', 'sqlite:memory') DEFAULT_URI = os.getenv('DB', 'sqlite:memory')
@@ -76,12 +77,18 @@ class TestAppAdmin(unittest.TestCase):
def run_view(self): def run_view(self):
return run_view_in(self.env) return run_view_in(self.env)
def run_view_file_stream(self):
view_path = os.path.join(self.env['request'].folder, 'views', 'appadmin.html')
self.env['response'].view = open_file(view_path, 'r')
return run_view_in(self.env)
def _test_index(self): def _test_index(self):
result = self.run_function() result = self.run_function()
self.assertTrue('db' in result['databases']) self.assertTrue('db' in result['databases'])
self.env.update(result) self.env.update(result)
try: try:
self.run_view() self.run_view()
self.run_view_file_stream()
except Exception as e: except Exception as e:
print(e.message) print(e.message)
self.fail('Could not make the view') self.fail('Could not make the view')