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

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