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