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   
 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   
33 -def exec_environment( 34 pyfile='', 35 request=Request(), 36 response=Response(), 37 session=Session(), 38 ):
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 # Populate the dummy environment with sensible defaults. 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 # Monkey patch so credentials checks pass. 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
135 -def exec_pythonrc():
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 # following 2 lines fix a problem with IPython; thanks Michael Toomim 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
237 -def parse_path_info(path_info):
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
253 -def die(msg):
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 # Reload environment before each test. 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 # Execute . operator so decorators are executed. 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
327 -def get_usage():
328 usage = """ 329 %prog [options] pythonfile 330 """ 331 return usage
332 333
334 -def execute_from_command_line(argv=None):
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