1
|
1 |
"""optik.option
|
|
2 |
|
|
3 |
Defines the Option class and some standard value-checking functions.
|
|
4 |
"""
|
|
5 |
|
|
6 |
# Copyright (c) 2001-2004 Gregory P. Ward. All rights reserved.
|
|
7 |
# See the README.txt distributed with Optik for licensing terms.
|
|
8 |
|
|
9 |
import sys
|
|
10 |
import types, string
|
|
11 |
from optik.errors import OptionError, OptionValueError, gettext
|
|
12 |
_ = gettext
|
|
13 |
|
|
14 |
__revision__ = "$Id: option.py,v 1.1 2009/02/05 23:03:30 stechong Exp $"
|
|
15 |
|
|
16 |
__all__ = ['Option']
|
|
17 |
|
|
18 |
# Do the right thing with boolean values for all known Python versions.
|
|
19 |
try:
|
|
20 |
True, False
|
|
21 |
except NameError:
|
|
22 |
(True, False) = (1, 0)
|
|
23 |
|
|
24 |
# For Python 1.5, just ignore unicode (try it as str)
|
|
25 |
try:
|
|
26 |
unicode
|
|
27 |
except NameError:
|
|
28 |
unicode=str
|
|
29 |
try:
|
|
30 |
types.UnicodeType
|
|
31 |
except AttributeError:
|
|
32 |
types.UnicodeType = types.StringType
|
|
33 |
|
|
34 |
_idmax = 2L * sys.maxint + 1
|
|
35 |
|
|
36 |
def _repr(self):
|
|
37 |
return "<%s at 0x%x: %s>" % (self.__class__.__name__,
|
|
38 |
id(self) & _idmax,
|
|
39 |
self)
|
|
40 |
|
|
41 |
def _parse_num(val, type):
|
|
42 |
if string.lower(val[:2]) == "0x": # hexadecimal
|
|
43 |
radix = 16
|
|
44 |
elif string.lower(val[:2]) == "0b": # binary
|
|
45 |
radix = 2
|
|
46 |
val = val[2:] or "0" # have to remove "0b" prefix
|
|
47 |
elif val[:1] == "0": # octal
|
|
48 |
radix = 8
|
|
49 |
else: # decimal
|
|
50 |
radix = 10
|
|
51 |
|
|
52 |
try:
|
|
53 |
return type(val, radix)
|
|
54 |
except TypeError:
|
|
55 |
# In Python pre-2.0, int() and long() did not support the radix
|
|
56 |
# argument. We catch the type error (not to be confused with ValueError,
|
|
57 |
# which is a real parsing failure), and try again with string.atol.
|
|
58 |
return type(string.atol(val, radix))
|
|
59 |
|
|
60 |
def _parse_int(val):
|
|
61 |
return _parse_num(val, int)
|
|
62 |
|
|
63 |
def _parse_long(val):
|
|
64 |
return _parse_num(val, long)
|
|
65 |
|
|
66 |
_builtin_cvt = { "int" : (_parse_int, _("integer")),
|
|
67 |
"long" : (_parse_long, _("long integer")),
|
|
68 |
"float" : (float, _("floating-point")),
|
|
69 |
"complex" : (complex, _("complex")) }
|
|
70 |
|
|
71 |
def check_builtin(option, opt, value):
|
|
72 |
(cvt, what) = _builtin_cvt[option.type]
|
|
73 |
try:
|
|
74 |
return cvt(value)
|
|
75 |
except ValueError:
|
|
76 |
raise OptionValueError(
|
|
77 |
_("option %s: invalid %s value: %s") % (opt, what, repr(value)))
|
|
78 |
|
|
79 |
def check_choice(option, opt, value):
|
|
80 |
if value in option.choices:
|
|
81 |
return value
|
|
82 |
else:
|
|
83 |
choices = string.join(map(repr, option.choices), ", ")
|
|
84 |
raise OptionValueError(
|
|
85 |
_("option %s: invalid choice: %s (choose from %s)")
|
|
86 |
% (opt, repr(value), choices))
|
|
87 |
|
|
88 |
# Not supplying a default is different from a default of None,
|
|
89 |
# so we need an explicit "not supplied" value.
|
|
90 |
NO_DEFAULT = ("NO", "DEFAULT")
|
|
91 |
|
|
92 |
|
|
93 |
class Option:
|
|
94 |
"""
|
|
95 |
Instance attributes:
|
|
96 |
_short_opts : [string]
|
|
97 |
_long_opts : [string]
|
|
98 |
|
|
99 |
action : string
|
|
100 |
type : string
|
|
101 |
dest : string
|
|
102 |
default : any
|
|
103 |
nargs : int
|
|
104 |
const : any
|
|
105 |
choices : [string]
|
|
106 |
callback : function
|
|
107 |
callback_args : (any*)
|
|
108 |
callback_kwargs : { string : any }
|
|
109 |
help : string
|
|
110 |
metavar : string
|
|
111 |
"""
|
|
112 |
|
|
113 |
# The list of instance attributes that may be set through
|
|
114 |
# keyword args to the constructor.
|
|
115 |
ATTRS = ['action',
|
|
116 |
'type',
|
|
117 |
'dest',
|
|
118 |
'default',
|
|
119 |
'nargs',
|
|
120 |
'const',
|
|
121 |
'choices',
|
|
122 |
'callback',
|
|
123 |
'callback_args',
|
|
124 |
'callback_kwargs',
|
|
125 |
'help',
|
|
126 |
'metavar']
|
|
127 |
|
|
128 |
# The set of actions allowed by option parsers. Explicitly listed
|
|
129 |
# here so the constructor can validate its arguments.
|
|
130 |
ACTIONS = ("store",
|
|
131 |
"store_const",
|
|
132 |
"store_true",
|
|
133 |
"store_false",
|
|
134 |
"append",
|
|
135 |
"append_const",
|
|
136 |
"count",
|
|
137 |
"callback",
|
|
138 |
"help",
|
|
139 |
"version")
|
|
140 |
|
|
141 |
# The set of actions that involve storing a value somewhere;
|
|
142 |
# also listed just for constructor argument validation. (If
|
|
143 |
# the action is one of these, there must be a destination.)
|
|
144 |
STORE_ACTIONS = ("store",
|
|
145 |
"store_const",
|
|
146 |
"store_true",
|
|
147 |
"store_false",
|
|
148 |
"append",
|
|
149 |
"append_const",
|
|
150 |
"count")
|
|
151 |
|
|
152 |
# The set of actions for which it makes sense to supply a value
|
|
153 |
# type, ie. which may consume an argument from the command line.
|
|
154 |
TYPED_ACTIONS = ("store",
|
|
155 |
"append",
|
|
156 |
"callback")
|
|
157 |
|
|
158 |
# The set of actions which *require* a value type, ie. that
|
|
159 |
# always consume an argument from the command line.
|
|
160 |
ALWAYS_TYPED_ACTIONS = ("store",
|
|
161 |
"append")
|
|
162 |
|
|
163 |
# The set of actions which take a 'const' attribute.
|
|
164 |
CONST_ACTIONS = ("store_const",
|
|
165 |
"append_const")
|
|
166 |
|
|
167 |
# The set of known types for option parsers. Again, listed here for
|
|
168 |
# constructor argument validation.
|
|
169 |
TYPES = ("string", "int", "long", "float", "complex", "choice")
|
|
170 |
|
|
171 |
# Dictionary of argument checking functions, which convert and
|
|
172 |
# validate option arguments according to the option type.
|
|
173 |
#
|
|
174 |
# Signature of checking functions is:
|
|
175 |
# check(option : Option, opt : string, value : string) -> any
|
|
176 |
# where
|
|
177 |
# option is the Option instance calling the checker
|
|
178 |
# opt is the actual option seen on the command-line
|
|
179 |
# (eg. "-a", "--file")
|
|
180 |
# value is the option argument seen on the command-line
|
|
181 |
#
|
|
182 |
# The return value should be in the appropriate Python type
|
|
183 |
# for option.type -- eg. an integer if option.type == "int".
|
|
184 |
#
|
|
185 |
# If no checker is defined for a type, arguments will be
|
|
186 |
# unchecked and remain strings.
|
|
187 |
TYPE_CHECKER = { "int" : check_builtin,
|
|
188 |
"long" : check_builtin,
|
|
189 |
"float" : check_builtin,
|
|
190 |
"complex": check_builtin,
|
|
191 |
"choice" : check_choice,
|
|
192 |
}
|
|
193 |
|
|
194 |
|
|
195 |
# CHECK_METHODS is a list of unbound method objects; they are called
|
|
196 |
# by the constructor, in order, after all attributes are
|
|
197 |
# initialized. The list is created and filled in later, after all
|
|
198 |
# the methods are actually defined. (I just put it here because I
|
|
199 |
# like to define and document all class attributes in the same
|
|
200 |
# place.) Subclasses that add another _check_*() method should
|
|
201 |
# define their own CHECK_METHODS list that adds their check method
|
|
202 |
# to those from this class.
|
|
203 |
CHECK_METHODS = None
|
|
204 |
|
|
205 |
|
|
206 |
# -- Constructor/initialization methods ----------------------------
|
|
207 |
|
|
208 |
def __init__(self, *opts, **attrs):
|
|
209 |
# Set _short_opts, _long_opts attrs from 'opts' tuple.
|
|
210 |
# Have to be set now, in case no option strings are supplied.
|
|
211 |
self._short_opts = []
|
|
212 |
self._long_opts = []
|
|
213 |
opts = self._check_opt_strings(opts)
|
|
214 |
self._set_opt_strings(opts)
|
|
215 |
|
|
216 |
# Set all other attrs (action, type, etc.) from 'attrs' dict
|
|
217 |
self._set_attrs(attrs)
|
|
218 |
|
|
219 |
# Check all the attributes we just set. There are lots of
|
|
220 |
# complicated interdependencies, but luckily they can be farmed
|
|
221 |
# out to the _check_*() methods listed in CHECK_METHODS -- which
|
|
222 |
# could be handy for subclasses! The one thing these all share
|
|
223 |
# is that they raise OptionError if they discover a problem.
|
|
224 |
for checker in self.CHECK_METHODS:
|
|
225 |
checker(self)
|
|
226 |
|
|
227 |
def _check_opt_strings(self, opts):
|
|
228 |
# Filter out None because early versions of Optik had exactly
|
|
229 |
# one short option and one long option, either of which
|
|
230 |
# could be None.
|
|
231 |
opts = filter(None, opts)
|
|
232 |
if not opts:
|
|
233 |
raise TypeError("at least one option string must be supplied")
|
|
234 |
return opts
|
|
235 |
|
|
236 |
def _set_opt_strings(self, opts):
|
|
237 |
for opt in opts:
|
|
238 |
if len(opt) < 2:
|
|
239 |
raise OptionError(
|
|
240 |
"invalid option string %s: "
|
|
241 |
"must be at least two characters long" % repr(opt), self)
|
|
242 |
elif len(opt) == 2:
|
|
243 |
if not (opt[0] == "-" and opt[1] != "-"):
|
|
244 |
raise OptionError(
|
|
245 |
"invalid short option string %s: "
|
|
246 |
"must be of the form -x, (x any non-dash char)" % repr(opt),
|
|
247 |
self)
|
|
248 |
self._short_opts.append(opt)
|
|
249 |
else:
|
|
250 |
if not (opt[0:2] == "--" and opt[2] != "-"):
|
|
251 |
raise OptionError(
|
|
252 |
"invalid long option string %s: "
|
|
253 |
"must start with --, followed by non-dash" % repr(opt),
|
|
254 |
self)
|
|
255 |
self._long_opts.append(opt)
|
|
256 |
|
|
257 |
def _set_attrs(self, attrs):
|
|
258 |
for attr in self.ATTRS:
|
|
259 |
if attrs.has_key(attr):
|
|
260 |
setattr(self, attr, attrs[attr])
|
|
261 |
del attrs[attr]
|
|
262 |
else:
|
|
263 |
if attr == 'default':
|
|
264 |
setattr(self, attr, NO_DEFAULT)
|
|
265 |
else:
|
|
266 |
setattr(self, attr, None)
|
|
267 |
if attrs:
|
|
268 |
raise OptionError(
|
|
269 |
"invalid keyword arguments: %s" % string.join(attrs.keys(), ", "),
|
|
270 |
self)
|
|
271 |
|
|
272 |
|
|
273 |
# -- Constructor validation methods --------------------------------
|
|
274 |
|
|
275 |
def _check_action(self):
|
|
276 |
if self.action is None:
|
|
277 |
self.action = "store"
|
|
278 |
elif self.action not in self.ACTIONS:
|
|
279 |
raise OptionError("invalid action: %s" % repr(self.action), self)
|
|
280 |
|
|
281 |
def _check_type(self):
|
|
282 |
if self.type is None:
|
|
283 |
if self.action in self.ALWAYS_TYPED_ACTIONS:
|
|
284 |
if self.choices is not None:
|
|
285 |
# The "choices" attribute implies "choice" type.
|
|
286 |
self.type = "choice"
|
|
287 |
else:
|
|
288 |
# No type given? "string" is the most sensible default.
|
|
289 |
self.type = "string"
|
|
290 |
else:
|
|
291 |
# Allow type objects as an alternative to their names.
|
|
292 |
if hasattr(self.type, "__name__"):
|
|
293 |
self.type = self.type.__name__
|
|
294 |
if self.type == "str":
|
|
295 |
self.type = "string"
|
|
296 |
|
|
297 |
if self.type not in self.TYPES:
|
|
298 |
raise OptionError("invalid option type: %s" % repr(self.type), self)
|
|
299 |
if self.action not in self.TYPED_ACTIONS:
|
|
300 |
raise OptionError(
|
|
301 |
"must not supply a type for action %s" % repr(self.action), self)
|
|
302 |
|
|
303 |
def _check_choice(self):
|
|
304 |
if self.type == "choice":
|
|
305 |
if self.choices is None:
|
|
306 |
raise OptionError(
|
|
307 |
"must supply a list of choices for type 'choice'", self)
|
|
308 |
elif type(self.choices) not in (types.TupleType, types.ListType):
|
|
309 |
raise OptionError(
|
|
310 |
"choices must be a list of strings ('%s' supplied)"
|
|
311 |
% string.split(str(type(self.choices)), "'")[1], self)
|
|
312 |
elif self.choices is not None:
|
|
313 |
raise OptionError(
|
|
314 |
"must not supply choices for type %s" % repr(self.type), self)
|
|
315 |
|
|
316 |
def _check_dest(self):
|
|
317 |
# No destination given, and we need one for this action. The
|
|
318 |
# self.type check is for callbacks that take a value.
|
|
319 |
takes_value = (self.action in self.STORE_ACTIONS or
|
|
320 |
self.type is not None)
|
|
321 |
if self.dest is None and takes_value:
|
|
322 |
|
|
323 |
# Glean a destination from the first long option string,
|
|
324 |
# or from the first short option string if no long options.
|
|
325 |
if self._long_opts:
|
|
326 |
# eg. "--foo-bar" -> "foo_bar"
|
|
327 |
self.dest = string.replace(self._long_opts[0][2:], '-', '_')
|
|
328 |
else:
|
|
329 |
self.dest = self._short_opts[0][1]
|
|
330 |
|
|
331 |
def _check_const(self):
|
|
332 |
if self.action not in self.CONST_ACTIONS and self.const is not None:
|
|
333 |
raise OptionError(
|
|
334 |
"'const' must not be supplied for action %s" % repr(self.action),
|
|
335 |
self)
|
|
336 |
|
|
337 |
def _check_nargs(self):
|
|
338 |
if self.action in self.TYPED_ACTIONS:
|
|
339 |
if self.nargs is None:
|
|
340 |
self.nargs = 1
|
|
341 |
elif self.nargs is not None:
|
|
342 |
raise OptionError(
|
|
343 |
"'nargs' must not be supplied for action %s" % repr(self.action),
|
|
344 |
self)
|
|
345 |
|
|
346 |
def _check_callback(self):
|
|
347 |
if self.action == "callback":
|
|
348 |
if not callable(self.callback):
|
|
349 |
raise OptionError(
|
|
350 |
"callback not callable: %s" % repr(self.callback), self)
|
|
351 |
if (self.callback_args is not None and
|
|
352 |
type(self.callback_args) is not types.TupleType):
|
|
353 |
raise OptionError(
|
|
354 |
"callback_args, if supplied, must be a tuple: not %s"
|
|
355 |
% repr(self.callback_args), self)
|
|
356 |
if (self.callback_kwargs is not None and
|
|
357 |
type(self.callback_kwargs) is not types.DictType):
|
|
358 |
raise OptionError(
|
|
359 |
"callback_kwargs, if supplied, must be a dict: not %s"
|
|
360 |
% repr(self.callback_kwargs), self)
|
|
361 |
else:
|
|
362 |
if self.callback is not None:
|
|
363 |
raise OptionError(
|
|
364 |
"callback supplied (%s) for non-callback option"
|
|
365 |
% repr(self.callback), self)
|
|
366 |
if self.callback_args is not None:
|
|
367 |
raise OptionError(
|
|
368 |
"callback_args supplied for non-callback option", self)
|
|
369 |
if self.callback_kwargs is not None:
|
|
370 |
raise OptionError(
|
|
371 |
"callback_kwargs supplied for non-callback option", self)
|
|
372 |
|
|
373 |
|
|
374 |
CHECK_METHODS = [_check_action,
|
|
375 |
_check_type,
|
|
376 |
_check_choice,
|
|
377 |
_check_dest,
|
|
378 |
_check_const,
|
|
379 |
_check_nargs,
|
|
380 |
_check_callback]
|
|
381 |
|
|
382 |
|
|
383 |
# -- Miscellaneous methods -----------------------------------------
|
|
384 |
|
|
385 |
def __str__(self):
|
|
386 |
return string.join(self._short_opts + self._long_opts, "/")
|
|
387 |
|
|
388 |
__repr__ = _repr
|
|
389 |
|
|
390 |
def takes_value(self):
|
|
391 |
return self.type is not None
|
|
392 |
|
|
393 |
def get_opt_string(self):
|
|
394 |
if self._long_opts:
|
|
395 |
return self._long_opts[0]
|
|
396 |
else:
|
|
397 |
return self._short_opts[0]
|
|
398 |
|
|
399 |
|
|
400 |
# -- Processing methods --------------------------------------------
|
|
401 |
|
|
402 |
def check_value(self, opt, value):
|
|
403 |
checker = self.TYPE_CHECKER.get(self.type)
|
|
404 |
if checker is None:
|
|
405 |
return value
|
|
406 |
else:
|
|
407 |
return checker(self, opt, value)
|
|
408 |
|
|
409 |
def convert_value(self, opt, value):
|
|
410 |
if value is not None:
|
|
411 |
if self.nargs == 1:
|
|
412 |
return self.check_value(opt, value)
|
|
413 |
else:
|
|
414 |
return tuple(map(lambda v,self=self,opt=opt: self.check_value(opt, v), value))
|
|
415 |
|
|
416 |
def process(self, opt, value, values, parser):
|
|
417 |
|
|
418 |
# First, convert the value(s) to the right type. Howl if any
|
|
419 |
# value(s) are bogus.
|
|
420 |
value = self.convert_value(opt, value)
|
|
421 |
|
|
422 |
# And then take whatever action is expected of us.
|
|
423 |
# This is a separate method to make life easier for
|
|
424 |
# subclasses to add new actions.
|
|
425 |
return self.take_action(
|
|
426 |
self.action, self.dest, opt, value, values, parser)
|
|
427 |
|
|
428 |
def take_action(self, action, dest, opt, value, values, parser):
|
|
429 |
if action == "store":
|
|
430 |
setattr(values, dest, value)
|
|
431 |
elif action == "store_const":
|
|
432 |
setattr(values, dest, self.const)
|
|
433 |
elif action == "store_true":
|
|
434 |
setattr(values, dest, True)
|
|
435 |
elif action == "store_false":
|
|
436 |
setattr(values, dest, False)
|
|
437 |
elif action == "append":
|
|
438 |
values.ensure_value(dest, []).append(value)
|
|
439 |
elif action == "append_const":
|
|
440 |
values.ensure_value(dest, []).append(self.const)
|
|
441 |
elif action == "count":
|
|
442 |
setattr(values, dest, values.ensure_value(dest, 0) + 1)
|
|
443 |
elif action == "callback":
|
|
444 |
args = self.callback_args or ()
|
|
445 |
kwargs = self.callback_kwargs or {}
|
|
446 |
apply(self.callback, (self, opt, value, parser)+args, kwargs)
|
|
447 |
elif action == "help":
|
|
448 |
parser.print_help()
|
|
449 |
parser.exit()
|
|
450 |
elif action == "version":
|
|
451 |
parser.print_version()
|
|
452 |
parser.exit()
|
|
453 |
else:
|
|
454 |
raise RuntimeError, "unknown action %s" % repr(self.action)
|
|
455 |
|
|
456 |
return 1
|
|
457 |
|
|
458 |
# class Option
|