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 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
85 """
86 Attention: this helper is new and experimental
87 """
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
135
136
137
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
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
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
214
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
231 environment['SQLField'] = SQLField
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
244 """
245 Bytecode compiles the file `filename`
246 """
247 py_compile.compile(filename)
248
249
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
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
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
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
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
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
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
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
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
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
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
499
500
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