Statistics
| Branch: | Tag: | Revision:

root / lib / cli.py @ d5835922

History | View | Annotate | Download (11.8 kB)

1 a8083063 Iustin Pop
#!/usr/bin/python
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 a8083063 Iustin Pop
# Copyright (C) 2006, 2007 Google Inc.
5 a8083063 Iustin Pop
#
6 a8083063 Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 a8083063 Iustin Pop
# it under the terms of the GNU General Public License as published by
8 a8083063 Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 a8083063 Iustin Pop
# (at your option) any later version.
10 a8083063 Iustin Pop
#
11 a8083063 Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 a8083063 Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 a8083063 Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 a8083063 Iustin Pop
# General Public License for more details.
15 a8083063 Iustin Pop
#
16 a8083063 Iustin Pop
# You should have received a copy of the GNU General Public License
17 a8083063 Iustin Pop
# along with this program; if not, write to the Free Software
18 a8083063 Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 a8083063 Iustin Pop
# 02110-1301, USA.
20 a8083063 Iustin Pop
21 a8083063 Iustin Pop
22 a8083063 Iustin Pop
"""Module dealing with command line parsing"""
23 a8083063 Iustin Pop
24 a8083063 Iustin Pop
25 a8083063 Iustin Pop
import sys
26 a8083063 Iustin Pop
import textwrap
27 a8083063 Iustin Pop
import os.path
28 a8083063 Iustin Pop
import copy
29 a8083063 Iustin Pop
30 a8083063 Iustin Pop
from ganeti import utils
31 a8083063 Iustin Pop
from ganeti import logger
32 a8083063 Iustin Pop
from ganeti import errors
33 a8083063 Iustin Pop
from ganeti import mcpu
34 a8083063 Iustin Pop
from ganeti import constants
35 a8083063 Iustin Pop
36 a8083063 Iustin Pop
from optparse import (OptionParser, make_option, TitledHelpFormatter,
37 a8083063 Iustin Pop
                      Option, OptionValueError, SUPPRESS_HELP)
38 a8083063 Iustin Pop
39 a8083063 Iustin Pop
__all__ = ["DEBUG_OPT", "NOHDR_OPT", "SEP_OPT", "GenericMain", "SubmitOpCode",
40 47988778 Iustin Pop
           "cli_option", "GenerateTable", "AskUser",
41 a8083063 Iustin Pop
           "ARGS_NONE", "ARGS_FIXED", "ARGS_ATLEAST", "ARGS_ANY", "ARGS_ONE",
42 fe7b0351 Michael Hanselmann
           "USEUNITS_OPT", "FIELDS_OPT", "FORCE_OPT"]
43 a8083063 Iustin Pop
44 a8083063 Iustin Pop
DEBUG_OPT = make_option("-d", "--debug", default=False,
45 a8083063 Iustin Pop
                        action="store_true",
46 a8083063 Iustin Pop
                        help="Turn debugging on")
47 a8083063 Iustin Pop
48 a8083063 Iustin Pop
NOHDR_OPT = make_option("--no-headers", default=False,
49 a8083063 Iustin Pop
                        action="store_true", dest="no_headers",
50 a8083063 Iustin Pop
                        help="Don't display column headers")
51 a8083063 Iustin Pop
52 137161c9 Michael Hanselmann
SEP_OPT = make_option("--separator", default=None,
53 a8083063 Iustin Pop
                      action="store", dest="separator",
54 a8083063 Iustin Pop
                      help="Separator between output fields"
55 a8083063 Iustin Pop
                      " (defaults to one space)")
56 a8083063 Iustin Pop
57 a8083063 Iustin Pop
USEUNITS_OPT = make_option("--human-readable", default=False,
58 a8083063 Iustin Pop
                           action="store_true", dest="human_readable",
59 a8083063 Iustin Pop
                           help="Print sizes in human readable format")
60 a8083063 Iustin Pop
61 dcb93971 Michael Hanselmann
FIELDS_OPT = make_option("-o", "--output", dest="output", action="store",
62 dcb93971 Michael Hanselmann
                         type="string", help="Select output fields",
63 dcb93971 Michael Hanselmann
                         metavar="FIELDS")
64 dcb93971 Michael Hanselmann
65 fe7b0351 Michael Hanselmann
FORCE_OPT = make_option("-f", "--force", dest="force", action="store_true",
66 fe7b0351 Michael Hanselmann
                        default=False, help="Force the operation")
