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