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 return environment
131
132
134 pythonrc = os.environ.get('PYTHONSTARTUP')
135 if pythonrc and os.path.isfile(pythonrc):
136 try:
137 execfile(pythonrc)
138 except NameError:
139 pass
140
141
142 -def run(
143 appname,
144 plain=False,
145 import_models=False,
146 startfile=None,
147 bpython=False
148 ):
149 """
150 Start interactive shell or run Python script (startfile) in web2py
151 controller environment. appname is formatted like:
152
153 a web2py application name
154 a/c exec the controller c into the application environment
155 """
156
157 (a, c, f) = parse_path_info(appname)
158 errmsg = 'invalid application name: %s' % appname
159 if not a:
160 die(errmsg)
161 adir = os.path.join('applications', a)
162 if not os.path.exists(adir):
163 if raw_input('application %s does not exist, create (y/n)?'
164 % a).lower() in ['y', 'yes']:
165 os.mkdir(adir)
166 w2p_unpack('welcome.w2p', adir)
167 for subfolder in ['models','views','controllers', 'databases',
168 'modules','cron','errors','sessions',
169 'languages','static','private','uploads']:
170 subpath = os.path.join(adir,subfolder)
171 if not os.path.exists(subpath):
172 os.mkdir(subpath)
173 db = os.path.join(adir,'models/db.py')
174 if os.path.exists(db):
175 fp = open(db,'r')
176 data = fp.read()
177 fp.close()
178 data = data.replace('<your secret key>','sha512:'+web2py_uuid())
179 fp = open(db,'w')
180 fp.write(data)
181 fp.close()
182
183 if c:
184 import_models = True
185 _env = env(a, c=c, import_models=import_models)
186 if c:
187 cfile = os.path.join('applications', a, 'controllers', c + '.py')
188 if not os.path.isfile(cfile):
189 cfile = os.path.join('applications', a, 'compiled', "controllers_%s_%s.pyc" % (c,f))
190 if not os.path.isfile(cfile):
191 die(errmsg)
192 else:
193 exec read_pyc(cfile) in _env
194 else:
195 execfile(cfile, _env)
196
197 if f:
198 exec ('print %s()' % f, _env)
199 elif startfile:
200 exec_pythonrc()
201 try:
202 execfile(startfile, _env)
203 except RestrictedError, e:
204 print e.traceback
205 else:
206 if not plain:
207 if bpython:
208 try:
209 import bpython
210 bpython.embed(locals_=_env)
211 return
212 except:
213 logger.warning(
214 'import bpython error; trying ipython...')
215 else:
216 try:
217 import IPython
218
219 if '__builtins__' in _env:
220 del _env['__builtins__']
221 shell = IPython.Shell.IPShell(argv=[], user_ns=_env)
222 shell.mainloop()
223 return
224 except:
225 logger.warning(
226 'import IPython error; use default python shell')
227 try:
228 import readline
229 import rlcompleter
230 except ImportError:
231 pass
232 else:
233 readline.set_completer(rlcompleter.Completer(_env).complete)
234 readline.parse_and_bind('tab:complete')
235 exec_pythonrc()
236 code.interact(local=_env)
237
238
240 """
241 Parse path info formatted like a/c/f where c and f are optional
242 and a leading / accepted.
243 Return tuple (a, c, f). If invalid path_info a is set to None.
244 If c or f are omitted they are set to None.
245 """
246
247 mo = re.match(r'^/?(?P<a>\w+)(/(?P<c>\w+)(/(?P<f>\w+))?)?$',
248 path_info)
249 if mo:
250 return (mo.group('a'), mo.group('c'), mo.group('f'))
251 else:
252 return (None, None, None)
253
254
256 print >> sys.stderr, msg
257 sys.exit(1)
258
259
260 -def test(testpath, import_models=True, verbose=False):
261 """
262 Run doctests in web2py environment. testpath is formatted like:
263
264 a tests all controllers in application a
265 a/c tests controller c in application a
266 a/c/f test function f in controller c, application a
267
268 Where a, c and f are application, controller and function names
269 respectively. If the testpath is a file name the file is tested.
270 If a controller is specified models are executed by default.
271 """
272
273 import doctest
274 if os.path.isfile(testpath):
275 mo = re.match(r'(|.*/)applications/(?P<a>[^/]+)', testpath)
276 if not mo:
277 die('test file is not in application directory: %s'
278 % testpath)
279 a = mo.group('a')
280 c = f = None
281 files = [testpath]
282 else:
283 (a, c, f) = parse_path_info(testpath)
284 errmsg = 'invalid test path: %s' % testpath
285 if not a:
286 die(errmsg)
287 cdir = os.path.join('applications', a, 'controllers')
288 if not os.path.isdir(cdir):
289 die(errmsg)
290 if c:
291 cfile = os.path.join(cdir, c + '.py')
292 if not os.path.isfile(cfile):
293 die(errmsg)
294 files = [cfile]
295 else:
296 files = glob.glob(os.path.join(cdir, '*.py'))
297 for testfile in files:
298 globs = env(a, import_models)
299 ignores = globs.keys()
300 execfile(testfile, globs)
301
302 def doctest_object(name, obj):
303 """doctest obj and enclosed methods and classes."""
304
305 if type(obj) in (types.FunctionType, types.TypeType,
306 types.ClassType, types.MethodType,
307 types.UnboundMethodType):
308
309
310
311 globs = env(a, c=c, f=f, import_models=import_models)
312 execfile(testfile, globs)
313 doctest.run_docstring_examples(obj, globs=globs,
314 name='%s: %s' % (os.path.basename(testfile),
315 name), verbose=verbose)
316 if type(obj) in (types.TypeType, types.ClassType):
317 for attr_name in dir(obj):
318
319
320
321 o = eval('%s.%s' % (name, attr_name), globs)
322 doctest_object(attr_name, o)
323
324 for (name, obj) in globs.items():
325 if name not in ignores and (f is None or f == name):
326 doctest_object(name, obj)
327
328
330 usage = """
331 %prog [options] pythonfile
332 """
333 return usage
334
335
337 if argv is None:
338 argv = sys.argv
339
340 parser = optparse.OptionParser(usage=get_usage())
341
342 parser.add_option('-S', '--shell', dest='shell', metavar='APPNAME',
343 help='run web2py in interactive shell or IPython(if installed) ' + \
344 'with specified appname')
345 msg = 'run web2py in interactive shell or bpython (if installed) with'
346 msg += ' specified appname (if app does not exist it will be created).'
347 msg += '\n Use combined with --shell'
348 parser.add_option(
349 '-B',
350 '--bpython',
351 action='store_true',
352 default=False,
353 dest='bpython',
354 help=msg,
355 )
356 parser.add_option(
357 '-P',
358 '--plain',
359 action='store_true',
360 default=False,
361 dest='plain',
362 help='only use plain python shell, should be used with --shell option',
363 )
364 parser.add_option(
365 '-M',
366 '--import_models',
367 action='store_true',
368 default=False,
369 dest='import_models',
370 help='auto import model files, default is False, ' + \
371 ' should be used with --shell option',
372 )
373 parser.add_option(
374 '-R',
375 '--run',
376 dest='run',
377 metavar='PYTHON_FILE',
378 default='',
379 help='run PYTHON_FILE in web2py environment, ' + \
380 'should be used with --shell option',
381 )
382
383 (options, args) = parser.parse_args(argv[1:])
384
385 if len(sys.argv) == 1:
386 parser.print_help()
387 sys.exit(0)
388
389 if len(args) > 0:
390 startfile = args[0]
391 else:
392 startfile = ''
393 run(options.shell, options.plain, startfile=startfile, bpython=options.bpython)
394
395
396 if __name__ == '__main__':
397 execute_from_command_line()
398