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
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
42 """Base class for commands that need to use the database"""
43
44 config = None
45
48
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
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
85 if args:
86 del sys.argv[1]
87 else:
88 del sys.argv[1:3]
89
90 self.find_config()
91
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
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
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
208
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:
218 try:
219 from IPython.terminal.interactiveshell \
220 import TerminalInteractiveShell as InteractiveShell
221 except ImportError:
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
341
342
343 commands = None
344
346 """Main command runner. Manages the primary command line arguments."""
347
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
386 if not args or args[0] not in commands:
387 _help()
388 sys.exit()
389
390 commandname = args[0]
391
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