blob: c63ce61b8ed4fac2898b9caaa183ee705040fd36 [file] [log] [blame]
Greg Ward2689e3d1999-03-22 14:52:19 +00001"""distutils.fancy_getopt
2
3Wrapper around the standard getopt module that provides the following
4additional features:
5 * short and long options are tied together
6 * options have help strings, so fancy_getopt could potentially
7 create a complete usage summary
8 * options set attributes of a passed-in object
9"""
10
11# created 1999/03/03, Greg Ward
12
13__rcsid__ = "$Id$"
14
15import string, re
16from types import *
17import getopt
18from distutils.errors import *
19
20# Much like command_re in distutils.core, this is close to but not quite
21# the same as a Python NAME -- except, in the spirit of most GNU
22# utilities, we use '-' in place of '_'. (The spirit of LISP lives on!)
23# The similarities to NAME are again not a coincidence...
24longopt_re = re.compile (r'^[a-zA-Z]([a-zA-Z0-9-]*)$')
25
26# This is used to translate long options to legitimate Python identifiers
27# (for use as attributes of some object).
28longopt_xlate = string.maketrans ('-', '_')
29
30
31def fancy_getopt (options, object, args):
32
33 # The 'options' table is a list of 3-tuples:
34 # (long_option, short_option, help_string)
35 # if an option takes an argument, its long_option should have '='
36 # appended; short_option should just be a single character, no ':' in
37 # any case. If a long_option doesn't have a corresponding
38 # short_option, short_option should be None. All option tuples must
39 # have long options.
40
41 # Build the short_opts string and long_opts list, remembering how
42 # the two are tied together
43
44 short_opts = [] # we'll join 'em when done
45 long_opts = []
46 short2long = {}
47 attr_name = {}
48 takes_arg = {}
49
50 for (long, short, help) in options:
51 # Type-check the option names
52 if type (long) is not StringType or len (long) < 2:
53 raise DistutilsGetoptError, \
54 "long option must be a string of length >= 2"
55
56 if (not ((short is None) or
57 (type (short) is StringType and len (short) == 1))):
58 raise DistutilsGetoptError, \
59 "short option must be None or string of length 1"
60
61 long_opts.append (long)
62
63 if long[-1] == '=': # option takes an argument?
64 if short: short = short + ':'
65 long = long[0:-1]
66 takes_arg[long] = 1
67 else:
68 takes_arg[long] = 0
69
70 # Now enforce some bondage on the long option name, so we can later
71 # translate it to an attribute name in 'object'. Have to do this a
72 # bit late to make sure we've removed any trailing '='.
73 if not longopt_re.match (long):
74 raise DistutilsGetoptError, \
75 ("invalid long option name '%s' " +
76 "(must be letters, numbers, hyphens only") % long
77
78 attr_name[long] = string.translate (long, longopt_xlate)
79 if short:
80 short_opts.append (short)
81 short2long[short[0]] = long
82
83 # end loop over 'options'
84
85 short_opts = string.join (short_opts)
86 try:
87 (opts, args) = getopt.getopt (args, short_opts, long_opts)
88 except getopt.error, msg:
89 raise DistutilsArgError, msg
90
91 for (opt, val) in opts:
92 if len (opt) == 2 and opt[0] == '-': # it's a short option
93 opt = short2long[opt[1]]
94
95 elif len (opt) > 2 and opt[0:2] == '--':
96 opt = opt[2:]
97
98 else:
99 raise RuntimeError, "getopt lies! (bad option string '%s')" % \
100 opt
101
102 attr = attr_name[opt]
103 if takes_arg[opt]:
104 setattr (object, attr, val)
105 else:
106 if val == '':
107 setattr (object, attr, 1)
108 else:
109 raise RuntimeError, "getopt lies! (bad value '%s')" % value
110
111 # end loop over options found in 'args'
112
113 return args
114
115# end fancy_getopt()