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