Package web2py :: Package gluon :: Module compileapp
[hide private]
[frames] | no frames]

Source Code for Module web2py.gluon.compileapp

  1  #!/usr/bin/env python 
  2  # -*- coding: utf-8 -*- 
  3   
  4  """ 
  5  This file is part of the web2py Web Framework 
  6  Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu> 
  7  License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html) 
  8   
  9  Functions required to execute app components 
 10  ============================================ 
 11   
 12  FOR INTERNAL USE ONLY 
 13  """ 
 14   
 15  import os 
 16  import copy 
 17  import random 
 18  from storage import Storage, List 
 19  from template import parse_template 
 20  from restricted import restricted, compile2 
 21  from fileutils import listdir 
 22  from myregex import regex_expose 
 23  from languages import translator 
 24  from sql import BaseAdapter, SQLDB, SQLField, DAL, Field 
 25  from sqlhtml import SQLFORM, SQLTABLE 
 26  from cache import Cache 
 27  import settings 
 28  from cfs import getcfs 
 29  import html 
 30  import validators 
 31  from http import HTTP, redirect 
 32  import marshal 
 33  import imp 
 34  import logging 
 35  logger = logging.getLogger("web2py") 
 36  import rewrite 
 37   
 38  try: 
 39      import py_compile 
 40  except: 
 41      logger.warning('unable to import py_compile') 
 42   
 43  is_gae = settings.global_settings.web2py_runtime_gae 
 44   
 45  TEST_CODE = \ 
 46      r""" 
 47  def _TEST(): 
 48      import doctest, sys, cStringIO, types, cgi, gluon.fileutils 
 49      if not gluon.fileutils.check_credentials(request): 
 50          raise HTTP(401, web2py_error='invalid credentials') 
 51      stdout = sys.stdout 
 52      html = '<h2>Testing controller "%s.py" ... done.</h2><br/>\n' \ 
 53          % request.controller 
 54      for key in sorted([key for key in globals() if not key in __symbols__+['_TEST']]): 
 55          eval_key = eval(key) 
 56          if type(eval_key) == types.FunctionType: 
 57              number_doctests = sum([len(ds.examples) for ds in doctest.DocTestFinder().find(eval_key)]) 
 58              if number_doctests>0: 
 59                  sys.stdout = cStringIO.StringIO() 
 60                  name = '%s/controllers/%s.py in %s.__doc__' \ 
 61                      % (request.folder, request.controller, key) 
 62                  doctest.run_docstring_examples(eval_key, 
 63                      globals(), False, name=name) 
 64                  report = sys.stdout.getvalue().strip() 
 65                  if report: 
 66                      pf = 'failed' 
 67                  else: 
 68                      pf = 'passed' 
 69                  html += '<h3 class="%s">Function %s [%s]</h3>\n' \ 
 70                      % (pf, key, pf) 
 71                  if report: 
 72                      html += CODE(report, language='web2py', \ 
 73                          link='/examples/global/vars/').xml() 
 74                  html += '<br/>\n' 
 75              else: 
 76                  html += \ 
 77                      '<h3 class="nodoctests">Function %s [no doctests]</h3><br/>\n' \ 
 78                      % (key) 
 79      response._vars = html 
 80      sys.stdout = stdout 
 81  _TEST() 
 82  """ 
 83   
