source: OpenRLabs-Git/deploy/rlabs-docker/web2py-rlabs/gluon/compileapp.py

main
Last change on this file was 42bd667, checked in by David Fuertes <dfuertes@…>, 4 years ago

Historial Limpio

  • Property mode set to 100755
File size: 28.3 KB
Line 
1# -*- coding: utf-8 -*-
2
3"""
4| This file is part of the web2py Web Framework
5| Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
6| License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
7
8Functions required to execute app components
9---------------------------------------------
10
11Note:
12    FOR INTERNAL USE ONLY
13"""
14
15import re
16import random
17import types
18import copy
19from functools import reduce
20import py_compile
21import os
22from os.path import join as pjoin, exists
23import sys
24import imp
25import marshal
26import fnmatch
27import shutil
28
29from gluon._compat import (builtin, PY2, unicodeT, to_native, to_bytes,
30    iteritems, integer_types, basestring, xrange, reload)
31from gluon.storage import Storage, List
32from gluon.globals import current, Response
33from gluon import html, validators, rewrite
34from gluon.http import HTTP, redirect
35from gluon.dal import DAL, Field
36from gluon.sqlhtml import SQLFORM, SQLTABLE
37from gluon.languages import TranslatorFactory
38from gluon.cache import Cache
39from gluon.validators import Validator
40from gluon.settings import global_settings
41from pydal.base import BaseAdapter
42from gluon.custom_import import custom_import_install
43from gluon.fileutils import mktree, listdir, read_file, write_file, abspath, add_path_first
44from gluon.template import parse_template
45from gluon.cfs import getcfs
46from gluon.restricted import restricted, compile2
47
48CACHED_REGEXES = {}
49CACHED_REGEXES_MAX_SIZE = 1000
50
51def re_compile(regex):
52    try:
53        return CACHED_REGEXES[regex]
54    except KeyError:
55        if len(CACHED_REGEXES) >= CACHED_REGEXES_MAX_SIZE:
56            CACHED_REGEXES.clear()
57        compiled_regex = CACHED_REGEXES[regex] = re.compile(regex)
58        return compiled_regex
59
60
61def LOAD(c=None, f='index', args=None, vars=None,
62         extension=None, target=None, ajax=False, ajax_trap=False,
63         url=None, user_signature=False, timeout=None, times=1,
64         content='loading...', post_vars=Storage(), **attr):
65    """  LOADs a component into the action's document
66
67    Args:
68        c(str): controller
69        f(str): function
70        args(tuple or list): arguments
71        vars(dict): vars
72        extension(str): extension
73        target(str): id of the target
74        ajax(bool): True to enable AJAX behaviour
75        ajax_trap(bool): True if `ajax` is set to `True`, traps
76            both links and forms "inside" the target
77        url(str): overrides `c`, `f`, `args` and `vars`
78        user_signature(bool): adds hmac signature to all links
79            with a key that is different for every user
80        timeout(int): in milliseconds, specifies the time to wait before
81            starting the request or the frequency if times is greater than
82            1 or "infinity"
83        times(integer or str): how many times the component will be requested
84            "infinity" or "continuous" are accepted to reload indefinitely the
85            component
86    """
87    if args is None:
88        args = []
89    vars = Storage(vars or {})
90    target = target or 'c' + str(random.random())[2:]
91    attr['_id'] = target
92    request = current.request
93    if '.' in f:
94        f, extension = f.rsplit('.', 1)
95    if url or ajax:
96        url = url or html.URL(request.application, c, f, r=request,
97                              args=args, vars=vars, extension=extension,
98                              user_signature=user_signature)
99        # timing options
100        if isinstance(times, basestring):
101            if times.upper() in ("INFINITY", "CONTINUOUS"):
102                times = "Infinity"
103            else:
104                # FIXME: should be a ValueError
105                raise TypeError("Unsupported times argument %s" % times)
106        elif isinstance(times, int):
107            if times <= 0:
108                raise ValueError("Times argument must be greater than zero, 'Infinity' or None")
109        else:
110            # NOTE: why do not use ValueError only?
111            raise TypeError("Unsupported times argument type %s" % type(times))
112        if timeout is not None:
113            if not isinstance(timeout, integer_types):
114                raise ValueError("Timeout argument must be an integer or None")
115            elif timeout <= 0:
116                raise ValueError(
117                    "Timeout argument must be greater than zero or None")
118            statement = "$.web2py.component('%s','%s', %s, %s);" \
119                % (url, target, timeout, times)
120            attr['_data-w2p_timeout'] = timeout
121            attr['_data-w2p_times'] = times
122        else:
123            statement = "$.web2py.component('%s','%s');" % (url, target)
124        attr['_data-w2p_remote'] = url
125        if target is not None:
126            return html.DIV(content, **attr)
127
128    else:
129        if not isinstance(args, (list, tuple)):
130            args = [args]
131        c = c or request.controller
132        other_request = Storage(request)
133        other_request['env'] = Storage(request.env)
134        other_request.controller = c
135        other_request.function = f
136        other_request.extension = extension or request.extension
137        other_request.args = List(args)
138        other_request.vars = vars
139        other_request.get_vars = vars
140        other_request.post_vars = post_vars
141        other_response = Response()
142        other_request.env.path_info = '/' + \
143            '/'.join([request.application, c, f] +
144                     [str(a) for a in other_request.args])
145        other_request.env.query_string = \
146            vars and html.URL(vars=vars).split('?')[1] or ''
147        other_request.env.http_web2py_component_location = \
148            request.env.path_info
149        other_request.cid = target
150        other_request.env.http_web2py_component_element = target
151        other_request.restful = types.MethodType(request.restful.__func__, other_request)
152        # A bit nasty but needed to use LOAD on action decorates with @request.restful()
153        other_response.view = '%s/%s.%s' % (c, f, other_request.extension)
154
155        other_environment = copy.copy(current.globalenv)  # FIXME: NASTY
156
157        other_response._view_environment = other_environment
158        other_response.generic_patterns = \
159            copy.copy(current.response.generic_patterns)
160        other_environment['request'] = other_request
161        other_environment['response'] = other_response
162
163        ## some magic here because current are thread-locals
164
165        original_request, current.request = current.request, other_request
166        original_response, current.response = current.response, other_response
167        page = run_controller_in(c, f, other_environment)
168        if isinstance(page, dict):
169            other_response._vars = page
170            other_response._view_environment.update(page)
171            page = run_view_in(other_response._view_environment)
172
173        current.request, current.response = original_request, original_response
174        js = None
175        if ajax_trap:
176            link = html.URL(request.application, c, f, r=request,
177                            args=args, vars=vars, extension=extension,
178                            user_signature=user_signature)
179            js = "$.web2py.trap_form('%s','%s');" % (link, target)
180        script = js and html.SCRIPT(js, _type="text/javascript") or ''
181        return html.TAG[''](html.DIV(html.XML(page), **attr), script)
182
183
184class LoadFactory(object):
185    """
186    Attention: this helper is new and experimental
187    """
188    def __init__(self, environment):
189        self.environment = environment
190
191    def __call__(self, c=None, f='index', args=None, vars=None,
192                 extension=None, target=None, ajax=False, ajax_trap=False,
193                 url=None, user_signature=False, content='loading...', **attr):
194        if args is None:
195            args = []
196        vars = Storage(vars or {})
197        target = target or 'c' + str(random.random())[2:]
198        attr['_id'] = target
199        request = current.request
200        if '.' in f:
201            f, extension = f.rsplit('.', 1)
202        if url or ajax:
203            url = url or html.URL(request.application, c, f, r=request,
204                                  args=args, vars=vars, extension=extension,
205                                  user_signature=user_signature)
206            script = html.SCRIPT('$.web2py.component("%s","%s")' % (url, target),
207                                 _type="text/javascript")
208            return html.TAG[''](script, html.DIV(content, **attr))
209        else:
210            if not isinstance(args, (list, tuple)):
211                args = [args]
212            c = c or request.controller
213
214            other_request = Storage(request)
215            other_request['env'] = Storage(request.env)
216            other_request.controller = c
217            other_request.function = f
218            other_request.extension = extension or request.extension
219            other_request.args = List(args)
220            other_request.vars = vars
221            other_request.get_vars = vars
222            other_request.post_vars = Storage()
223            other_response = Response()
224            other_request.env.path_info = '/' + \
225                '/'.join([request.application, c, f] +
226                         [str(a) for a in other_request.args])
227            other_request.env.query_string = \
228                vars and html.URL(vars=vars).split('?')[1] or ''
229            other_request.env.http_web2py_component_location = \
230                request.env.path_info
231            other_request.cid = target
232            other_request.env.http_web2py_component_element = target
233            other_response.view = '%s/%s.%s' % (c, f, other_request.extension)
234            other_environment = copy.copy(self.environment)
235            other_response._view_environment = other_environment
236            other_response.generic_patterns = \
237                copy.copy(current.response.generic_patterns)
238            other_environment['request'] = other_request
239            other_environment['response'] = other_response
240
241            ## some magic here because current are thread-locals
242
243            original_request, current.request = current.request, other_request
244            original_response, current.response = current.response, other_response
245            page = run_controller_in(c, f, other_environment)
246            if isinstance(page, dict):
247                other_response._vars = page
248                other_response._view_environment.update(page)
249                page = run_view_in(other_response._view_environment)
250
251            current.request, current.response = original_request, original_response
252            js = None
253            if ajax_trap:
254                link = html.URL(request.application, c, f, r=request,
255                                args=args, vars=vars, extension=extension,
256                                user_signature=user_signature)
257                js = "$.web2py.trap_form('%s','%s');" % (link, target)
258            script = js and html.SCRIPT(js, _type="text/javascript") or ''
259            return html.TAG[''](html.DIV(html.XML(page), **attr), script)
260
261
262def local_import_aux(name, reload_force=False, app='welcome'):
263    """
264    In apps, instead of importing a local module
265    (in applications/app/modules) with::
266
267       import a.b.c as d
268
269    you should do::
270
271       d = local_import('a.b.c')
272
273    or (to force a reload):
274
275       d = local_import('a.b.c', reload=True)
276
277    This prevents conflict between applications and un-necessary execs.
278    It can be used to import any module, including regular Python modules.
279    """
280    items = name.replace('/', '.')
281    name = "applications.%s.modules.%s" % (app, items)
282    module = __import__(name)
283    for item in name.split(".")[1:]:
284        module = getattr(module, item)
285    if reload_force:
286        reload(module)
287    return module
288#
289# OLD IMPLEMENTATION:
290#
291#   items = name.replace('/','.').split('.')
292#   filename, modulepath = items[-1], pjoin(apath,'modules',*items[:-1])
293#   imp.acquire_lock()
294#   try:
295#       file=None
296#       (file,path,desc) = imp.find_module(filename,[modulepath]+sys.path)
297#       if not path in sys.modules or reload:
298#           if is_gae:
299#               module={}
300#               execfile(path,{},module)
301#               module=Storage(module)
302#           else:
303#               module = imp.load_module(path,file,path,desc)
304#           sys.modules[path] = module
305#       else:
306#           module = sys.modules[path]
307#   except Exception, e:
308#       module = None
309#   if file:
310#       file.close()
311#   imp.release_lock()
312#   if not module:
313#       raise ImportError, "cannot find module %s in %s" % (
314#           filename, modulepath)
315#   return module
316
317
318_base_environment_ = dict((k, getattr(html, k)) for k in html.__all__)
319_base_environment_.update(
320    (k, getattr(validators, k)) for k in validators.__all__)
321_base_environment_['__builtins__'] = __builtins__
322_base_environment_['HTTP'] = HTTP
323_base_environment_['redirect'] = redirect
324_base_environment_['DAL'] = DAL
325_base_environment_['Field'] = Field
326_base_environment_['SQLDB'] = DAL        # for backward compatibility
327_base_environment_['SQLField'] = Field  # for backward compatibility
328_base_environment_['SQLFORM'] = SQLFORM
329_base_environment_['SQLTABLE'] = SQLTABLE
330_base_environment_['LOAD'] = LOAD
331# For an easier PY3 migration
332_base_environment_['PY2'] = PY2
333_base_environment_['to_native'] = to_native
334_base_environment_['to_bytes'] = to_bytes
335_base_environment_['iteritems'] = iteritems
336_base_environment_['reduce'] = reduce
337_base_environment_['xrange'] = xrange
338
339
340def build_environment(request, response, session, store_current=True):
341    """
342    Build the environment dictionary into which web2py files are executed.
343    """
344    # h,v = html,validators
345    environment = dict(_base_environment_)
346
347    if not request.env:
348        request.env = Storage()
349    # Enable standard conditional models (i.e., /*.py, /[controller]/*.py, and
350    # /[controller]/[function]/*.py)
351    response.models_to_run = [
352        r'^\w+\.py$',
353        r'^%s/\w+\.py$' % request.controller,
354        r'^%s/%s/\w+\.py$' % (request.controller, request.function)
355        ]
356
357    T = environment['T'] = TranslatorFactory(pjoin(request.folder, 'languages'),
358                                             request.env.http_accept_language)
359    c = environment['cache'] = Cache(request)
360
361    # configure the validator to use the t translator
362    Validator.translator = staticmethod(lambda text: None if text is None else str(T(text)))
363
364    if store_current:
365        current.globalenv = environment
366        current.request = request
367        current.response = response
368        current.session = session
369        current.T = T
370        current.cache = c
371
372    if global_settings.is_jython:
373        # jython hack
374        class mybuiltin(object):
375            """
376            NOTE could simple use a dict and populate it,
377            NOTE not sure if this changes things though if monkey patching import.....
378            """
379            # __builtins__
380            def __getitem__(self, key):
381                try:
382                    return getattr(builtin, key)
383                except AttributeError:
384                    raise KeyError(key)
385
386            def __setitem__(self, key, value):
387                setattr(self, key, value)
388
389        global __builtins__
390        __builtins__ = mybuiltin()
391
392    environment['request'] = request
393    environment['response'] = response
394    environment['session'] = session
395    environment['local_import'] = \
396        lambda name, reload=False, app=request.application:\
397        local_import_aux(name, reload, app)
398    BaseAdapter.set_folder(pjoin(request.folder, 'databases'))
399    custom_import_install()
400    return environment
401
402
403def save_pyc(filename):
404    """
405    Bytecode compiles the file `filename`
406    """
407    cfile = "%sc" % filename
408    py_compile.compile(filename, cfile=cfile)
409
410
411if PY2:
412    MARSHAL_HEADER_SIZE = 8
413else:
414    MARSHAL_HEADER_SIZE = 16 if sys.version_info[1] >= 7 else 12
415
416def read_pyc(filename):
417    """
418    Read the code inside a bytecode compiled file if the MAGIC number is
419    compatible
420
421    Returns:
422        a code object
423    """
424    data = read_file(filename, 'rb')
425    if not global_settings.web2py_runtime_gae and \
426        not data.startswith(imp.get_magic()):
427        raise SystemError('compiled code is incompatible')
428    return marshal.loads(data[MARSHAL_HEADER_SIZE:])
429
430
431REGEX_PATH_CLASS = r"[\w%s-]" % os.sep if os.sep != '\\' else r'[\w\\-]'
432REGEX_VIEW_PATH = r"%s+(?:\.\w+)*$" % REGEX_PATH_CLASS
433
434def compile_views(folder, skip_failed_views=False):
435    """
436    Compiles all the views in the application specified by `folder`
437    """
438    path = pjoin(folder, 'views')
439    failed_views = []
440    for fname in listdir(path, REGEX_VIEW_PATH, followlinks=True):
441        try:
442            data = parse_template(fname, path)
443        except Exception as e:
444            if skip_failed_views:
445                failed_views.append(fname)
446            else:
447                # FIXME: should raise something more specific than Exception
448                raise Exception("%s in %s" % (e, fname))
449        else:
450            filename = 'views.%s.py' % fname.replace(os.sep, '.')
451            filename = pjoin(folder, 'compiled', filename)
452            write_file(filename, data)
453            save_pyc(filename)
454            os.unlink(filename)
455    return failed_views or None
456
457
458REGEX_MODEL_PATH = r"%s+\.py$" % REGEX_PATH_CLASS
459
460def compile_models(folder):
461    """
462    Compiles all the models in the application specified by `folder`
463    """
464    path = pjoin(folder, 'models')
465    for fname in listdir(path, REGEX_MODEL_PATH, followlinks=True):
466        data = read_file(pjoin(path, fname))
467        modelfile = 'models.'+fname.replace(os.sep, '.')
468        filename = pjoin(folder, 'compiled', modelfile)
469        mktree(filename)
470        write_file(filename, data)
471        save_pyc(filename)
472        os.unlink(filename)
473
474
475REGEX_LONG_STRING = re.compile('(""".*?"""|' "'''.*?''')", re.DOTALL)
476REGEX_EXPOSED = re.compile(r'^def\s+(_?[a-zA-Z0-9]\w*)\( *\)\s*:', re.MULTILINE)
477
478def find_exposed_functions(data):
479    data = REGEX_LONG_STRING.sub('', data)
480    return REGEX_EXPOSED.findall(data)
481
482
483REGEX_CONTROLLER = r'[\w-]+\.py$'
484
485def compile_controllers(folder):
486    """
487    Compiles all the controllers in the application specified by `folder`
488    """
489    path = pjoin(folder, 'controllers')
490    for fname in listdir(path, REGEX_CONTROLLER, followlinks=True):
491        data = read_file(pjoin(path, fname))
492        exposed = find_exposed_functions(data)
493        for function in exposed:
494            command = data + "\nresponse._vars=response._caller(%s)\n" % \
495                function
496            filename = pjoin(folder, 'compiled',
497                             'controllers.%s.%s.py' % (fname[:-3], function))
498            write_file(filename, command)
499            save_pyc(filename)
500            os.unlink(filename)
501
502
503if PY2:
504    def model_cmp(a, b, sep='.'):
505        return cmp(a.count(sep), b.count(sep)) or cmp(a, b)
506
507    def model_cmp_sep(a, b, sep=os.sep):
508        return model_cmp(a, b, sep)
509
510
511REGEX_COMPILED_MODEL = r'models[_.][\w.-]+\.pyc$'
512REGEX_MODEL = r'[\w-]+\.py$'
513
514def run_models_in(environment):
515    """
516    Runs all models (in the app specified by the current folder)
517    It tries pre-compiled models first before compiling them.
518    """
519    request = current.request
520    folder = request.folder
521    c = request.controller
522    # f = environment['request'].function
523    response = current.response
524
525    path = pjoin(folder, 'models')
526    cpath = pjoin(folder, 'compiled')
527    compiled = exists(cpath)
528    if PY2:
529        if compiled:
530            models = sorted(listdir(cpath, REGEX_COMPILED_MODEL, 0),
531                            model_cmp)
532        else:
533            models = sorted(listdir(path, REGEX_MODEL, 0, sort=False),
534                            model_cmp_sep)
535    else:
536        if compiled:
537            models = sorted(listdir(cpath, REGEX_COMPILED_MODEL, 0),
538                            key=lambda f: '{0:03d}'.format(f.count('.')) + f)
539        else:
540            models = sorted(listdir(path, REGEX_MODEL, 0, sort=False),
541                            key=lambda f: '{0:03d}'.format(f.count(os.sep)) + f)
542
543    models_to_run = None
544    for model in models:
545        if response.models_to_run != models_to_run:
546            regex = models_to_run = response.models_to_run[:]
547            if isinstance(regex, list):
548                regex = re_compile('|'.join(regex))
549        if models_to_run:
550            if compiled:
551                n = len(cpath)+8
552                fname = model[n:-4].replace('.', '/')+'.py'
553            else:
554                n = len(path)+1
555                fname = model[n:].replace(os.sep, '/')
556            if not regex.search(fname) and c != 'appadmin':
557                continue
558            elif compiled:
559                f = lambda: read_pyc(model)
560            else:
561                f = lambda: compile2(read_file(model), model)
562            ccode = getcfs(model, model, f)
563            restricted(ccode, environment, layer=model)
564
565
566TEST_CODE = r"""
567def _TEST():
568    import doctest, sys, cStringIO, types, gluon.fileutils
569    if not gluon.fileutils.check_credentials(request):
570        raise HTTP(401, web2py_error='invalid credentials')
571    stdout = sys.stdout
572    html = '<h2>Testing controller "%s.py" ... done.</h2><br/>\n' \
573        % request.controller
574    for key in sorted([key for key in globals() if not key in __symbols__+['_TEST']]):
575        eval_key = eval(key)
576        if type(eval_key) == types.FunctionType:
577            number_doctests = sum([len(ds.examples) for ds in doctest.DocTestFinder().find(eval_key)])
578            if number_doctests>0:
579                sys.stdout = cStringIO.StringIO()
580                name = '%s/controllers/%s.py in %s.__doc__' \
581                    % (request.folder, request.controller, key)
582                doctest.run_docstring_examples(eval_key,
583                    globals(), False, name=name)
584                report = sys.stdout.getvalue().strip()
585                if report:
586                    pf = 'failed'
587                else:
588                    pf = 'passed'
589                html += '<h3 class="%s">Function %s [%s]</h3>\n' \
590                    % (pf, key, pf)
591                if report:
592                    html += CODE(report, language='web2py', \
593                        link='/examples/global/vars/').xml()
594                html += '<br/>\n'
595            else:
596                html += \
597                    '<h3 class="nodoctests">Function %s [no doctests]</h3><br/>\n' \
598                    % (key)
599    response._vars = html
600    sys.stdout = stdout
601_TEST()
602"""
603
604def run_controller_in(controller, function, environment):
605    """
606    Runs the controller.function() (for the app specified by
607    the current folder).
608    It tries pre-compiled controller.function.pyc first before compiling it.
609    """
610    # if compiled should run compiled!
611    folder = current.request.folder
612    cpath = pjoin(folder, 'compiled')
613    badc = 'invalid controller (%s/%s)' % (controller, function)
614    badf = 'invalid function (%s/%s)' % (controller, function)
615    if exists(cpath):
616        filename = pjoin(cpath, 'controllers.%s.%s.pyc' % (controller, function))
617        try:
618            ccode = getcfs(filename, filename, lambda: read_pyc(filename))
619        except IOError:
620            raise HTTP(404,
621                       rewrite.THREAD_LOCAL.routes.error_message % badf,
622                       web2py_error=badf)
623    elif function == '_TEST':
624        # TESTING: adjust the path to include site packages
625        paths = (global_settings.gluon_parent, abspath(
626            'site-packages', gluon=True), abspath('gluon', gluon=True), '')
627        [add_path_first(path) for path in paths]
628        # TESTING END
629
630        filename = pjoin(folder, 'controllers/%s.py'
631                                 % controller)
632        if not exists(filename):
633            raise HTTP(404,
634                       rewrite.THREAD_LOCAL.routes.error_message % badc,
635                       web2py_error=badc)
636        environment['__symbols__'] = list(environment.keys())
637        code = read_file(filename)
638        code += TEST_CODE
639        ccode = compile2(code, filename)
640    else:
641        filename = pjoin(folder, 'controllers/%s.py' % controller)
642        try:
643            code = getcfs(filename, filename, lambda: read_file(filename))
644        except IOError:
645            raise HTTP(404,
646                       rewrite.THREAD_LOCAL.routes.error_message % badc,
647                       web2py_error=badc)
648        exposed = find_exposed_functions(code)
649        if function not in exposed:
650            raise HTTP(404,
651                       rewrite.THREAD_LOCAL.routes.error_message % badf,
652                       web2py_error=badf)
653        code = "%s\nresponse._vars=response._caller(%s)" % (code, function)
654        layer = "%s:%s" % (filename, function)
655        ccode = getcfs(layer, filename, lambda: compile2(code, filename))
656
657    restricted(ccode, environment, layer=filename)
658    response = environment["response"]
659    vars = response._vars
660    if response.postprocessing:
661        vars = reduce(lambda vars, p: p(vars), response.postprocessing, vars)
662    if isinstance(vars, unicodeT):
663        vars = to_native(vars)
664    elif hasattr(vars, 'xml') and callable(vars.xml):
665        vars = vars.xml()
666    return vars
667
668
669def run_view_in(environment):
670    """
671    Executes the view for the requested action.
672    The view is the one specified in `response.view` or determined by the url
673    or `view/generic.extension`
674    It tries the pre-compiled views.controller.function.pyc before compiling it.
675    """
676    request = current.request
677    response = current.response
678    view = environment['response'].view
679    folder = request.folder
680    cpath = pjoin(folder, 'compiled')
681    badv = 'invalid view (%s)' % view
682    patterns = response.get('generic_patterns')
683    layer = None
684    scode = None
685    if patterns:
686        regex = re_compile('|'.join(fnmatch.translate(p) for p in patterns))
687        short_action = '%(controller)s/%(function)s.%(extension)s' % request
688        allow_generic = regex.search(short_action)
689    else:
690        allow_generic = False
691    if not isinstance(view, str):
692        ccode = parse_template(view, pjoin(folder, 'views'),
693                               context=environment)
694        layer = 'file stream'
695    else:
696        filename = pjoin(folder, 'views', view)
697        if exists(cpath):  # compiled views
698            x = view.replace('/', '.')
699            files = ['views.%s.pyc' % x]
700            is_compiled = exists(pjoin(cpath, files[0]))
701            # Don't use a generic view if the non-compiled view exists.
702            if is_compiled or (not is_compiled and not exists(filename)):
703                if allow_generic:
704                    files.append('views.generic.%s.pyc' % request.extension)
705                # for backward compatibility
706                if request.extension == 'html':
707                    files.append('views.%s.pyc' % x[:-5])
708                    if allow_generic:
709                        files.append('views.generic.pyc')
710                # end backward compatibility code
711                for f in files:
712                    compiled = pjoin(cpath, f)
713                    if exists(compiled):
714                        ccode = getcfs(compiled, compiled, lambda: read_pyc(compiled))
715                        layer = compiled
716                        break
717        # if the view is not compiled
718        if not layer:
719            if not exists(filename) and allow_generic:
720                view = 'generic.' + request.extension
721                filename = pjoin(folder, 'views', view)
722            if not exists(filename):
723                raise HTTP(404,
724                           rewrite.THREAD_LOCAL.routes.error_message % badv,
725                           web2py_error=badv)
726            # Parse template
727            scode = parse_template(view,
728                                   pjoin(folder, 'views'),
729                                   context=environment)
730            # Compile template
731            ccode = compile2(scode, filename)
732            layer = filename
733    restricted(ccode, environment, layer=layer, scode=scode)
734    # parse_template saves everything in response body
735    return environment['response'].body.getvalue()
736
737
738REGEX_COMPILED_CONTROLLER = r'[\w-]+\.pyc$'
739
740def remove_compiled_application(folder):
741    """
742    Deletes the folder `compiled` containing the compiled application.
743    """
744    try:
745        shutil.rmtree(pjoin(folder, 'compiled'))
746        path = pjoin(folder, 'controllers')
747        for file in listdir(path, REGEX_COMPILED_CONTROLLER, drop=False):
748            os.unlink(file)
749    except OSError:
750        pass
751
752
753def compile_application(folder, skip_failed_views=False):
754    """
755    Compiles all models, views, controller for the application in `folder`.
756    """
757    remove_compiled_application(folder)
758    os.mkdir(pjoin(folder, 'compiled'))
759    compile_models(folder)
760    compile_controllers(folder)
761    failed_views = compile_views(folder, skip_failed_views)
762    return failed_views
Note: See TracBrowser for help on using the repository browser.