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