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