1
#============================================================================
2
# This library is free software; you can redistribute it and/or
3
# modify it under the terms of version 2.1 of the GNU Lesser General Public
4
# License as published by the Free Software Foundation.
6
# This library is distributed in the hope that it will be useful,
7
# but WITHOUT ANY WARRANTY; without even the implied warranty of
8
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9
# Lesser General Public License for more details.
11
# You should have received a copy of the GNU Lesser General Public
12
# License along with this library; if not, write to the Free Software
13
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14
#============================================================================
15
# Copyright (C) 2004, 2005 Mike Wray <mike.wray@hp.com>
16
# Copyright (C) 2005 XenSource Ltd.
17
#============================================================================
19
"""Object-oriented command-line option support.
29
def _line_wrap(text, width = 70):
32
words = text.strip().split()
35
if len(current_line) + len(word) + 1 < width:
36
current_line += word + ' '
38
lines.append(current_line.strip())
39
current_line = word + ' '
42
lines.append(current_line.strip())
45
def wrap(text, width = 70):
46
""" Really basic textwrap. Useful because textwrap is not available
47
for Python 2.2, and textwrap.wrap ignores newlines in Python 2.3+.
53
for line in text.split('\n'):
54
lines += _line_wrap(line, width)
57
class OptionError(Exception):
58
"""Denotes an error in option parsing."""
59
def __init__(self, message, usage = ''):
60
self.message = message
65
class XMLFileError(Exception):
66
"""Thrown is input is an XML File"""
67
def __init__(self, XMLFile):
68
self.XMLFile = XMLFile
70
return "XMLFileError: %s" % self.XMLFile
75
"""An individual option.
77
def __init__(self, opts, name, short=None, long=None,
78
val=None, fn=None, use=None, default=None):
81
opts parent options object
82
name name of the field it controls
83
short short (1-char) command line switch (optional)
84
long long command-line switch. Defaults to option name.
85
val string used to print option args in help.
86
If val is not specified the option has no arg.
87
fn function to call when the option is specified.
88
use usage (help) string
89
default default value if not specified on command-line
99
self.default = default
102
self.optkeys.append('-' + self.short)
104
self.optkeys.append('--' + self.long)
106
self.specified_opt = None
107
self.specified_val = None
113
self.specified_opt = None
114
self.specified_val = None
116
self.set(self.default)
120
return self.name + '=' + str(self.specified_val)
123
""" Formats the option into:
124
'-k, --key description'
128
keys = ', '.join(['%s=%s' % (k, self.val) for k in self.optkeys])
130
keys = ', '.join(self.optkeys)
131
desc = wrap(self.use, 55)
132
if len(keys) > PARAM_WIDTH:
135
wrapped = ('\n' + ' ' * (PARAM_WIDTH + 1)).join(desc)
136
return keys.ljust(PARAM_WIDTH + 1) + wrapped
138
def set(self, value):
139
"""Set the option value.
141
self.opts.setopt(self.name, value)
144
"""Get the option value.
146
return self.opts.getopt(self.name)
148
def append(self, value):
149
"""Append a value to the option value.
156
"""Short option spec.
160
return self.short + ':'
171
return self.long + '='
177
def format(self, str, start=' ', out=sys.stdout):
178
"""Print a string, with consistent indentation at the start of lines.
180
lines = str.split('\n')
188
def show(self, out=sys.stdout):
190
for x in self.optkeys:
199
self.format(self.use, out=out);
201
self.format('Default ' + str(self.default or 'None'), out=out)
203
def specify(self, k, v):
204
"""Specify the option. Called when the option is set
205
from the command line.
208
v optional value given (if any)
210
if k in self.optkeys:
211
if self.val is None and v:
212
self.opts.err("Option '%s' does not take a value" % k)
213
self.specified_opt = k
214
self.specified_val = v
222
"""Test whether the option has been specified: set
223
from the command line.
225
return self.specified_opt
228
"""An individual option variable.
230
def __init__(self, opts, name,
231
val=None, fn=None, use=None, default=None):
234
opts parent options object
235
name name of the field it controls
236
val string used to print option args in help.
237
If val is not specified the option has no arg.
238
fn function to call when the option is specified.
239
use usage (help) string
240
default default value if not specified on command-line
244
Opt.__init__(self, opts, name, val=val, fn=fn, use=use, default=default)
246
self.optkeys.append(self.long)
254
def show(self, out=sys.stdout):
255
print >>out, ' %s=%s' % (self.optkeys[0], self.val)
257
self.format(self.use, out=out);
259
self.format('Default ' + str(self.default or 'None'), out=out)
262
"""Class to hold option values.
268
"""Container for options.
271
imports = ["import sys",
274
"from xen.util.ip import *",
277
def __init__(self, use=None):
278
"""Options constructor.
285
# Options indexed by name.
286
self.options_map = {}
287
# Command-line arguments.
290
self.vals = OptVals()
291
# Variables for default scripts.
293
# Option to use for bare words.
294
self.default_opt = None
298
self.vals = OptVals()
300
for opt in self.options:
305
return '\n'.join(map(str, self.options))
308
options = [s for s in self.options if s.optkeys[0][0] == '-']
311
output += '\nOptions:\n\n'
312
output += '\n'.join([str(o) for o in options])
317
optvals = [s for s in self.options if s.optkeys[0][0] != '-']
320
output += '\nValues:\n\n'
321
output += '\n'.join([str(o) for o in optvals])
325
def opt(self, name, **args):
329
**args keyword params for option constructor
331
x = Opt(self, name, **args)
332
self.options.append(x)
333
self.options_map[name] = x
336
def default(self, name):
337
self.default_opt = name
339
def getdefault(self, val):
340
if self.default_opt is None:
342
opt = self.option(self.default_opt)
345
def var(self, name, **args):
346
x = OptVar(self, name, **args)
347
self.options.append(x)
348
self.options_map[name] = x
351
def setvar(self, var, val):
352
"""Set a default script variable.
356
def getvar(self, var):
357
"""Get a default script variable.
359
return self.vars.get(var)
361
def option(self, name):
362
"""Get an option (object).
364
return self.options_map.get(name)
366
def setopt(self, name, val):
367
"""Set an option value.
368
An option can also be set using 'opts.vals.name = val'.
370
setattr(self.vals, name, val)
372
def getopt(self, name):
373
"""Get an option value.
374
An option value can also be got using 'opts.vals.name'.
376
return getattr(self.vals, name)
378
def specified(self, name):
379
"""Test if an option has been specified.
381
opt = self.option(name)
382
return opt and opt.specified()
385
"""Print an error to stderr and exit.
387
print >>sys.stderr, "Error:", msg
391
"""Print a message to stdout (unless quiet is set).
393
if self.vals.quiet: return
397
"""Print a warning to stdout.
399
print >>sys.stderr, "Warning:", msg
401
def parse(self, argv):
402
"""Parse arguments argv using the options.
404
return remaining arguments
408
# hack to work around lack of gnu getopts parsing in python 2.2
412
# let getopt parse whatever it feels like -- if anything
414
(xvals, args) = getopt.getopt(args[0:],
417
except getopt.GetoptError, err:
418
raise OptionError(str(err), self.use)
422
for opt in self.options:
423
if opt.specify(k, v): break
425
raise OptionError('Unknown option: %s' % k, self.use)
430
# then process the 1st arg
431
(arg,args) = (args[0], args[1:])
435
(k, v) = arg.split('=', 1)
436
for opt in self.options:
437
if opt.specify(k, v):
440
elif self.getdefault(arg):
447
def short_opts(self):
448
"""Get short options specifier for getopt.
451
for x in self.options:
458
"""Get long options specifier for getopt.
461
for x in self.options:
468
print 'Usage: ', self.argv[0], self.use or 'OPTIONS'
471
for opt in self.options:
478
print 'The config file defines the following variables:'
479
for var in self.vars:
484
def config_usage(self):
486
print 'The following are automically imported:'
487
for x in self.imports:
492
def load_defconfig(self, help=0):
493
"""Load a defconfig script. Assumes these options set:
495
'defconfig' script name
497
for x in [ '' ] + self.vals.path.split(':'):
499
p = os.path.join(x, self.vals.defconfig)
501
p = self.vals.defconfig
502
if not p.startswith('/'):
503
p = os.path.join(os.path.curdir, p)
504
if os.path.exists(p):
505
self.info('Using config file "%s".' % p)
508
is_xml = (f.read(1) == '<')
512
raise XMLFileError(p)
517
raise OptionError('Unable to open config file: %s' % \
521
def load(self, defconfig, help):
522
"""Load a defconfig file. Local variables in the file
523
are used to set options with the same names.
524
Variables are not used to set options that are already specified.
526
# Create global and local dicts for the file.
527
# Initialize locals to the vars.
528
# Use exec to do the standard imports and
529
# define variables we are passing to the script.
532
locs.update(self.vars)
533
cmd = '\n'.join(self.imports +
534
[ "from xen.xm.help import Vars",
535
"xm_file = '%s'" % defconfig,
536
"xm_help = %d" % help,
537
"xm_vars = Vars(xm_file, xm_help, locals())"
539
exec cmd in globs, locs
541
execfile(defconfig, globs, locs)
542
except SyntaxError,e:
544
"Errors were found at line %d while processing %s:\n\t%s"\
545
%(e.lineno,defconfig,e.text)
551
# Extract the values set by the script and set the corresponding
552
# options, if not set on the command line.
553
vtypes = [ types.StringType,
558
for (k, v) in locs.items():
559
if self.specified(k): continue
560
if not(type(v) in vtypes): continue
563
def set_true(opt, k, v):
564
"""Set an option true."""
567
def set_false(opt, k, v):
568
"""Set an option false."""
571
def set_bool(opt, k, v):
572
"""Set a boolean option.
574
if v in ('yes', 'y'):
576
elif v in ('no', 'n'):
579
opt.opts.err('Invalid value:' +v)
581
def set_value(opt, k, v):
582
"""Set an option to a value."""
585
def set_int(opt, k, v):
586
"""Set an option to an integer value."""
590
opt.opts.err('Invalid value: ' + str(v))
593
def set_long(opt, k, v):
594
"""Set an option to a long integer value."""
598
opt.opts.err('Invalid value: ' + str(v))
601
def set_float(opt, k, v):
602
"""Set an option to a float value."""
606
opt.opts.err('Invalid value: ' + str(v))
609
def append_value(opt, k, v):
610
"""Append a value to a list option."""
613
def set_var(opt, k, v):
614
"""Set a default script variable.
616
(var, val) = v.strip().split('=', 1)
617
opt.opts.setvar(var.strip(), val.strip())