67 fe7b0351 Michael Hanselmann
68 a8083063 Iustin Pop
_LOCK_OPT = make_option("--lock-retries", default=None,
69 a8083063 Iustin Pop
                        type="int", help=SUPPRESS_HELP)
70 a8083063 Iustin Pop
71 a8083063 Iustin Pop
72 a8083063 Iustin Pop
def ARGS_FIXED(val):
73 a8083063 Iustin Pop
  """Macro-like function denoting a fixed number of arguments"""
74 a8083063 Iustin Pop
  return -val
75 a8083063 Iustin Pop
76 a8083063 Iustin Pop
77 a8083063 Iustin Pop
def ARGS_ATLEAST(val):
78 a8083063 Iustin Pop
  """Macro-like function denoting a minimum number of arguments"""
79 a8083063 Iustin Pop
  return val
80 a8083063 Iustin Pop
81 a8083063 Iustin Pop
82 a8083063 Iustin Pop
ARGS_NONE = None
83 a8083063 Iustin Pop
ARGS_ONE = ARGS_FIXED(1)
84 a8083063 Iustin Pop
ARGS_ANY = ARGS_ATLEAST(0)
85 a8083063 Iustin Pop
86 a8083063 Iustin Pop
87 a8083063 Iustin Pop
def check_unit(option, opt, value):
88 a8083063 Iustin Pop
  try:
89 a8083063 Iustin Pop
    return utils.ParseUnit(value)
90 a8083063 Iustin Pop
  except errors.UnitParseError, err:
91 3ecf6786 Iustin Pop
    raise OptionValueError("option %s: %s" % (opt, err))
92 a8083063 Iustin Pop
93 a8083063 Iustin Pop
94 a8083063 Iustin Pop
class CliOption(Option):
95 a8083063 Iustin Pop
  TYPES = Option.TYPES + ("unit",)
96 a8083063 Iustin Pop
  TYPE_CHECKER = copy.copy(Option.TYPE_CHECKER)
97 a8083063 Iustin Pop
  TYPE_CHECKER["unit"] = check_unit
98 a8083063 Iustin Pop
99 a8083063 Iustin Pop
100 a8083063 Iustin Pop
# optparse.py sets make_option, so we do it for our own option class, too
101 a8083063 Iustin Pop
cli_option = CliOption
102 a8083063 Iustin Pop
103 a8083063 Iustin Pop
104 a8083063 Iustin Pop
def _ParseArgs(argv, commands):
105 a8083063 Iustin Pop
  """Parses the command line and return the function which must be
106 a8083063 Iustin Pop
  executed together with its arguments
107 a8083063 Iustin Pop

108 a8083063 Iustin Pop
  Arguments:
109 a8083063 Iustin Pop
    argv: the command line
110 a8083063 Iustin Pop

111 a8083063 Iustin Pop
    commands: dictionary with special contents, see the design doc for
112 a8083063 Iustin Pop
    cmdline handling
113 098c0958 Michael Hanselmann

114 a8083063 Iustin Pop
  """
115 a8083063 Iustin Pop
  if len(argv) == 0:
116 a8083063 Iustin Pop
    binary = "<command>"
117 a8083063 Iustin Pop
  else:
118 a8083063 Iustin Pop
    binary = argv[0].split("/")[-1]
119 a8083063 Iustin Pop
120 a8083063 Iustin Pop
  if len(argv) > 1 and argv[1] == "--version":
121 a8083063 Iustin Pop
    print "%s (ganeti) %s" % (binary, constants.RELEASE_VERSION)
122 a8083063 Iustin Pop
    # Quit right away. That way we don't have to care about this special
123 a8083063 Iustin Pop
    # argument. optparse.py does it the same.
124 a8083063 Iustin Pop
    sys.exit(0)
125 a8083063 Iustin Pop
126 a8083063 Iustin Pop
  if len(argv) < 2 or argv[1] not in commands.keys():
127 a8083063 Iustin Pop
    # let's do a nice thing
128 a8083063 Iustin Pop
    sortedcmds = commands.keys()
129 a8083063 Iustin Pop
    sortedcmds.sort()
130 a8083063 Iustin Pop
    print ("Usage: %(bin)s {command} [options...] [argument...]"
131 a8083063 Iustin Pop
           "\n%(bin)s <command> --help to see details, or"
132 a8083063 Iustin Pop
           " man %(bin)s\n" % {"bin": binary})
