blob: 125dceb3e1457c8e7de2554331a23ea139efed32 [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
Greg Ward0081cc51999-08-14 23:44:37 +000050 for option in options:
51 try:
52 (long, short, help) = option
53 except ValueError:
54 raise DistutilsGetoptError, \
55 "invalid option tuple " + str (option)
56
Greg Ward2689e3d1999-03-22 14:52:19 +000057 # Type-check the option names
58 if type (long) is not StringType or len (long) < 2:
59 raise DistutilsGetoptError, \
Greg Ward0081cc51999-08-14 23:44:37 +000060 "long option '%s' must be a string of length >= 2" % \
61 long
Greg Ward2689e3d1999-03-22 14:52:19 +000062
63 if (not ((short is None) or
64 (type (short) is StringType and len (short) == 1))):
65 raise DistutilsGetoptError, \
Greg Ward0081cc51999-08-14 23:44:37 +000066 "short option '%s' must be None or string of length 1" % \
67 short
Greg Ward2689e3d1999-03-22 14:52:19 +000068
69 long_opts.append (long)
70
71 if long[-1] == '=': # option takes an argument?
72 if short: short = short + ':'
73 long = long[0:-1]
74 takes_arg[long] = 1
75 else:
76 takes_arg[long] = 0
77
78 # Now enforce some bondage on the long option name, so we can later
79 # translate it to an attribute name in 'object'. Have to do this a
80 # bit late to make sure we've removed any trailing '='.
81 if not longopt_re.match (long):
82 raise DistutilsGetoptError, \
83 ("invalid long option name '%s' " +
84 "(must be letters, numbers, hyphens only") % long
85
86 attr_name[long] = string.translate (long, longopt_xlate)
87 if short:
88 short_opts.append (short)
89 short2long[short[0]] = long
90
91 # end loop over 'options'
92
93 short_opts = string.join (short_opts)
94 try:
95 (opts, args) = getopt.getopt (args, short_opts, long_opts)
96 except getopt.error, msg:
97 raise DistutilsArgError, msg
98
99 for (opt, val) in opts:
100 if len (opt) == 2 and opt[0] == '-': # it's a short option
101 opt = short2long[opt[1]]
102
103 elif len (opt) > 2 and opt[0:2] == '--':
104 opt = opt[2:]
105
106 else:
107 raise RuntimeError, "getopt lies! (bad option string '%s')" % \
108 opt
109
110 attr = attr_name[opt]
111 if takes_arg[opt]:
112 setattr (object, attr, val)
113 else:
114 if val == '':
115 setattr (object, attr, 1)
116 else:
117 raise RuntimeError, "getopt lies! (bad value '%s')" % value
118
119 # end loop over options found in 'args'
120
121 return args
122
123# end fancy_getopt()