Mercurial > p > roundup > code
diff roundup/admin.py @ 5250:ac7fe3483206
Make admin.py 2/3-agnostic.
| author | Eric S. Raymond <esr@thyrsus.com> |
|---|---|
| date | Sat, 26 Aug 2017 00:24:01 -0400 |
| parents | 198b6e810c67 |
| children | 4cf48ff01e04 bed579f654ee |
line wrap: on
line diff
--- a/roundup/admin.py Fri Aug 25 11:07:21 2017 -0400 +++ b/roundup/admin.py Sat Aug 26 00:24:01 2017 -0400 @@ -19,6 +19,8 @@ """Administration commands for maintaining Roundup trackers. """ +from __future__ import print_function + __docformat__ = 'restructuredtext' import csv, getopt, getpass, os, re, shutil, sys, UserDict, operator @@ -30,6 +32,12 @@ from roundup.i18n import _ from roundup.exceptions import UsageError +# Polyglot code +try: + my_input = raw_input +except NameError: + my_input = input + class CommandDict(UserDict.UserDict): """Simple dictionary that lets us do lookups using partial keys. @@ -137,7 +145,7 @@ """ sys.stdout.write( _('Commands: ')) commands = [''] - for command in self.commands.itervalues(): + for command in self.commands.values(): h = _(command.__doc__).split('\n')[0] commands.append(' '+h[7:]) commands.sort() @@ -149,27 +157,27 @@ def help_commands_html(self, indent_re=re.compile(r'^(\s+)\S+')): """ Produce an HTML command list. """ - commands = sorted(self.commands.itervalues(), + commands = sorted(iter(self.commands.values()), operator.attrgetter('__name__')) for command in commands: h = _(command.__doc__).split('\n') name = command.__name__[3:] usage = h[0] - print """ + print(""" <tr><td valign=top><strong>%(name)s</strong></td> <td><tt>%(usage)s</tt><p> -<pre>""" % locals() +<pre>""" % locals()) indent = indent_re.match(h[3]) if indent: indent = len(indent.group(1)) for line in h[3:]: if indent: - print line[indent:] + print(line[indent:]) else: - print line - print '</pre></td></tr>\n' + print(line) + print('</pre></td></tr>\n') def help_all(self): - print _(""" + print(_(""" All commands (except help) require a tracker specifier. This is just the path to the roundup tracker you're working with. A roundup tracker is where roundup keeps the database and configuration file that defines @@ -230,10 +238,10 @@ "." means "right now" Command help: -""") - for name, command in self.commands.items(): - print _('%s:')%name - print ' ', _(command.__doc__) +""")) + for name, command in list(self.commands.items()): + print(_('%s:')%name) + print(' ', _(command.__doc__)) def do_help(self, args, nl_re=re.compile('[\r\n]'), indent_re=re.compile(r'^(\s+)\S+')): @@ -260,20 +268,20 @@ try: l = self.commands.get(topic) except KeyError: - print _('Sorry, no help for "%(topic)s"')%locals() + print(_('Sorry, no help for "%(topic)s"')%locals()) return 1 # display the help for each match, removing the docsring indent for name, help in l: lines = nl_re.split(_(help.__doc__)) - print lines[0] + print(lines[0]) indent = indent_re.match(lines[1]) if indent: indent = len(indent.group(1)) for line in lines[1:]: if indent: - print line[indent:] + print(line[indent:]) else: - print line + print(line) return 0 def listTemplates(self): @@ -337,10 +345,10 @@ def help_initopts(self): templates = self.listTemplates() - print _('Templates:'), ', '.join(templates) + print(_('Templates:'), ', '.join(templates)) import roundup.backends backends = roundup.backends.list_backends() - print _('Back ends:'), ', '.join(backends) + print(_('Back ends:'), ', '.join(backends)) def do_install(self, tracker_home, args): ''"""Usage: install [template [backend [key=val[,key=val]]]] @@ -380,7 +388,7 @@ if list(filter(os.path.exists, [config_ini_file, os.path.join(tracker_home, 'config.py')])): if not self.force: - ok = raw_input(_( + ok = my_input(_( """WARNING: There appears to be a tracker in "%(tracker_home)s"! If you re-install it, you will lose all the data! Erase it? Y/N: """) % locals()) @@ -415,7 +423,7 @@ try: defns = dict([item.split("=") for item in args[3].split(",")]) except: - print _('Error in configuration settings: "%s"') % args[3] + print(_('Error in configuration settings: "%s"') % args[3]) raise else: defns = {} @@ -424,22 +432,22 @@ # install! init.install(tracker_home, templates[template]['path'], settings=defns) - print _(""" + print(_(""" --------------------------------------------------------------------------- You should now edit the tracker configuration file: - %(config_file)s""") % {"config_file": config_ini_file} + %(config_file)s""") % {"config_file": config_ini_file}) # find list of options that need manual adjustments # XXX config._get_unset_options() is marked as private # (leading underscore). make it public or don't care? need_set = CoreConfig(tracker_home)._get_unset_options() if need_set: - print _(" ... at a minimum, you must set following options:") + print(_(" ... at a minimum, you must set following options:")) for section in need_set: - print " [%s]: %s" % (section, ", ".join(need_set[section])) + print(" [%s]: %s" % (section, ", ".join(need_set[section]))) # note about schema modifications - print _(""" + print(_(""" If you wish to modify the database schema, you should also edit the schema file: %(database_config_file)s @@ -453,7 +461,7 @@ """) % { 'database_config_file': os.path.join(tracker_home, 'schema.py'), 'database_init_file': os.path.join(tracker_home, 'initial_data.py'), -} +}) return 0 def _get_choice(self, list_name, prompt, options, argument, default=None): @@ -465,7 +473,7 @@ return default sys.stdout.write('%s: %s\n' % (list_name, ', '.join(options))) while argument not in options: - argument = raw_input('%s [%s]: ' % (prompt, default)) + argument = my_input('%s [%s]: ' % (prompt, default)) if not argument: return default return argument @@ -522,7 +530,7 @@ # is there already a database? if tracker.exists(): if not self.force: - ok = raw_input(_( + ok = my_input(_( """WARNING: The database is already initialised! If you re-initialise it, you will lose all the data! Erase it? Y/N: """)) @@ -606,9 +614,9 @@ propclassname = self.db.getclass(property.classname).classname id = cl.get(nodeid, propname) for i in id: - print propclassname + i + print(propclassname + i) else: - print cl.get(nodeid, propname) + print(cl.get(nodeid, propname)) except IndexError: raise UsageError(_('no such %(classname)s node ' '"%(nodeid)s"')%locals()) @@ -616,7 +624,7 @@ raise UsageError(_('no such %(classname)s property ' '"%(propname)s"')%locals()) if self.separator: - print self.separator.join(l) + print(self.separator.join(l)) return 0 @@ -665,7 +673,7 @@ props = copy.copy(propset) # make a new copy for every designator cl = self.get_class(classname) - for key, value in props.items(): + for key, value in list(props.items()): try: # You must reinitialize the props every time though. # if props['nosy'] = '+admin' initally, it gets @@ -705,7 +713,7 @@ props = self.props_from_args(args[1:]) # convert the user-input value to a value used for find() - for propname, value in props.iteritems(): + for propname, value in props.items(): if ',' in value: values = value.split(',') else: @@ -728,18 +736,18 @@ id = cl.find(**props) for i in id: designator.append(classname + i) - print self.separator.join(designator) + print(self.separator.join(designator)) else: - print self.separator.join(cl.find(**props)) + print(self.separator.join(cl.find(**props))) else: if self.print_designator: id = cl.find(**props) for i in id: designator.append(classname + i) - print designator + print(designator) else: - print cl.find(**props) + print(cl.find(**props)) except KeyError: raise UsageError(_('%(classname)s has no property ' '"%(propname)s"')%locals()) @@ -795,7 +803,7 @@ keys = sorted(cl.properties) for key in keys: value = cl.get(nodeid, key) - print _('%(key)s: %(value)s')%locals() + print(_('%(key)s: %(value)s')%locals()) def do_create(self, args): ''"""Usage: create classname property=value ... @@ -830,11 +838,11 @@ 'propname': key.capitalize()}) again = getpass.getpass(_(' %(propname)s (Again): ')%{ 'propname': key.capitalize()}) - if value != again: print _('Sorry, try again...') + if value != again: print(_('Sorry, try again...')) if value: props[key] = value else: - value = raw_input(_('%(propname)s (%(proptype)s): ')%{ + value = my_input(_('%(propname)s (%(proptype)s): ')%{ 'propname': key.capitalize(), 'proptype': name}) if value: props[key] = value @@ -901,11 +909,11 @@ except KeyError: raise UsageError(_('%(classname)s has no property ' '"%(propname)s"')%locals()) - print self.separator.join(proplist) + print(self.separator.join(proplist)) else: # create a list of index id's since user didn't specify # otherwise - print self.separator.join(cl.list()) + print(self.separator.join(cl.list())) else: for nodeid in cl.list(): try: @@ -913,7 +921,7 @@ except KeyError: raise UsageError(_('%(classname)s has no property ' '"%(propname)s"')%locals()) - print _('%(nodeid)4s: %(value)s')%locals() + print(_('%(nodeid)4s: %(value)s')%locals()) return 0 def do_table(self, args): @@ -990,7 +998,7 @@ props.append((spec, maxlen)) # now display the heading - print ' '.join([name.capitalize().ljust(width) for name,width in props]) + print(' '.join([name.capitalize().ljust(width) for name,width in props])) # and the table data for nodeid in cl.list(): @@ -1008,7 +1016,7 @@ value = str(nodeid) f = '%%-%ds'%width l.append(f%value[:width]) - print ' '.join(l) + print(' '.join(l)) return 0 def do_history(self, args): @@ -1038,8 +1046,8 @@ skipquiet = True try: - print self.db.getclass(classname).history(nodeid, - skipquiet=skipquiet) + print(self.db.getclass(classname).history(nodeid, + skipquiet=skipquiet)) except KeyError: raise UsageError(_('no such class "%(classname)s"')%locals()) except IndexError: @@ -1219,8 +1227,7 @@ journals.writerow(row) jf.close() if max_len > self.db.config.CSV_FIELD_SIZE: - print >> sys.stderr, \ - "Warning: config csv_field_size should be at least %s"%max_len + print("Warning: config csv_field_size should be at least %s"%max_len, file=sys.stderr) return 0 def do_exporttables(self, args): @@ -1302,7 +1309,7 @@ maxid = max(maxid, int(nodeid)) # (print to sys.stdout here to allow tests to squash it .. ugh) - print >> sys.stdout + print(file=sys.stdout) f.close() @@ -1313,7 +1320,7 @@ f.close() # (print to sys.stdout here to allow tests to squash it .. ugh) - print >> sys.stdout, 'setting', classname, maxid+1 + print('setting', classname, maxid+1, file=sys.stdout) # set the id counter self.db.setid(classname, str(maxid+1)) @@ -1444,10 +1451,10 @@ the habit. """ if getattr(self.db, 'db_version_updated'): - print _('Tracker updated') + print(_('Tracker updated')) self.db_uncommitted = True else: - print _('No migration action required') + print(_('No migration action required')) return 0 def run_command(self, args): @@ -1473,21 +1480,21 @@ functions = self.commands.get(command) except KeyError: # not a valid command - print _('Unknown command "%(command)s" ("help commands" for a ' - 'list)')%locals() + print(_('Unknown command "%(command)s" ("help commands" for a ' + 'list)')%locals()) return 1 # check for multiple matches if len(functions) > 1: - print _('Multiple commands match "%(command)s": %(list)s')%{'command': - command, 'list': ', '.join([i[0] for i in functions])} + print(_('Multiple commands match "%(command)s": %(list)s')%{'command': + command, 'list': ', '.join([i[0] for i in functions])}) return 1 command, function = functions[0] # make sure we have a tracker_home while not self.tracker_home: if not self.force: - self.tracker_home = raw_input(_('Enter tracker home: ')).strip() + self.tracker_home = my_input(_('Enter tracker home: ')).strip() else: self.tracker_home = os.curdir @@ -1496,13 +1503,13 @@ try: return self.do_initialise(self.tracker_home, args) except UsageError as message: - print _('Error: %(message)s')%locals() + print(_('Error: %(message)s')%locals()) return 1 elif command == 'install': try: return self.do_install(self.tracker_home, args) except UsageError as message: - print _('Error: %(message)s')%locals() + print(_('Error: %(message)s')%locals()) return 1 # get the tracker @@ -1510,11 +1517,11 @@ tracker = roundup.instance.open(self.tracker_home) except ValueError as message: self.tracker_home = '' - print _("Error: Couldn't open tracker: %(message)s")%locals() + print(_("Error: Couldn't open tracker: %(message)s")%locals()) return 1 except NoConfigError as message: self.tracker_home = '' - print _("Error: Couldn't open tracker: %(message)s")%locals() + print(_("Error: Couldn't open tracker: %(message)s")%locals()) return 1 # only open the database once! @@ -1528,9 +1535,9 @@ try: ret = function(args[1:]) except UsageError as message: - print _('Error: %(message)s')%locals() - print - print function.__doc__ + print(_('Error: %(message)s')%locals()) + print() + print(function.__doc__) ret = 1 except: import traceback @@ -1541,18 +1548,18 @@ def interactive(self): """Run in an interactive mode """ - print _('Roundup %s ready for input.\nType "help" for help.' - % roundup_version) + print(_('Roundup %s ready for input.\nType "help" for help.' + % roundup_version)) try: import readline except ImportError: - print _('Note: command history and editing not available') + print(_('Note: command history and editing not available')) while 1: try: - command = raw_input(_('roundup> ')) + command = my_input(_('roundup> ')) except EOFError: - print _('exit...') + print(_('exit...')) break if not command: continue try: @@ -1565,7 +1572,7 @@ # exit.. check for transactions if self.db and self.db_uncommitted: - commit = raw_input(_('There are unsaved changes. Commit them (y/N)? ')) + commit = my_input(_('There are unsaved changes. Commit them (y/N)? ')) if commit and commit[0].lower() == 'y': self.db.commit() return 0 @@ -1594,7 +1601,7 @@ self.usage() return 0 elif opt == '-v': - print '%s (python %s)'%(roundup_version, sys.version.split()[0]) + print('%s (python %s)'%(roundup_version, sys.version.split()[0])) return 0 elif opt == '-V': self.verbose = 1