84 -class LoadFactory(object):
85 """ 86 Attention: this helper is new and experimental 87 """
88 - def __init__(self,environment):
89 self.environment = environment
90 - def __call__(self, c=None, f='index', args=[], vars={}, 91 extension=None, target=None,ajax=False,ajax_trap=False, 92 url=None):
93 import globals 94 target = target or 'c'+str(random.random())[2:] 95 request = self.environment['request'] 96 if '.' in f: 97 f, extension = f.split('.',1) 98 if c and not url and not ajax: 99 other_environment = copy.copy(self.environment) 100 other_request = globals.Request() 101 other_request.application = request.application 102 other_request.controller = c 103 other_request.function = f 104 other_request.extension = extension or request.extension 105 other_request.args = List(args) 106 other_request.folder = request.folder 107 other_request.env = request.env 108 if not ajax_trap: 109 other_request.vars = request.vars 110 other_request.get_vars = request.get_vars 111 other_request.post_vars = request.post_vars 112 else: 113 other_request.vars = vars 114 other_environment['request'] = other_request 115 other_response = globals.Response() 116 other_environment['response'] = other_response 117 other_response._view_environment = other_environment 118 other_request.env.http_web2py_component_location = \ 119 request.env.path_info 120 other_request.env.http_web2py_component_element = target 121 other_response.view = '%s/%s.%s' % (c,f, other_request.extension) 122 page = run_controller_in(c, f, other_environment) 123 if isinstance(page, dict): 124 other_response._vars = page 125 for key in page: 126 other_response._view_environment[key] = page[key] 127 run_view_in(other_response._view_environment) 128 page = other_response.body.getvalue() 129 js = None 130 if ajax_trap: 131 link = html.URL(request.application, c, f, r=request, 132 args=args, vars=vars, extension=extension), 133 js = "web2py_trap_form('%s','%s');" % (link, target) 134 # not sure about this 135 # for (name,value) in other_response.headers: 136 # if name == 'web2py-component-command': 137 # js += value 138 script = js and html.SCRIPT(js,_type="text/javascript") or '' 139 return html.TAG[''](html.DIV(html.XML(page),_id=target),script) 140 else: 141 url = url or html.URL(request.application, c, f, r=request, 142 args=args, vars=vars, extension=extension) 143 script = html.SCRIPT('web2py_component("%s","%s")' % (url, target), 144 _type="text/javascript") 145 return html.TAG[''](script, html.DIV('loading...', _id=target))
146 147
148 -def local_import_aux(name, force=False, app='welcome'):
149 """ 150 In apps, instead of importing a local module 151 (in applications/app/modules) with:: 152 153 import a.b.c as d 154 155 you should do:: 156 157 d = local_import('a.b.c') 158 159 or (to force a reload): 160 161 d = local_import('a.b.c', reload=True) 162 163 This prevents conflict between applications and un-necessary execs. 164 It can be used to import any module, including regular Python modules. 165 """ 166 items = name.replace('/','.') 167 name = "applications.%s.modules.%s" % (app, items) 168 module = __import__(name) 169 for item in name.split(".")[1:]: 170 module = getattr(module, item) 171 if force: 172 reload(module) 173 return module
174 175 176 """ 177 OLD IMPLEMENTATION: 178 items = name.replace('/','.').split('.') 179 filename, modulepath = items[-1], os.path.join(apath,'modules',*items[:-1]) 180 imp.acquire_lock() 181 try: 182 file=None 183 (file,path,desc) = imp.find_module(filename,[modulepath]+sys.path) 184 if not path in sys.modules or reload: 185 if is_gae: 186 module={} 187 execfile(path,{},module) 188 module=Storage(module) 189 else: 190 module = imp.load_module(path,file,path,desc) 191 sys.modules[path] = module 192 else: 193 module = sys.modules[path] 194 except Exception, e: 195 module = None 196 if file: 197 file.close() 198 imp.release_lock() 199 if not module: 200 raise ImportError, "cannot find module %s in %s" % (filename, modulepath) 201 return module 202 """ 203
204 -def build_environment(request, response, session):
205 """ 206 Build the environment dictionary into which web2py files are executed. 207 """ 208 209 environment = {} 210 for key in html.__all__: 211 environment[key] = getattr(html, key) 212 213 # Overwrite the URL function with a proxy 214 # url function which contains this request. 215 environment['URL'] = html._gURL(request) 216 217 for key in validators.__all__: 218 environment[key] = getattr(validators, key) 219 if not request.env: 220 request.env = Storage() 221 environment['T'] = translator(request) 222 environment['HTTP'] = HTTP 223 environment['redirect'] = redirect 224 environment['request'] = request 225 environment['response'] = response 226 environment['session'] = session 227 environment['cache'] = Cache(request) 228 environment['DAL'] = DAL 229 environment['Field'] = Field 230 environment['SQLDB'] = SQLDB # for backward compatibility 231 environment['SQLField'] = SQLField # for backward compatibility 232 environment['SQLFORM'] = SQLFORM 233 environment['SQLTABLE'] = SQLTABLE 234 environment['LOAD'] = LoadFactory(environment) 235 environment['local_import'] = \ 236 lambda name, reload=False, app=request.application:\ 237 local_import_aux(name,reload,app) 238 BaseAdapter.set_folder(os.path.join(request.folder, 'databases')) 239 response._view_environment = copy.copy(environment) 240 return environment
241 242
243 -def save_pyc(filename):
244 """ 245 Bytecode compiles the file `filename` 246 """ 247 py_compile.compile(filename)
248 249
250 -def read_pyc(filename):
251 """ 252 Read the code inside a bytecode compiled file if the MAGIC number is 253 compatible 254 255 :returns: a code object 256 """ 257 fp = open(filename, 'rb') 258 data = fp.read() 259 fp.close() 260 if not is_gae and data[:4] != imp.get_magic(): 261 raise SystemError, 'compiled code is incompatible' 262 return marshal.loads(data[8:])
263 264
265 -def compile_views(folder):
266 """ 267 Compiles all the views in the application specified by `folder` 268 """ 269 270 path = os.path.join(folder, 'views') 271 for file in listdir(path, '^[\w/]+\.\w+$'): 272 data = parse_template(file, path) 273 filename = ('views/%s.py' % file).replace('/', '_').replace('\\', '_') 274 filename = os.path.join(folder, 'compiled', filename) 275 fp = open(filename, 'w') 276 fp.write(data) 277 fp.close() 278 save_pyc(filename) 279 os.unlink(filename)
280 281
282 -def compile_models(folder):
283 """ 284 Compiles all the models in the application specified by `folder` 285 """ 286 287 path = os.path.join(folder, 'models') 288 for file in listdir(path, '.+\.py$'): 289 fp = open(os.path.join(path, file), 'r') 290 data = fp.read() 291 fp.close() 292 filename = os.path.join(folder, 'compiled', ('models/' 293 + file).replace('/', '_')) 294 fp = open(filename, 'w') 295 fp.write(data) 296 fp.close() 297 save_pyc(filename) 298 os.unlink(filename)
299 300
301 -def compile_controllers(folder):
302 """ 303 Compiles all the controllers in the application specified by `folder` 304 """ 305 306 path = os.path.join(folder, 'controllers') 307 for file in listdir(path, '.+\.py$'): 308 ### why is this here? save_pyc(os.path.join(path, file)) 309 fp = open(os.path.join(path,file), 'r') 310 data = fp.read() 311 fp.close() 312 exposed = regex_expose.findall(data) 313 for function in exposed: 314 command = data + "\nresponse._vars=response._caller(%s)\n" % \ 315 function 316 filename = os.path.join(folder, 'compiled', ('controllers/' 317 + file[:-3]).replace('/', '_') 318 + '_' + function + '.py') 319 fp = open(filename, 'w') 320 fp.write(command) 321 fp.close() 322 save_pyc(filename) 323 os.unlink(filename)
324 325
326 -def run_models_in(environment):
327 """ 328 Runs all models (in the app specified by the current folder) 329 It tries pre-compiled models first before compiling them. 330 """ 331 332 folder = environment['request'].folder 333 path = os.path.join(folder, 'compiled') 334 if os.path.exists(path): 335 for model in listdir(path, '^models_.+\.pyc$', 0): 336 restricted(read_pyc(model), environment, layer=model) 337 else: 338 models = listdir(os.path.join(folder, 'models'), '^\w+\.py$', 339 0) 340 for model in models: 341 layer = model 342 if is_gae: 343 code = getcfs(model, model, 344 lambda: compile2(open(model, 'r').read(),layer)) 345 else: 346 code = getcfs(model, model, None) 347 restricted(code, environment, layer)
348 349
350 -def run_controller_in(controller, function, environment):
351 """ 352 Runs the controller.function() (for the app specified by 353 the current folder). 354 It tries pre-compiled controller_function.pyc first before compiling it. 355 """ 356 357 # if compiled should run compiled! 358 359 folder = environment['request'].folder 360 path = os.path.join(folder, 'compiled') 361 badc = 'invalid controller (%s/%s)' % (controller, function) 362 badf = 'invalid function (%s/%s)' % (controller, function) 363 if os.path.exists(path): 364 filename = os.path.join(path, 'controllers_%s_%s.pyc' 365 % (controller, function)) 366 if not os.path.exists(filename): 367 raise HTTP(404, 368 rewrite.thread.routes.error_message % badf, 369 web2py_error=badf) 370 restricted(read_pyc(filename), environment, layer=filename) 371 elif function == '_TEST': 372 filename = os.path.join(folder, 'controllers/%s.py' 373 % controller) 374 if not os.path.exists(filename): 375 raise HTTP(404, 376 rewrite.thread.routes.error_message % badc, 377 web2py_error=badc) 378 environment['__symbols__'] = environment.keys() 379 fp = open(filename, 'r') 380 code = fp.read() 381 fp.close() 382 code += TEST_CODE 383 restricted(code, environment, layer=filename) 384 else: 385 filename = os.path.join(folder, 'controllers/%s.py' 386 % controller) 387 if not os.path.exists(filename): 388 raise HTTP(404, 389 rewrite.thread.routes.error_message % badc, 390 web2py_error=badc) 391 fp = open(filename, 'r') 392 code = fp.read() 393 fp.close() 394 exposed = regex_expose.findall(code) 395 if not function in exposed: 396 raise HTTP(404, 397 rewrite.thread.routes.error_message % badf, 398 web2py_error=badf) 399 code = "%s\nresponse._vars=response._caller(%s)\n" % (code, function) 400 if is_gae: 401 layer = filename + ':' + function 402 code = getcfs(layer, filename, lambda: compile2(code,layer)) 403 restricted(code, environment, filename) 404 response = environment['response'] 405 vars=response._vars 406 if response.postprocessing: 407 for p in response.postprocessing: 408 vars = p(vars) 409 if isinstance(vars,unicode): 410 vars = vars.encode('utf8') 411 if hasattr(vars,'xml'): 412 vars = vars.xml() 413 return vars
414
415 -def run_view_in(environment):
416 """ 417 Executes the view for the requested action. 418 The view is the one specified in `response.view` or determined by the url 419 or `view/generic.extension` 420 It tries the pre-compiled views_controller_function.pyc before compiling it. 421 """ 422 423 request = environment['request'] 424 response = environment['response'] 425 folder = request.folder 426 path = os.path.join(folder, 'compiled') 427 badv = 'invalid view (%s)' % response.view 428 if not isinstance(response.view, str): 429 ccode = parse_template(response.view, os.path.join(folder, 'views'), 430 context=environment) 431 restricted(ccode, environment, 'file stream') 432 elif os.path.exists(path): 433 x = response.view.replace('/', '_') 434 if request.extension == 'html': 435 # for backward compatibility 436 files = [os.path.join(path, 'views_%s.pyc' % x), 437 os.path.join(path, 'views_%s.pyc' % x[:-5]), 438 os.path.join(path, 'views_generic.html.pyc'), 439 os.path.join(path, 'views_generic.pyc')] 440 else: 441 files = [os.path.join(path, 'views_%s.pyc' % x), 442 os.path.join(path, 'views_generic.%s.pyc' 443 % request.extension)] 444 for filename in files: 445 if os.path.exists(filename): 446 code = read_pyc(filename) 447 restricted(code, environment, layer=filename) 448 return 449 raise HTTP(404, 450 rewrite.thread.routes.error_message % badv, 451 web2py_error=badv) 452 else: 453 filename = os.path.join(folder, 'views', response.view) 454 if not os.path.exists(filename): 455 response.view = 'generic.' + request.extension 456 filename = os.path.join(folder, 'views', response.view) 457 if not os.path.exists(filename): 458 raise HTTP(404, 459 rewrite.thread.routes.error_message % badv, 460 web2py_error=badv) 461 layer = filename 462 if is_gae: 463 ccode = getcfs(layer, filename, 464 lambda: compile2(parse_template(response.view, 465 os.path.join(folder, 'views'), 466 context=environment),layer)) 467 else: 468 ccode = parse_template(response.view, 469 os.path.join(folder, 'views'), 470 context=environment) 471 restricted(ccode, environment, layer)
472
473 -def remove_compiled_application(folder):
474 """ 475 Deletes the folder `compiled` containing the compiled application. 476 """ 477 try: 478 path = os.path.join(folder, 'compiled') 479 for file in listdir(path): 480 os.unlink(os.path.join(path, file)) 481 os.rmdir(path) 482 path = os.path.join(folder, 'controllers') 483 for file in os.listdir(path): 484 if file.endswith('.pyc'): 485 os.unlink(os.path.join(path, file)) 486 except OSError: 487 pass
488 489
490 -def compile_application(folder):
491 """ 492 Compiles all models, views, controller for the application in `folder`. 493 """ 494 remove_compiled_application(folder) 495 os.mkdir(os.path.join(folder, 'compiled')) 496 compile_models(folder) 497 compile_controllers(folder) 498 compile_views(folder)
499 500
501 -def test():
502 """ 503 Example:: 504 505 >>> import traceback, types 506 >>> environment={'x':1} 507 >>> open('a.py', 'w').write('print 1/x') 508 >>> save_pyc('a.py') 509 >>> os.unlink('a.py') 510 >>> if type(read_pyc('a.pyc'))==types.CodeType: print 'code' 511 code 512 >>> exec read_pyc('a.pyc') in environment 513 1 514 """ 515 516 return
517 518 519 if __name__ == '__main__': 520 import doctest 521 doctest.testmod() 522