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