source: trunk/packages/xen-3.1/xen-3.1/tools/python/xen/xm/opts.py @ 34

Last change on this file since 34 was 34, checked in by hartmans, 18 years ago

Add xen and xen-common

File size: 17.3 KB
Line 
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.
5#
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.
10#
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#============================================================================
18
19"""Object-oriented command-line option support.
20"""
21import getopt
22import os
23import os.path
24import sys
25import types
26
27
28
29def _line_wrap(text, width = 70):
30    lines = []
31    current_line = ''
32    words = text.strip().split()
33    while words:
34        word = words.pop(0)
35        if len(current_line) + len(word) + 1 < width:
36            current_line += word + ' '
37        else:
38            lines.append(current_line.strip())
39            current_line = word + ' '
40           
41    if current_line:
42        lines.append(current_line.strip())
43    return lines
44
45def 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+.
48    """
49    if len(text) < width:
50        return [text]
51   
52    lines = []
53    for line in text.split('\n'):
54        lines += _line_wrap(line, width)
55    return lines
56
57class OptionError(Exception):
58    """Denotes an error in option parsing."""
59    def __init__(self, message, usage = ''):
60        self.message = message
61        self.usage = usage
62    def __str__(self):
63        return self.message
64
65class XMLFileError(Exception):
66    """Thrown is input is an XML File"""
67    def __init__(self, XMLFile):
68        self.XMLFile = XMLFile
69    def __str__(self):
70        return "XMLFileError: %s" % self.XMLFile
71    def getFile(self):
72        return self.XMLFile
73
74class Opt:
75    """An individual option.
76    """
77    def __init__(self, opts, name, short=None, long=None,
78                 val=None, fn=None, use=None, default=None):
79        """Create an option.
80
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
90        """
91        self.opts = opts
92        self.name = name
93        self.short = short
94        if long is None:
95            long = name
96        self.long = long
97        self.val = val
98        self.use = use
99        self.default = default
100        self.optkeys = []
101        if self.short:
102            self.optkeys.append('-' + self.short)
103        if self.long:
104            self.optkeys.append('--' + self.long)
105        self.fn = fn
106        self.specified_opt = None
107        self.specified_val = None
108        self.value = None
109        self.set(default)
110
111
112    def reset(self):
113        self.specified_opt = None
114        self.specified_val = None
115        self.value = None
116        self.set(self.default)
117
118
119    def __repr__(self):
120        return self.name + '=' + str(self.specified_val)
121
122    def __str__(self):
123        """ Formats the option into:
124        '-k, --key     description'
125        """
126        PARAM_WIDTH = 20
127        if self.val:
128            keys = ', '.join(['%s=%s' % (k, self.val) for k in self.optkeys])
129        else:
130            keys = ', '.join(self.optkeys)
131        desc = wrap(self.use, 55)
132        if len(keys) > PARAM_WIDTH:
133            desc = [''] + desc
134           
135        wrapped = ('\n' + ' ' * (PARAM_WIDTH + 1)).join(desc)
136        return keys.ljust(PARAM_WIDTH + 1) + wrapped
137
138    def set(self, value):
139        """Set the option value.
140        """
141        self.opts.setopt(self.name, value)
142
143    def get(self):
144        """Get the option value.
145        """
146        return self.opts.getopt(self.name)
147
148    def append(self, value):
149        """Append a value to the option value.
150        """
151        v = self.get() or []
152        v.append(value)
153        self.set(v)
154
155    def short_opt(self):
156        """Short option spec.
157        """
158        if self.short:
159            if self.val:
160                return self.short + ':'
161            else:
162                return self.short
163        else:
164            return None
165
166    def long_opt(self):
167        """Long option spec.
168        """
169        if self.long:
170            if self.val:
171                return self.long + '='
172            else:
173                return self.long
174        else:
175            return None
176
177    def format(self, str, start='    ', out=sys.stdout):
178        """Print a string, with consistent indentation at the start of lines.
179        """
180        lines = str.split('\n')
181        for l in lines:
182            l = l.strip()
183            if start:
184                out.write(start)
185            out.write(l)
186            out.write('\n')
187
188    def show(self, out=sys.stdout):
189        sep = ' '
190        for x in self.optkeys:
191            out.write(sep)
192            out.write(x)
193            sep = ', '
194        if self.val:
195            out.write(' ')
196            out.write(self.val)
197        out.write('\n')
198        if self.use:
199            self.format(self.use, out=out);
200        if self.val:
201            self.format('Default ' + str(self.default or 'None'), out=out)
202
203    def specify(self, k, v):
204        """Specify the option. Called when the option is set
205        from the command line.
206
207        k  option switch used
208        v  optional value given (if any)
209        """
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
215            if self.fn:
216                self.fn(self, k, v)
217            return 1
218        else:
219            return 0
220
221    def specified(self):
222        """Test whether the option has been specified: set
223        from the command line.
224        """
225        return self.specified_opt
226
227class OptVar(Opt):
228    """An individual option variable.
229    """
230    def __init__(self, opts, name,
231                 val=None, fn=None, use=None, default=None):
232        """Create an option.
233
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
241        """
242        if val is None:
243            val = name.upper()
244        Opt.__init__(self, opts, name, val=val, fn=fn, use=use, default=default)
245        self.optkeys = []
246        self.optkeys.append(self.long)
247
248    def short_opt(self):
249        return None
250
251    def long_opt(self):
252        return None
253
254    def show(self, out=sys.stdout):
255        print >>out, ' %s=%s' % (self.optkeys[0], self.val) 
256        if self.use:
257            self.format(self.use, out=out);
258        if self.val:
259            self.format('Default ' + str(self.default or 'None'), out=out)
260
261class OptVals:
262    """Class to hold option values.
263    """
264    def __init__(self):
265        self.quiet = False
266
267class Opts:
268    """Container for options.
269    """
270
271    imports = ["import sys",
272               "import os",
273               "import os.path",
274               "from xen.util.ip import *",
275               ]
276
277    def __init__(self, use=None):
278        """Options constructor.
279
280        use  usage string
281        """
282        self.use = use
283        # List of options.
284        self.options = []
285        # Options indexed by name.
286        self.options_map = {}
287        # Command-line arguments.
288        self.argv = []
289        # Option values.
290        self.vals = OptVals()
291        # Variables for default scripts.
292        self.vars = {}
293        # Option to use for bare words.
294        self.default_opt = None
295
296
297    def reset(self):
298        self.vals = OptVals()
299        self.vars = {}
300        for opt in self.options:
301            opt.reset()
302
303
304    def __repr__(self):
305        return '\n'.join(map(str, self.options))
306
307    def __str__(self):
308        options = [s for s in self.options if s.optkeys[0][0] == '-']
309        output = ''
310        if options:
311            output += '\nOptions:\n\n'
312            output += '\n'.join([str(o) for o in options])
313            output += '\n'
314        return output
315
316    def val_usage(self):
317        optvals = [s for s in self.options if s.optkeys[0][0] != '-']
318        output = ''
319        if optvals:
320            output += '\nValues:\n\n'
321            output += '\n'.join([str(o) for o in optvals])
322            output += '\n'
323        return output
324   
325    def opt(self, name, **args):
326        """Add an option.
327
328        name    option name
329        **args  keyword params for option constructor
330        """
331        x = Opt(self, name, **args)
332        self.options.append(x)
333        self.options_map[name] = x
334        return x
335
336    def default(self, name):
337        self.default_opt = name
338
339    def getdefault(self, val):
340        if self.default_opt is None:
341            return 0
342        opt = self.option(self.default_opt)
343        return opt.set(val)
344
345    def var(self, name, **args):
346        x = OptVar(self, name, **args)
347        self.options.append(x)
348        self.options_map[name] = x
349        return x     
350
351    def setvar(self, var, val):
352        """Set a default script variable.
353        """
354        self.vars[var] = val
355
356    def getvar(self, var):
357        """Get a default script variable.
358        """
359        return self.vars.get(var)
360
361    def option(self, name):
362        """Get an option (object).
363        """
364        return self.options_map.get(name)
365
366    def setopt(self, name, val):
367        """Set an option value.
368        An option can also be set using 'opts.vals.name = val'.
369        """
370        setattr(self.vals, name, val)
371
372    def getopt(self, name):
373        """Get an option value.
374        An option value can also be got using 'opts.vals.name'.
375        """
376        return getattr(self.vals, name)
377
378    def specified(self, name):
379        """Test if an option has been specified.
380        """
381        opt = self.option(name)
382        return opt and opt.specified()
383
384    def err(self, msg):
385        """Print an error to stderr and exit.
386        """
387        print >>sys.stderr, "Error:", msg
388        sys.exit(1)
389
390    def info(self, msg):
391        """Print a message to stdout (unless quiet is set).
392        """
393        if self.vals.quiet: return
394        print msg
395
396    def warn(self, msg):
397        """Print a warning to stdout.
398        """
399        print >>sys.stderr, "Warning:", msg
400
401    def parse(self, argv):
402        """Parse arguments argv using the options.
403
404        return remaining arguments
405        """
406        self.argv = argv
407
408        # hack to work around lack of gnu getopts parsing in python 2.2
409        args = argv[1:]
410        xargs = []
411        while args:
412            # let getopt parse whatever it feels like -- if anything
413            try:
414                (xvals, args) = getopt.getopt(args[0:],
415                                              self.short_opts(),
416                                              self.long_opts())
417            except getopt.GetoptError, err:
418                raise OptionError(str(err), self.use)
419            #self.err(str(err))
420               
421            for (k, v) in xvals:
422                for opt in self.options:
423                    if opt.specify(k, v): break
424                else:
425                    raise OptionError('Unknown option: %s' % k, self.use)
426
427            if not args:
428                break
429           
430            # then process the 1st arg
431            (arg,args) = (args[0], args[1:])
432
433            isvar = 0
434            if '=' in arg:
435                (k, v) = arg.split('=', 1)
436                for opt in self.options:
437                    if opt.specify(k, v):
438                        isvar = 1
439                        break
440            elif self.getdefault(arg):
441                isvar = 1
442            if not isvar:
443                xargs.append(arg)
444
445        return xargs
446
447    def short_opts(self):
448        """Get short options specifier for getopt.
449        """
450        l = []
451        for x in self.options:
452            y = x.short_opt()
453            if not y: continue
454            l.append(y)
455        return ''.join(l)
456
457    def long_opts(self):
458        """Get long options specifier for getopt.
459        """
460        l = []
461        for x in self.options:
462            y = x.long_opt()
463            if not y: continue
464            l.append(y)
465        return l
466
467    def usage(self):
468        print 'Usage: ', self.argv[0], self.use or 'OPTIONS'
469        print
470        if self.options:
471            for opt in self.options:
472                opt.show()
473                print
474            print
475
476    def var_usage(self):
477        if self.vars:
478            print 'The config file defines the following variables:'
479            for var in self.vars:
480                var.show()
481                print
482            print
483
484    def config_usage(self):
485        if self.imports:
486            print 'The following are automically imported:'
487            for x in self.imports:
488                print '   ', x
489            print
490        self.var_usage()
491
492    def load_defconfig(self, help=0):
493        """Load a defconfig script. Assumes these options set:
494        'path'    search path
495        'defconfig' script name
496        """
497        for x in [ '' ] + self.vals.path.split(':'):
498            if x:
499                p = os.path.join(x, self.vals.defconfig)
500            else:
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)
506
507                f = open(p)
508                is_xml = (f.read(1) == '<')
509                f.close()
510
511                if is_xml:
512                    raise XMLFileError(p)
513
514                self.load(p, help)
515                break
516        else:
517            raise OptionError('Unable to open config file: %s' % \
518                              self.vals.defconfig,
519                              self.use)
520
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.
525        """
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.
530        globs = {}
531        locs = {}
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())"
538                          ])
539        exec cmd in globs, locs
540        try:
541            execfile(defconfig, globs, locs)
542        except SyntaxError,e:
543                raise SyntaxError, \
544                "Errors were found at line %d while processing %s:\n\t%s"\
545                %(e.lineno,defconfig,e.text)
546        except:
547            if not help: raise
548        if help:
549            self.config_usage()
550            return
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,
554                   types.ListType,
555                   types.IntType,
556                   types.FloatType
557                   ]
558        for (k, v) in locs.items():
559            if self.specified(k): continue
560            if not(type(v) in vtypes): continue
561            self.setopt(k, v)
562
563def set_true(opt, k, v):
564    """Set an option true."""
565    opt.set(1)
566
567def set_false(opt, k, v):
568    """Set an option false."""
569    opt.set(0)
570
571def set_bool(opt, k, v):
572    """Set a boolean option.
573    """
574    if v in ('yes', 'y'):
575        opt.set(1)
576    elif v in ('no', 'n'):
577        opt.set(0)
578    else:
579        opt.opts.err('Invalid value:' +v)
580       
581def set_value(opt, k, v):
582    """Set an option to a value."""
583    opt.set(v)
584
585def set_int(opt, k, v):
586    """Set an option to an integer value."""
587    try:
588        v = int(v)
589    except:
590        opt.opts.err('Invalid value: ' + str(v))
591    opt.set(v)
592
593def set_long(opt, k, v):
594    """Set an option to a long integer value."""
595    try:
596        v = long(v)
597    except:
598        opt.opts.err('Invalid value: ' + str(v))
599    opt.set(v)
600
601def set_float(opt, k, v):
602    """Set an option to a float value."""
603    try:
604        v = float(v)
605    except:
606        opt.opts.err('Invalid value: ' + str(v))
607    opt.set(v)
608
609def append_value(opt, k, v):
610    """Append a value to a list option."""
611    opt.append(v)
612
613def set_var(opt, k, v):
614    """Set a default script variable.
615    """
616    (var, val) = v.strip().split('=', 1)
617    opt.opts.setvar(var.strip(), val.strip())
618
Note: See TracBrowser for help on using the repository browser.