1
2
3
4 """
5 This file is part of the web2py Web Framework
6 Developed by Massimo Di Pierro <mdipierro@cs.depaul.edu>,
7 limodou <limodou@gmail.com> and srackham <srackham@gmail.com>.
8 License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
9
10 """
11
12 import os
13 import sys
14 import code
15 import logging
16 import types
17 import re
18 import optparse
19 import glob
20 import traceback
21 import fileutils
22 import settings
23 from utils import web2py_uuid
24 from compileapp import build_environment, read_pyc, run_models_in
25 from restricted import RestrictedError
26 from globals import Request, Response, Session
27 from storage import Storage
28 from admin import w2p_unpack
29 from dal import BaseAdapter
30
31
32 logger = logging.getLogger("web2py")
33
34 -def exec_environment(
35 pyfile='',
36 request=None,
37 response=None,
38 session=None,
39 ):
40 """
41 .. function:: gluon.shell.exec_environment([pyfile=''[, request=Request()
42 [, response=Response[, session=Session()]]]])
43
44 Environment builder and module loader.
45
46
47 Builds a web2py environment and optionally executes a Python
48 file into the environment.
49 A Storage dictionary containing the resulting environment is returned.
50 The working directory must be web2py root -- this is the web2py default.
51
52 """
53
54 if request is None: request = Request()
55 if response is None: response = Response()
56 if session is None: session = Session()
57
58 if request.folder is None:
59 mo = re.match(r'(|.*/)applications/(?P<appname>[^/]+)', pyfile)
60 if mo:
61 appname = mo.group('appname')
62 request.folder = os.path.join('applications', appname)
63 else:
64 request.folder = ''
65 env = build_environment(request, response, session, store_current=False)
66 if pyfile:
67 pycfile = pyfile + 'c'
68 if os.path.isfile(pycfile):
69 exec read_pyc(pycfile) in env
70 else:
71 execfile(pyfile, env)
72 return Storage(env)
73
74
75 -def env(
76 a,
77 import_models=False,
78 c=None,
79 f=None,
80 dir='',
81 extra_request={},
82 ):
83 """
84 Return web2py execution environment for application (a), controller (c),
85 function (f).
86 If import_models is True the exec all application models into the
87 environment.
88
89 extra_request allows you to pass along any extra
90 variables to the request object before your models
91 get executed. This was mainly done to support
92 web2py_utils.test_runner, however you can use it
93 with any wrapper scripts that need access to the
94 web2py environment.
95 """
96
97 request = Request()
98 response = Response()
99 session = Session()
100 request.application = a
101
102
103
104 if not dir:
105 request.folder = os.path.join('applications', a)
106 else:
107 request.folder = dir
108 request.controller = c or 'default'
109 request.function = f or 'index'
110 response.view = '%s/%s.html' % (request.controller,
111 request.function)
112 request.env.path_info = '/%s/%s/%s' % (a, c, f)
113 request.env.http_host = '127.0.0.1:8000'
114 request.env.remote_addr = '127.0.0.1'
115 request.env.web2py_runtime_gae = settings.global_settings.web2py_runtime_gae
116
117 for k,v in extra_request.items():
118 request[k] = v
119
120
121
122 def check_credentials(request, other_application='admin'):
123 return True
124
125 fileutils.check_credentials = check_credentials
126
127 environment = build_environment(request, response, session)
128
129 if import_models:
130 try:
131 run_models_in(environment)
132 except RestrictedError, e:
133 sys.stderr.write(e.traceback+'\n')
134 sys.exit(1)
135
136 environment['__name__'] = '__main__'
137 return environment
138
139
141 pythonrc = os.environ.get('PYTHONSTARTUP')
142 if pythonrc and os.path.isfile(pythonrc):
143 try:
144 execfile(pythonrc)
145 except NameError:
146 pass
147
148
149 -def run(
150 appname,
151 plain=False,
152 import_models=False,
153 startfile=None,
154 bpython=False,
155 python_code=False
156 ):
157 """
158 Start interactive shell or run Python script (startfile) in web2py
159 controller environment. appname is formatted like:
160
161 a web2py application name
162 a/c exec the controller c into the application environment
163 """
164
165 (a, c, f) = parse_path_info(appname)
166 errmsg = 'invalid application name: %s' % appname
167 if not a:
168 die(errmsg)
169 adir = os.path.join('applications', a)
170 if not os.path.exists(adir):
171 if raw_input('application %s does not exist, create (y/n)?'
172 % a).lower() in ['y', 'yes']:
173 os.mkdir(adir)
174 w2p_unpack('welcome.w2p', adir)
175 for subfolder in ['models','views','controllers', 'databases',
176 'modules','cron','errors','sessions',
177 'languages','static','private','uploads']:
178 subpath = os.path.join(adir,subfolder)
179 if not os.path.exists(subpath):
180 os.mkdir(subpath)
181 db = os.path.join(adir,'models/db.py')
182 if os.path.exists(db):
183 data = fileutils.read_file(db)
184 data = data.replace('<your secret key>','sha512:'+web2py_uuid())
185 fileutils.write_file(db, data)
186
187 if c:
188 import_models = True
189 _env = env(a, c=c, import_models=import_models)
190 if c:
191 cfile = os.path.join('applications', a, 'controllers', c + '.py')
192 if not os.path.isfile(cfile):
193 cfile = os.path.join('applications', a, 'compiled', "controllers_%s_%s.pyc" % (c,f))
194 if not os.path.isfile(cfile):
195 die(errmsg)
196 else:
197 exec read_pyc(cfile) in _env
198 else:
199 execfile(cfile, _env)
200
201 if f:
202 exec ('print %s()' % f, _env)
203 elif startfile:
204 exec_pythonrc()
205 try:
206 execfile(startfile, _env)
207 if import_models: BaseAdapter.close_all_instances('commit')
208 except Exception, e:
209 print traceback.format_exc()
210 if import_models: BaseAdapter.close_all_instances('rollback')
211 elif python_code:
212 exec_pythonrc()
213 try:
214 exec(python_code, _env)
215 if import_models: BaseAdapter.close_all_instances('commit')
216 except Exception, e:
217 print traceback.format_exc()
218 if import_models: BaseAdapter.close_all_instances('rollback')
219 else:
220 if not plain:
221 if bpython:
222 try:
223 import bpython
224 bpython.embed(locals_=_env)
225 return
226 except:
227 logger.warning(
228 'import bpython error; trying ipython...')
229 else:
230 try:
231 import IPython
232 if IPython.__version__ >= '0.11':
233 from IPython.frontend.terminal.embed import InteractiveShellEmbed
234 shell = InteractiveShellEmbed(user_ns=_env)
235 shell()
236 return
237 else:
238
239
240 if '__builtins__' in _env:
241 del _env['__builtins__']
242 shell = IPython.Shell.IPShell(argv=[],user_ns=_env)
243 shell.mainloop()
244 return
245 except:
246 logger.warning(
247 'import IPython error; use default python shell')
248 try:
249 import readline
250 import rlcompleter
251 except ImportError:
252 pass
253 else:
254 readline.set_completer(rlcompleter.Completer(_env).complete)
255 readline.parse_and_bind('tab:complete')
256 exec_pythonrc()
257 code.interact(local=_env)
258
259
261 """
262 Parse path info formatted like a/c/f where c and f are optional
263 and a leading / accepted.
264 Return tuple (a, c, f). If invalid path_info a is set to None.
265 If c or f are omitted they are set to None.
266 """
267
268 mo = re.match(r'^/?(?P<a>\w+)(/(?P<c>\w+)(/(?P<f>\w+))?)?$',
269 path_info)
270 if mo:
271 return (mo.group('a'), mo.group('c'), mo.group('f'))
272 else:
273 return (None, None, None)
274
275
277 print >> sys.stderr, msg
278 sys.exit(1)
279
280
281 -def test(testpath, import_models=True, verbose=False):
282 """
283 Run doctests in web2py environment. testpath is formatted like:
284
285 a tests all controllers in application a
286 a/c tests controller c in application a
287 a/c/f test function f in controller c, application a
288
289 Where a, c and f are application, controller and function names
290 respectively. If the testpath is a file name the file is tested.
291 If a controller is specified models are executed by default.
292 """
293
294 import doctest
295 if os.path.isfile(testpath):
296 mo = re.match(r'(|.*/)applications/(?P<a>[^/]+)', testpath)
297 if not mo:
298 die('test file is not in application directory: %s'
299 % testpath)
300 a = mo.group('a')
301 c = f = None
302 files = [testpath]
303 else:
304 (a, c, f) = parse_path_info(testpath)
305 errmsg = 'invalid test path: %s' % testpath
306 if not a:
307 die(errmsg)
308 cdir = os.path.join('applications', a, 'controllers')
309 if not os.path.isdir(cdir):
310 die(errmsg)
311 if c:
312 cfile = os.path.join(cdir, c + '.py')
313 if not os.path.isfile(cfile):
314 die(errmsg)
315 files = [cfile]
316 else:
317 files = glob.glob(os.path.join(cdir, '*.py'))
318 for testfile in files:
319 globs = env(a, import_models)
320 ignores = globs.keys()
321 execfile(testfile, globs)
322
323 def doctest_object(name, obj):
324 """doctest obj and enclosed methods and classes."""
325
326 if type(obj) in (types.FunctionType, types.TypeType,
327 types.ClassType, types.MethodType,
328 types.UnboundMethodType):
329
330
331
332 globs = env(a, c=c, f=f, import_models=import_models)
333 execfile(testfile, globs)
334 doctest.run_docstring_examples(obj, globs=globs,
335 name='%s: %s' % (os.path.basename(testfile),
336 name), verbose=verbose)
337 if type(obj) in (types.TypeType, types.ClassType):
338 for attr_name in dir(obj):
339
340
341
342 o = eval('%s.%s' % (name, attr_name), globs)
343 doctest_object(attr_name, o)
344
345 for (name, obj) in globs.items():
346 if name not in ignores and (f is None or f == name):
347 doctest_object(name, obj)
348
349
351 usage = """
352 %prog [options] pythonfile
353 """
354 return usage
355
356
358 if argv is None:
359 argv = sys.argv
360
361 parser = optparse.OptionParser(usage=get_usage())
362
363 parser.add_option('-S', '--shell', dest='shell', metavar='APPNAME',
364 help='run web2py in interactive shell or IPython(if installed) ' + \
365 'with specified appname')
366 msg = 'run web2py in interactive shell or bpython (if installed) with'
367 msg += ' specified appname (if app does not exist it will be created).'
368 msg += '\n Use combined with --shell'
369 parser.add_option(
370 '-B',
371 '--bpython',
372 action='store_true',
373 default=False,
374 dest='bpython',
375 help=msg,
376 )
377 parser.add_option(
378 '-P',
379 '--plain',
380 action='store_true',
381 default=False,
382 dest='plain',
383 help='only use plain python shell, should be used with --shell option',
384 )
385 parser.add_option(
386 '-M',
387 '--import_models',
388 action='store_true',
389 default=False,
390 dest='import_models',
391 help='auto import model files, default is False, ' + \
392 ' should be used with --shell option',
393 )
394 parser.add_option(
395 '-R',
396 '--run',
397 dest='run',
398 metavar='PYTHON_FILE',
399 default='',
400 help='run PYTHON_FILE in web2py environment, ' + \
401 'should be used with --shell option',
402 )
403
404 (options, args) = parser.parse_args(argv[1:])
405
406 if len(sys.argv) == 1:
407 parser.print_help()
408 sys.exit(0)
409
410 if len(args) > 0:
411 startfile = args[0]
412 else:
413 startfile = ''
414 run(options.shell, options.plain, startfile=startfile, bpython=options.bpython)
415
416
417 if __name__ == '__main__':
418 execute_from_command_line()
419