Package turbogears :: Package command :: Module base

Source Code for Module turbogears.command.base

  1  """Commands for the TurboGears command line tool.""" 
  2   
  3  __all__ = ['main'] 
  4   
  5  import glob 
  6  import optparse 
  7  import os 
  8  import sys 
  9   
 10  import pkg_resources 
 11  from peak.rules import NoApplicableMethods 
 12   
 13  from turbogears import config, database, startup, view, widgets 
 14  from turbogears.identity import SecureObject, from_any_host 
 15  from turbogears.util import (get_model, get_project_name, get_project_config, 
 16      get_package_name, load_project_config) 
 17   
 18  from sacommand import sacommand 
 19   
 20  sys.path.insert(0, os.getcwd()) 
 21   
 22  no_connection_param = ['help', 'list'] 
 23  no_model_param = ['help'] 
 24   
 25   
26 -def silent_os_remove(fname):
27 """Try to remove file 'fname' but mute any error that may happen. 28 29 Returns True if file was actually removed and False otherwise. 30 31 """ 32 try: 33 os.remove(fname) 34 except os.error: 35 pass 36 else: 37 return True 38 return False
39 40
41 -class CommandWithDB(object):
42 """Base class for commands that need to use the database""" 43 44 config = None 45
46 - def __init__(self, version):
47 pass
48
49 - def find_config(self):
50 """Chooses the config file, trying to guess whether this is a 51 development or installed project.""" 52 load_project_config(self.config) 53 self.dburi = config.get('sqlobject.dburi', None) 54 if self.dburi and self.dburi.startswith('notrans_'): 55 self.dburi = self.dburi[8:]
56 57
58 -class SQL(CommandWithDB):
59 """Wrapper command for sqlobject-admin, and some sqlalchemy support. 60 61 This automatically supplies sqlobject-admin with the database that 62 is found in the config file. 63 64 Will also supply the model module as appropriate. 65 66 """ 67 68 desc = "Run the database provider manager" 69 need_project = True 70
71 - def __init__(self, version):
72 if len(sys.argv) == 1 or sys.argv[1][0] == '-': 73 parser = optparse.OptionParser( 74 usage="%prog sql [command]\n\nhint: '%prog sql help'" 75 " will list the sql specific commands", 76 version='%prog ' + version) 77 parser.add_option('-c', '--config', help="config file", 78 dest='config') 79 options, args = parser.parse_args(sys.argv[1:3]) 80 81 if not options.config: 82 parser.error("Please provide a valid option or command.") 83 self.config = options.config 84 # get rid of our config option 85 if args: 86 del sys.argv[1] 87 else: 88 del sys.argv[1:3] 89 90 self.find_config()
91
92 - def run(self):
93 """Run the sqlobject-admin tool or sacommand module functions.""" 94 95 if not '--egg' in sys.argv and not get_project_name(): 96 print "This doesn't look like a TurboGears project." 97 return 98 else: 99 sqlcommand = sys.argv[1] 100 101 if config.get('sqlalchemy.dburi'): 102 try: 103 sacommand(sqlcommand, sys.argv) 104 except NoApplicableMethods: 105 sacommand('help', []) 106 return 107 108 try: 109 from sqlobject.manager import command as socommand 110 except ImportError: 111 from turbogears.util import missing_dependency_error 112 print missing_dependency_error('SQLObject') 113 return 114 115 if sqlcommand not in no_connection_param: 116 if self.dburi: 117 print "Using database URI %s" % self.dburi 118 sys.argv.insert(2, self.dburi) 119 sys.argv.insert(2, "-c") 120 else: 121 print ("Database URI not specified in the config file" 122 " (%s).\nPlease be sure it's on the command line." 123 % (self.config or get_project_config())) 124 125 if sqlcommand not in no_model_param: 126 if not '--egg' in sys.argv: 127 eggname = glob.glob('*.egg-info') 128 if not eggname or not os.path.exists( 129 os.path.join(eggname[0], 'sqlobject.txt')): 130 eggname = self.fix_egginfo(eggname) 131 eggname = eggname[0].replace('.egg-info', '') 132 if not '.' in sys.path: 133 sys.path.append('.') 134 pkg_resources.working_set.add_entry('.') 135 sys.argv.insert(2, eggname) 136 sys.argv.insert(2, '--egg') 137 138 socommand.the_runner.run(sys.argv)
139
140 - def fix_egginfo(self, eggname):
141 """Add egg-info directory if necessary.""" 142 print """ 143 This project seems incomplete. In order to use the sqlobject commands 144 without manually specifying a model, there needs to be an 145 egg-info directory with an appropriate sqlobject.txt file. 146 147 I can fix this automatically. Would you like me to? 148 """ 149 dofix = raw_input("Enter [y] or n: ") 150 if not dofix or dofix.lower()[0] == 'y': 151 oldargs = sys.argv 152 sys.argv = ['setup.py', 'egg_info'] 153 import imp 154 imp.load_module('setup', *imp.find_module('setup', ['.'])) 155 sys.argv = oldargs 156 157 import setuptools 158 package = setuptools.find_packages()[0] 159 eggname = glob.glob('*.egg-info') 160 sqlobjectmeta = open(os.path.join(eggname[0], 'sqlobject.txt'), 'w') 161 sqlobjectmeta.write('db_module=%(package)s.model\n' 162 'history_dir=$base/%(package)s/sqlobject-history' 163 % dict(package=package)) 164 else: 165 sys.exit(0) 166 return eggname
167 168
169 -class Shell(CommandWithDB):
170 """Convenient version of the Python interactive shell. 171 172 This shell attempts to locate your configuration file and model module 173 so that it can import everything from your model and make it available 174 in the Python shell namespace. 175 176 """ 177 178 desc = "Start a Python prompt with your database available" 179 need_project = True 180
181 - def run(self):
182 """Run the shell""" 183 self.find_config() 184 185 locals = dict(__name__='tg-admin') 186 try: 187 mod = get_model() 188 if mod: 189 locals.update(mod.__dict__) 190 except (pkg_resources.DistributionNotFound, ImportError), e: 191 mod = None 192 print "Warning: Failed to import your data model: %s" % e 193 print "You will not have access to your data model objects." 194 print 195 196 if config.get('sqlalchemy.dburi'): 197 using_sqlalchemy = True 198 database.bind_metadata() 199 locals.update(session=database.session, 200 metadata=database.metadata) 201 else: 202 using_sqlalchemy = False 203 204 class CustomShellMixin(object): 205 def commit_changes(self): 206 if mod: 207 # XXX Can we check somehow, if there are actually any 208 # database changes to be commited? 209 r = raw_input("Do you wish to commit" 210 " your database changes? [yes]") 211 if not r.startswith('n'): 212 if using_sqlalchemy: 213 self.push("session.flush()") 214 else: 215 self.push("hub.commit()")
216 217 try: # use IPython if possible 218 try: 219 from IPython.terminal.interactiveshell \ 220 import TerminalInteractiveShell as InteractiveShell 221 except ImportError: # very old IPython 222 from IPython.iplib import Interactiveshell 223 224 class CustomShell(InteractiveShell, CustomShellMixin): 225 def raw_input(self, *args, **kw): 226 try: 227 return InteractiveShell.raw_input(self, *args, **kw) 228 except EOFError: 229 self.commit_changes() 230 raise EOFError 231 232 shell = CustomShell(user_ns=locals, shell_class=CustomShell) 233 shell.mainloop() 234 except ImportError: 235 import code 236 237 class CustomShell(code.InteractiveConsole, CustomShellMixin): 238 def raw_input(self, *args, **kw): 239 try: 240 import readline 241 except ImportError: 242 pass 243 try: 244 r = code.InteractiveConsole.raw_input(self, 245 *args, **kw) 246 for encoding in (getattr(sys.stdin, 'encoding', None), 247 sys.getdefaultencoding(), 'utf-8', 'latin-1'): 248 if encoding: 249 try: 250 return r.decode(encoding) 251 except UnicodeError: 252 pass 253 return r 254 except EOFError: 255 self.commit_changes() 256 raise EOFError 257 258 shell = CustomShell(locals=locals) 259 shell.interact() 260 261
262 -class ToolboxCommand(CommandWithDB):
263 264 desc = "Launch the TurboGears Toolbox" 265
266 - def __init__(self, version):
267 self.hostlist = ['127.0.0.1', '::1'] 268 269 parser = optparse.OptionParser( 270 usage="%prog toolbox [options]", 271 version='%prog ' + version) 272 parser.add_option('-n', '--no-open', 273 help="don't open browser automatically", 274 dest='noopen', action='store_true', default=False) 275 parser.add_option('-c', '--add-client', 276 help="allow client ip address specified to connect to toolbox" 277 " (can be specified more than once)", 278 dest='host', action='append', default=None) 279 parser.add_option('-p', '--port', 280 help="port to run the Toolbox on", 281 dest='port', default=7654) 282 parser.add_option('--config', help="config file to use", 283 dest='config', default=self.config or get_project_config()) 284 285 options, args = parser.parse_args(sys.argv[1:]) 286 self.port = int(options.port) 287 self.noopen = options.noopen 288 self.config = options.config 289 290 if options.host: 291 self.hostlist.extend(options.host) 292 293 widgets.load_widgets()
294
295 - def openbrowser(self):
296 import webbrowser 297 webbrowser.open('http://localhost:%d' % self.port)
298
299 - def run(self):
300 import cherrypy 301 from turbogears import toolbox 302 303 # Make sure we have full configuration with every option 304 # in it so other plugins or whatever find what they need 305 # when starting even inside the toolblox 306 conf = get_package_name() 307 conf = conf and "%s.config" % conf or None 308 conf = config.config_obj(configfile=self.config, modulename=conf) 309 310 if 'global' in conf: 311 config.update({'global': conf['global']}) 312 313 # amend some parameters since we are running from the command 314 # line in order to change port, log methods... 315 config.update({'global': { 316 'server.socket_port': self.port, 317 'server.webpath': '', 318 'environment': 'production', 319 'engine.autoreload.on': False, 320 'server.package': 'turbogears.toolbox', 321 'identity.failure_url': '/noaccess', 322 'identity.force_external_redirect': False, 323 'tg.strict_parameters': False, 324 'tg.defaultview': 'genshi', 325 'genshi.default_doctype': 'html', 326 'genshi.default_encoding': 'utf-8', 327 }}) 328 329 startup.config_static() 330 331 root = SecureObject(toolbox.Toolbox(), 332 from_any_host(self.hostlist), exclude=['noaccess']) 333 cherrypy.tree.mount(root, '/', config=config.app) 334 335 view.load_engines() 336 if self.noopen: 337 cherrypy.engine.start() 338 else: 339 cherrypy.engine.start_with_callback(self.openbrowser) 340 cherrypy.engine.block()
341 342 343 commands = None 344
345 -def main():
346 """Main command runner. Manages the primary command line arguments.""" 347 # add commands defined by entrypoints 348 commands = {} 349 for entrypoint in pkg_resources.iter_entry_points('turbogears.command'): 350 command = entrypoint.load() 351 commands[entrypoint.name] = (command.desc, entrypoint) 352 from turbogears import __version__ 353 354 def _help(): 355 """Custom help text for tg-admin.""" 356 357 print """ 358 TurboGears %s command line interface 359 360 Usage: %s [options] <command> 361 362 Options: 363 -c CONFIG --config=CONFIG Config file to use 364 -e EGG_SPEC --egg=EGG_SPEC Run command on given egg 365 -h --help Print help (about command) 366 367 Commands:""" % (__version__, sys.argv[0]) 368 369 longest = max([len(key) for key in commands.keys()]) 370 format = '%' + str(longest) + 's %s' 371 commandlist = commands.keys() 372 commandlist.sort() 373 for key in commandlist: 374 print format % (key, commands[key][0])
375 376 parser = optparse.OptionParser(add_help_option=False) 377 parser.allow_interspersed_args = False 378 parser.add_option('-c', '--config', dest='config') 379 parser.add_option('-e', '--egg', dest='egg') 380 parser.add_option('-h', '--help', dest='help', action='store_true') 381 if len(sys.argv) > 1 and sys.argv[1] == 'help': 382 sys.argv[1] = '--help' 383 options, args = parser.parse_args(sys.argv[1:]) 384 385 # if command is not found display help 386 if not args or args[0] not in commands: 387 _help() 388 sys.exit() 389 390 commandname = args[0] 391 # strip command and any global options from the sys.argv 392 del sys.argv[1:] 393 if options.help: 394 sys.argv.append('-h') 395 sys.argv.extend(args[1:]) 396 command = commands[commandname][1] 397 command = command.load() 398 399 if options.egg: 400 egg = pkg_resources.get_distribution(options.egg) 401 os.chdir(egg.location) 402 403 if hasattr(command, 'need_project'): 404 if not get_project_name(): 405 print "This command needs to be run from inside a project directory" 406 return 407 elif not options.config and not os.path.isfile(get_project_config()): 408 print """No default config file was found. 409 If it has been renamed use: 410 tg-admin --config=<FILE> %s""" % commandname 411 return 412 413 command.config = options.config 414 command = command(__version__) 415 command.run() 416