133 a8083063 Iustin Pop
    # compute the max line length for cmd + usage
134 a8083063 Iustin Pop
    mlen = max([len(" %s %s" % (cmd, commands[cmd][3])) for cmd in commands])
135 a8083063 Iustin Pop
    mlen = min(60, mlen) # should not get here...
136 a8083063 Iustin Pop
    # and format a nice command list
137 a8083063 Iustin Pop
    print "Commands:"
138 a8083063 Iustin Pop
    for cmd in sortedcmds:
139 a8083063 Iustin Pop
      cmdstr = " %s %s" % (cmd, commands[cmd][3])
140 a8083063 Iustin Pop
      help_text = commands[cmd][4]
141 a8083063 Iustin Pop
      help_lines = textwrap.wrap(help_text, 79-3-mlen)
142 a8083063 Iustin Pop
      print "%-*s - %s" % (mlen, cmdstr,
143 a8083063 Iustin Pop
                                          help_lines.pop(0))
144 a8083063 Iustin Pop
      for line in help_lines:
145 a8083063 Iustin Pop
        print "%-*s   %s" % (mlen, "", line)
146 a8083063 Iustin Pop
    print
147 a8083063 Iustin Pop
    return None, None, None
148 a8083063 Iustin Pop
  cmd = argv.pop(1)
149 a8083063 Iustin Pop
  func, nargs, parser_opts, usage, description = commands[cmd]
150 a8083063 Iustin Pop
  parser_opts.append(_LOCK_OPT)
151 a8083063 Iustin Pop
  parser = OptionParser(option_list=parser_opts,
152 a8083063 Iustin Pop
                        description=description,
153 a8083063 Iustin Pop
                        formatter=TitledHelpFormatter(),
154 a8083063 Iustin Pop
                        usage="%%prog %s %s" % (cmd, usage))
155 a8083063 Iustin Pop
  parser.disable_interspersed_args()
156 a8083063 Iustin Pop
  options, args = parser.parse_args()
157 a8083063 Iustin Pop
  if nargs is None:
158 a8083063 Iustin Pop
    if len(args) != 0:
159 a8083063 Iustin Pop
      print >> sys.stderr, ("Error: Command %s expects no arguments" % cmd)
160 a8083063 Iustin Pop
      return None, None, None
161 a8083063 Iustin Pop
  elif nargs < 0 and len(args) != -nargs:
162 a8083063 Iustin Pop
    print >> sys.stderr, ("Error: Command %s expects %d argument(s)" %
163 a8083063 Iustin Pop
                         (cmd, -nargs))
164 a8083063 Iustin Pop
    return None, None, None
165 a8083063 Iustin Pop
  elif nargs >= 0 and len(args) < nargs:
166 a8083063 Iustin Pop
    print >> sys.stderr, ("Error: Command %s expects at least %d argument(s)" %
167 a8083063 Iustin Pop
                         (cmd, nargs))
168 a8083063 Iustin Pop
    return None, None, None
169 a8083063 Iustin Pop
170 a8083063 Iustin Pop
  return func, options, args
171 a8083063 Iustin Pop
172 a8083063 Iustin Pop
173 47988778 Iustin Pop
def AskUser(text, choices=None):
174 47988778 Iustin Pop
  """Ask the user a question.
175 a8083063 Iustin Pop

176 a8083063 Iustin Pop
  Args:
177 47988778 Iustin Pop
    text - the question to ask.
178 a8083063 Iustin Pop

179 47988778 Iustin Pop
    choices - list with elements tuples (input_char, return_value,
180 47988778 Iustin Pop
    description); if not given, it will default to: [('y', True,
181 47988778 Iustin Pop
    'Perform the operation'), ('n', False, 'Do no do the operation')];
182 47988778 Iustin Pop
    note that the '?' char is reserved for help
183 47988778 Iustin Pop

184 47988778 Iustin Pop
  Returns: one of the return values from the choices list; if input is
185 47988778 Iustin Pop
  not possible (i.e. not running with a tty, we return the last entry
186 47988778 Iustin Pop
  from the list
187 a8083063 Iustin Pop

188 a8083063 Iustin Pop
  """
189 47988778 Iustin Pop
  if choices is None:
190 47988778 Iustin Pop
    choices = [('y', True, 'Perform the operation'),
191 47988778 Iustin Pop
               ('n', False, 'Do not perform the operation')]
