|
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 |