Package web2py :: Package gluon :: Module shell
[hide private]
[frames] | no frames]

Source Code for Module web2py.gluon.shell

  1  #!/usr/bin/env python 
  2  # -*- coding: utf-8 -*- 
  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 # Populate the dummy environment with sensible defaults. 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 # Monkey patch so credentials checks pass. 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
140 -def exec_pythonrc():
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 # following 2 lines fix a problem with 239 # IPython; thanks Michael Toomim 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
260 -def parse_path_info(path_info):
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
276 -def die(msg):
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 # Reload environment before each test. 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 # Execute . operator so decorators are executed. 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
350 -def get_usage():
351 usage = """ 352 %prog [options] pythonfile 353 """ 354 return usage
355 356
357 -def execute_from_command_line(argv=None):
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