192 47988778 Iustin Pop
  if not choices or not isinstance(choices, list):
193 47988778 Iustin Pop
    raise errors.ProgrammerError("Invalid choiches argument to AskUser")
194 47988778 Iustin Pop
  for entry in choices:
195 47988778 Iustin Pop
    if not isinstance(entry, tuple) or len(entry) < 3 or entry[0] == '?':
196 47988778 Iustin Pop
      raise errors.ProgrammerError("Invalid choiches element to AskUser")
197 47988778 Iustin Pop
198 47988778 Iustin Pop
  answer = choices[-1][1]
199 47988778 Iustin Pop
  new_text = []
200 47988778 Iustin Pop
  for line in text.splitlines():
201 47988778 Iustin Pop
    new_text.append(textwrap.fill(line, 70, replace_whitespace=False))
202 47988778 Iustin Pop
  text = "\n".join(new_text)
203 a8083063 Iustin Pop
  try:
204 a8083063 Iustin Pop
    f = file("/dev/tty", "r+")
205 a8083063 Iustin Pop
  except IOError:
206 47988778 Iustin Pop
    return answer
207 a8083063 Iustin Pop
  try:
208 47988778 Iustin Pop
    chars = [entry[0] for entry in choices]
209 47988778 Iustin Pop
    chars[-1] = "[%s]" % chars[-1]
210 47988778 Iustin Pop
    chars.append('?')
211 47988778 Iustin Pop
    maps = dict([(entry[0], entry[1]) for entry in choices])
212 47988778 Iustin Pop
    while True:
213 47988778 Iustin Pop
      f.write(text)
214 47988778 Iustin Pop
      f.write('\n')
215 47988778 Iustin Pop
      f.write("/".join(chars))
216 47988778 Iustin Pop
      f.write(": ")
217 47988778 Iustin Pop
      line = f.readline(2).strip().lower()
218 47988778 Iustin Pop
      if line in maps:
219 47988778 Iustin Pop
        answer = maps[line]
220 47988778 Iustin Pop
        break
221 47988778 Iustin Pop
      elif line == '?':
222 47988778 Iustin Pop
        for entry in choices:
223 47988778 Iustin Pop
          f.write(" %s - %s\n" % (entry[0], entry[2]))
224 47988778 Iustin Pop
        f.write("\n")
225 47988778 Iustin Pop
        continue
226 a8083063 Iustin Pop
  finally:
227 a8083063 Iustin Pop
    f.close()
228 a8083063 Iustin Pop
  return answer
229 a8083063 Iustin Pop
230 a8083063 Iustin Pop
231 a8083063 Iustin Pop
def SubmitOpCode(op):
232 a8083063 Iustin Pop
  """Function to submit an opcode.
233 a8083063 Iustin Pop

234 a8083063 Iustin Pop
  This is just a simple wrapper over the construction of the processor
235 a8083063 Iustin Pop
  instance. It should be extended to better handle feedback and
236 a8083063 Iustin Pop
  interaction functions.
237 a8083063 Iustin Pop

238 a8083063 Iustin Pop
  """
239 a8083063 Iustin Pop
  proc = mcpu.Processor()
240 a8083063 Iustin Pop
  return proc.ExecOpCode(op, logger.ToStdout)
241 a8083063 Iustin Pop
242 a8083063 Iustin Pop
243 a8083063 Iustin Pop
def GenericMain(commands):
244 a8083063 Iustin Pop
  """Generic main function for all the gnt-* commands.
245 a8083063 Iustin Pop

246 a8083063 Iustin Pop
  Argument: a dictionary with a special structure, see the design doc
247 a8083063 Iustin Pop
  for command line handling.
248 a8083063 Iustin Pop

249 a8083063 Iustin Pop
  """
250 a8083063 Iustin Pop
  # save the program name and the entire command line for later logging
251 a8083063 Iustin Pop
  if sys.argv:
252 a8083063 Iustin Pop
    binary = os.path.basename(sys.argv[0]) or sys.argv[0]
253 a8083063 Iustin Pop
    if len(sys.argv) >= 2:
254 a8083063 Iustin Pop
      binary += " " + sys.argv[1]
255 a8083063 Iustin Pop
      old_cmdline = " ".join(sys.argv[2:])
256 a8083063 Iustin Pop
    else:
