~ubuntu-branches/ubuntu/utopic/scons/utopic-proposed

« back to all changes in this revision

Viewing changes to engine/SCons/Optik/option.py

  • Committer: Bazaar Package Importer
  • Author(s): Mark Brown
  • Date: 2004-08-24 08:57:22 UTC
  • mfrom: (0.2.1 upstream) (1.1.1 warty)
  • Revision ID: james.westby@ubuntu.com-20040824085722-hfk4f0pjbyu0ebxv
Tags: 0.96.1-1
New upstream release.

Show diffs side-by-side

added added

removed removed

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