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 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
142
143
144
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
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
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
215
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
232 environment['SQLField'] = SQLField
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
245 """
246 Bytecode compiles the file `filename`
247 """
248 py_compile.compile(filename)
249
250
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
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
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
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 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
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
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
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
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
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
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
500
501
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