257 a8083063 Iustin Pop
      old_cmdline = ""
258 a8083063 Iustin Pop
  else:
259 a8083063 Iustin Pop
    binary = "<unknown program>"
260 a8083063 Iustin Pop
    old_cmdline = ""
261 a8083063 Iustin Pop
262 a8083063 Iustin Pop
  func, options, args = _ParseArgs(sys.argv, commands)
263 a8083063 Iustin Pop
  if func is None: # parse error
264 a8083063 Iustin Pop
    return 1
265 a8083063 Iustin Pop
266 a8083063 Iustin Pop
  logger.SetupLogging(debug=options.debug, program=binary)
267 a8083063 Iustin Pop
268 a8083063 Iustin Pop
  try:
269 a8083063 Iustin Pop
    utils.Lock('cmd', max_retries=options.lock_retries, debug=options.debug)
270 a8083063 Iustin Pop
  except errors.LockError, err:
271 a8083063 Iustin Pop
    logger.ToStderr(str(err))
272 a8083063 Iustin Pop
    return 1
273 a8083063 Iustin Pop
274 a8083063 Iustin Pop
  if old_cmdline:
275 a8083063 Iustin Pop
    logger.Info("run with arguments '%s'" % old_cmdline)
276 a8083063 Iustin Pop
  else:
277 a8083063 Iustin Pop
    logger.Info("run with no arguments")
278 a8083063 Iustin Pop
279 a8083063 Iustin Pop
  try:
280 a8083063 Iustin Pop
    try:
281 a8083063 Iustin Pop
      result = func(options, args)
282 a8083063 Iustin Pop
    except errors.ConfigurationError, err:
283 a8083063 Iustin Pop
      logger.Error("Corrupt configuration file: %s" % err)
284 a8083063 Iustin Pop
      logger.ToStderr("Aborting.")
285 a8083063 Iustin Pop
      result = 2
286 a8083063 Iustin Pop
    except errors.HooksAbort, err:
287 a8083063 Iustin Pop
      logger.ToStderr("Failure: hooks execution failed:")
288 a8083063 Iustin Pop
      for node, script, out in err.args[0]:
289 a8083063 Iustin Pop
        if out:
290 a8083063 Iustin Pop
          logger.ToStderr("  node: %s, script: %s, output: %s" %
291 a8083063 Iustin Pop
                          (node, script, out))
292 a8083063 Iustin Pop
        else:
293 a8083063 Iustin Pop
          logger.ToStderr("  node: %s, script: %s (no output)" %
294 a8083063 Iustin Pop
                          (node, script))
295 a8083063 Iustin Pop
      result = 1
296 a8083063 Iustin Pop
    except errors.HooksFailure, err:
297 a8083063 Iustin Pop
      logger.ToStderr("Failure: hooks general failure: %s" % str(err))
298 a8083063 Iustin Pop
      result = 1
299 89e1fc26 Iustin Pop
    except errors.ResolverError, err:
300 89e1fc26 Iustin Pop
      this_host = utils.HostInfo.SysName()
301 89e1fc26 Iustin Pop
      if err.args[0] == this_host:
302 89e1fc26 Iustin Pop
        msg = "Failure: can't resolve my own hostname ('%s')"
303 89e1fc26 Iustin Pop
      else:
304 89e1fc26 Iustin Pop
        msg = "Failure: can't resolve hostname '%s'"
305 89e1fc26 Iustin Pop
      logger.ToStderr(msg % err.args[0])
306 89e1fc26 Iustin Pop
      result = 1
307 a8083063 Iustin Pop
    except errors.OpPrereqError, err:
308 a8083063 Iustin Pop
      logger.ToStderr("Failure: prerequisites not met for this"
309 a8083063 Iustin Pop
                      " operation:\n%s" % str(err))
310 a8083063 Iustin Pop
      result = 1
311 a8083063 Iustin Pop
    except errors.OpExecError, err:
312 a8083063 Iustin Pop
      logger.ToStderr("Failure: command execution error:\n%s" % str(err))
313 a8083063 Iustin Pop
      result = 1
314 a8083063 Iustin Pop
  finally:
315 a8083063 Iustin Pop
    utils.Unlock('cmd')
316 a8083063 Iustin Pop
    utils.LockCleanup()
317 a8083063 Iustin Pop
318 a8083063 Iustin Pop
  return result
319 137161c9 Michael Hanselmann
320 137161c9 Michael Hanselmann
321 16be8703 Iustin Pop
def GenerateTable(headers, fields, separator, data,
322 16be8703 Iustin Pop
                  numfields=None, unitfields=None):
323 137161c9 Michael Hanselmann
  """Prints a table with headers and different fields.
324 137161c9 Michael Hanselmann

325 137161c9 Michael Hanselmann
  Args:
326 137161c9 Michael Hanselmann
    headers: Dict of header titles or None if no headers should be shown
327 137161c9 Michael Hanselmann
    fields: List of fields to show
328 137161c9 Michael Hanselmann
    separator: String used to separate fields or None for spaces
329 137161c9 Michael Hanselmann
    data: Data to be printed
330 137161c9 Michael Hanselmann
    numfields: List of fields to be aligned to right
331 137161c9 Michael Hanselmann
    unitfields: List of fields to be formatted as units
332 137161c9 Michael Hanselmann

333 137161c9 Michael Hanselmann
  """
334 137161c9 Michael Hanselmann
  if numfields is None:
335 137161c9 Michael Hanselmann
    numfields = []
336 137161c9 Michael Hanselmann
  if unitfields is None:
337 137161c9 Michael Hanselmann
    unitfields = []
338 137161c9 Michael Hanselmann
339 137161c9 Michael Hanselmann
  format_fields = []
340 137161c9 Michael Hanselmann
  for field in fields:
341 137161c9 Michael Hanselmann
    if separator is not None:
342 137161c9 Michael Hanselmann
      format_fields.append("%s")
343 137161c9 Michael Hanselmann
    elif field in numfields:
344 137161c9 Michael Hanselmann
      format_fields.append("%*s")
345 137161c9 Michael Hanselmann
    else:
346 137161c9 Michael Hanselmann
      format_fields.append("%-*s")
347 137161c9 Michael Hanselmann
348 137161c9 Michael Hanselmann
  if separator is None:
349 137161c9 Michael Hanselmann
    mlens = [0 for name in fields]
350 137161c9 Michael Hanselmann
    format = ' '.join(format_fields)
351 137161c9 Michael Hanselmann
  else:
352 137161c9 Michael Hanselmann
    format = separator.replace("%", "%%").join(format_fields)
353 137161c9 Michael Hanselmann
354 137161c9 Michael Hanselmann
  for row in data:
355 137161c9 Michael Hanselmann
    for idx, val in enumerate(row):
356 137161c9 Michael Hanselmann
      if fields[idx] in unitfields:
357 137161c9 Michael Hanselmann
        try:
358 137161c9 Michael Hanselmann
          val = int(val)
359 137161c9 Michael Hanselmann
        except ValueError:
360 137161c9 Michael Hanselmann
          pass
361 137161c9 Michael Hanselmann
        else:
362 137161c9 Michael Hanselmann
          val = row[idx] = utils.FormatUnit(val)
363 137161c9 Michael Hanselmann
      if separator is None:
364 137161c9 Michael Hanselmann
        mlens[idx] = max(mlens[idx], len(val))
365 137161c9 Michael Hanselmann
366 16be8703 Iustin Pop
  result = []
367 137161c9 Michael Hanselmann
  if headers:
368 137161c9 Michael Hanselmann
    args = []
369 137161c9 Michael Hanselmann
    for idx, name in enumerate(fields):
370 137161c9 Michael Hanselmann
      hdr = headers[name]
371 137161c9 Michael Hanselmann
      if separator is None:
372 137161c9 Michael Hanselmann
        mlens[idx] = max(mlens[idx], len(hdr))
373 137161c9 Michael Hanselmann
        args.append(mlens[idx])
374 137161c9 Michael Hanselmann
      args.append(hdr)
375 16be8703 Iustin Pop
    result.append(format % tuple(args))
376 137161c9 Michael Hanselmann
377 137161c9 Michael Hanselmann
  for line in data:
378 137161c9 Michael Hanselmann
    args = []
379 137161c9 Michael Hanselmann
    for idx in xrange(len(fields)):
380 137161c9 Michael Hanselmann
      if separator is None:
381 137161c9 Michael Hanselmann
        args.append(mlens[idx])
382 137161c9 Michael Hanselmann
      args.append(line[idx])
383 16be8703 Iustin Pop
    result.append(format % tuple(args))
384 16be8703 Iustin Pop
385 16be8703 Iustin Pop
  return result