/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/option.py

  • Committer: Ian Clatworthy
  • Date: 2007-08-30 13:53:57 UTC
  • mto: (2779.1.1 ianc-integration)
  • mto: This revision was merged to the branch mainline in revision 2783.
  • Revision ID: ian.clatworthy@internode.on.net-20070830135357-dqr7saq6ciipx09d
Fix existing option and help tests

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2004, 2005, 2006, 2007 Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
# TODO: For things like --diff-prefix, we want a way to customize the display
 
18
# of the option argument.
 
19
 
 
20
import re
 
21
 
 
22
from bzrlib.lazy_import import lazy_import
 
23
lazy_import(globals(), """
 
24
import optparse
 
25
 
 
26
from bzrlib import (
 
27
    errors,
 
28
    log,
 
29
    registry,
 
30
    revisionspec,
 
31
    symbol_versioning,
 
32
    )
 
33
""")
 
34
from bzrlib.trace import warning
 
35
 
 
36
 
 
37
def _parse_revision_str(revstr):
 
38
    """This handles a revision string -> revno.
 
39
 
 
40
    This always returns a list.  The list will have one element for
 
41
    each revision specifier supplied.
 
42
 
 
43
    >>> _parse_revision_str('234')
 
44
    [<RevisionSpec_revno 234>]
 
45
    >>> _parse_revision_str('234..567')
 
46
    [<RevisionSpec_revno 234>, <RevisionSpec_revno 567>]
 
47
    >>> _parse_revision_str('..')
 
48
    [<RevisionSpec None>, <RevisionSpec None>]
 
49
    >>> _parse_revision_str('..234')
 
50
    [<RevisionSpec None>, <RevisionSpec_revno 234>]
 
51
    >>> _parse_revision_str('234..')
 
52
    [<RevisionSpec_revno 234>, <RevisionSpec None>]
 
53
    >>> _parse_revision_str('234..456..789') # Maybe this should be an error
 
54
    [<RevisionSpec_revno 234>, <RevisionSpec_revno 456>, <RevisionSpec_revno 789>]
 
55
    >>> _parse_revision_str('234....789') #Error ?
 
56
    [<RevisionSpec_revno 234>, <RevisionSpec None>, <RevisionSpec_revno 789>]
 
57
    >>> _parse_revision_str('revid:test@other.com-234234')
 
58
    [<RevisionSpec_revid revid:test@other.com-234234>]
 
59
    >>> _parse_revision_str('revid:test@other.com-234234..revid:test@other.com-234235')
 
60
    [<RevisionSpec_revid revid:test@other.com-234234>, <RevisionSpec_revid revid:test@other.com-234235>]
 
61
    >>> _parse_revision_str('revid:test@other.com-234234..23')
 
62
    [<RevisionSpec_revid revid:test@other.com-234234>, <RevisionSpec_revno 23>]
 
63
    >>> _parse_revision_str('date:2005-04-12')
 
64
    [<RevisionSpec_date date:2005-04-12>]
 
65
    >>> _parse_revision_str('date:2005-04-12 12:24:33')
 
66
    [<RevisionSpec_date date:2005-04-12 12:24:33>]
 
67
    >>> _parse_revision_str('date:2005-04-12T12:24:33')
 
68
    [<RevisionSpec_date date:2005-04-12T12:24:33>]
 
69
    >>> _parse_revision_str('date:2005-04-12,12:24:33')
 
70
    [<RevisionSpec_date date:2005-04-12,12:24:33>]
 
71
    >>> _parse_revision_str('-5..23')
 
72
    [<RevisionSpec_revno -5>, <RevisionSpec_revno 23>]
 
73
    >>> _parse_revision_str('-5')
 
74
    [<RevisionSpec_revno -5>]
 
75
    >>> _parse_revision_str('123a')
 
76
    Traceback (most recent call last):
 
77
      ...
 
78
    NoSuchRevisionSpec: No namespace registered for string: '123a'
 
79
    >>> _parse_revision_str('abc')
 
80
    Traceback (most recent call last):
 
81
      ...
 
82
    NoSuchRevisionSpec: No namespace registered for string: 'abc'
 
83
    >>> _parse_revision_str('branch:../branch2')
 
84
    [<RevisionSpec_branch branch:../branch2>]
 
85
    >>> _parse_revision_str('branch:../../branch2')
 
86
    [<RevisionSpec_branch branch:../../branch2>]
 
87
    >>> _parse_revision_str('branch:../../branch2..23')
 
88
    [<RevisionSpec_branch branch:../../branch2>, <RevisionSpec_revno 23>]
 
89
    >>> _parse_revision_str('branch:..\\\\branch2')
 
90
    [<RevisionSpec_branch branch:..\\branch2>]
 
91
    >>> _parse_revision_str('branch:..\\\\..\\\\branch2..23')
 
92
    [<RevisionSpec_branch branch:..\\..\\branch2>, <RevisionSpec_revno 23>]
 
93
    """
 
94
    # TODO: Maybe move this into revisionspec.py
 
95
    revs = []
 
96
    # split on .. that is not followed by a / or \
 
97
    sep = re.compile(r'\.\.(?![\\/])')
 
98
    for x in sep.split(revstr):
 
99
        revs.append(revisionspec.RevisionSpec.from_string(x or None))
 
100
    return revs
 
101
 
 
102
 
 
103
def _parse_merge_type(typestring):
 
104
    return get_merge_type(typestring)
 
105
 
 
106
def get_merge_type(typestring):
 
107
    """Attempt to find the merge class/factory associated with a string."""
 
108
    from merge import merge_types
 
109
    try:
 
110
        return merge_types[typestring][0]
 
111
    except KeyError:
 
112
        templ = '%s%%7s: %%s' % (' '*12)
 
113
        lines = [templ % (f[0], f[1][1]) for f in merge_types.iteritems()]
 
114
        type_list = '\n'.join(lines)
 
115
        msg = "No known merge type %s. Supported types are:\n%s" %\
 
116
            (typestring, type_list)
 
117
        raise errors.BzrCommandError(msg)
 
118
 
 
119
 
 
120
class Option(object):
 
121
    """Description of a command line option
 
122
    
 
123
    :ivar _short_name: If this option has a single-letter name, this is it.
 
124
    Otherwise None.
 
125
    """
 
126
 
 
127
    # The dictionary of standard options. These are always legal.
 
128
    STD_OPTIONS = {}
 
129
 
 
130
    # The dictionary of commonly used options. these are only legal
 
131
    # if a command explicitly references them by name in the list
 
132
    # of supported options.
 
133
    OPTIONS = {}
 
134
 
 
135
    def __init__(self, name, help='', type=None, argname=None,
 
136
                 short_name=None, custom_callback=None):
 
137
        """Make a new command option.
 
138
 
 
139
        :param name: regular name of the command, used in the double-dash
 
140
            form and also as the parameter to the command's run() 
 
141
            method.
 
142
 
 
143
        :param help: help message displayed in command help
 
144
 
 
145
        :param type: function called to parse the option argument, or 
 
146
            None (default) if this option doesn't take an argument.
 
147
 
 
148
        :param argname: name of option argument, if any
 
149
 
 
150
        :param short_name: short option code for use with a single -, e.g.
 
151
            short_name="v" to enable parsing of -v.
 
152
 
 
153
        :param custom_callback: a callback routine to be called after normal
 
154
            processing. The signature of the callback routine is the same
 
155
            as that for normal option callbacks. See optparse in the standard
 
156
            Python library for details.
 
157
        """
 
158
        self.name = name
 
159
        self.help = help
 
160
        self.type = type
 
161
        self._short_name = short_name
 
162
        if type is None:
 
163
            assert argname is None
 
164
        elif argname is None:
 
165
            argname = 'ARG'
 
166
        self.argname = argname
 
167
        self.custom_callback = custom_callback
 
168
 
 
169
    def short_name(self):
 
170
        if self._short_name:
 
171
            return self._short_name
 
172
 
 
173
    def set_short_name(self, short_name):
 
174
        self._short_name = short_name
 
175
 
 
176
    def get_negation_name(self):
 
177
        if self.name.startswith('no-'):
 
178
            return self.name[3:]
 
179
        else:
 
180
            return 'no-' + self.name
 
181
 
 
182
    def add_option(self, parser, short_name):
 
183
        """Add this option to an Optparse parser"""
 
184
        option_strings = ['--%s' % self.name]
 
185
        if short_name is not None:
 
186
            option_strings.append('-%s' % short_name)
 
187
        optargfn = self.type
 
188
        if optargfn is None:
 
189
            parser.add_option(action='callback', 
 
190
                              callback=self._optparse_bool_callback, 
 
191
                              callback_args=(True,),
 
192
                              help=self.help,
 
193
                              *option_strings)
 
194
            negation_strings = ['--%s' % self.get_negation_name()]
 
195
            parser.add_option(action='callback', 
 
196
                              callback=self._optparse_bool_callback, 
 
197
                              callback_args=(False,),
 
198
                              help=optparse.SUPPRESS_HELP, *negation_strings)
 
199
        else:
 
200
            parser.add_option(action='callback', 
 
201
                              callback=self._optparse_callback, 
 
202
                              type='string', metavar=self.argname.upper(),
 
203
                              help=self.help,
 
204
                              default=OptionParser.DEFAULT_VALUE, 
 
205
                              *option_strings)
 
206
 
 
207
    def _optparse_bool_callback(self, option, opt_str, value, parser, bool_v):
 
208
        setattr(parser.values, self.name, bool_v)
 
209
        if self.custom_callback is not None:
 
210
            self.custom_callback(option, self.name, bool_v, parser)
 
211
 
 
212
    def _optparse_callback(self, option, opt, value, parser):
 
213
        setattr(parser.values, self.name, self.type(value))
 
214
        if self.custom_callback is not None:
 
215
            self.custom_callback(option, opt, value, parser)
 
216
 
 
217
    def iter_switches(self):
 
218
        """Iterate through the list of switches provided by the option
 
219
        
 
220
        :return: an iterator of (name, short_name, argname, help)
 
221
        """
 
222
        argname =  self.argname
 
223
        if argname is not None:
 
224
            argname = argname.upper()
 
225
        yield self.name, self.short_name(), argname, self.help
 
226
 
 
227
    def is_hidden(self, name):
 
228
        return False
 
229
 
 
230
 
 
231
class ListOption(Option):
 
232
    """Option used to provide a list of values.
 
233
 
 
234
    On the command line, arguments are specified by a repeated use of the
 
235
    option. '-' is a special argument that resets the list. For example,
 
236
      --foo=a --foo=b
 
237
    sets the value of the 'foo' option to ['a', 'b'], and
 
238
      --foo=a --foo=b --foo=- --foo=c
 
239
    sets the value of the 'foo' option to ['c'].
 
240
    """
 
241
 
 
242
    def add_option(self, parser, short_name):
 
243
        """Add this option to an Optparse parser."""
 
244
        option_strings = ['--%s' % self.name]
 
245
        if short_name is not None:
 
246
            option_strings.append('-%s' % short_name)
 
247
        parser.add_option(action='callback',
 
248
                          callback=self._optparse_callback,
 
249
                          type='string', metavar=self.argname.upper(),
 
250
                          help=self.help, default=[],
 
251
                          *option_strings)
 
252
 
 
253
    def _optparse_callback(self, option, opt, value, parser):
 
254
        values = getattr(parser.values, self.name)
 
255
        if value == '-':
 
256
            del values[:]
 
257
        else:
 
258
            values.append(self.type(value))
 
259
        if self.custom_callback is not None:
 
260
            self.custom_callback(option, opt, value, parser)
 
261
 
 
262
 
 
263
class RegistryOption(Option):
 
264
    """Option based on a registry
 
265
 
 
266
    The values for the options correspond to entries in the registry.  Input
 
267
    must be a registry key.  After validation, it is converted into an object
 
268
    using Registry.get or a caller-provided converter.
 
269
    """
 
270
 
 
271
    def validate_value(self, value):
 
272
        """Validate a value name"""
 
273
        if value not in self.registry:
 
274
            raise errors.BadOptionValue(self.name, value)
 
275
 
 
276
    def convert(self, value):
 
277
        """Convert a value name into an output type"""
 
278
        self.validate_value(value)
 
279
        if self.converter is None:
 
280
            return self.registry.get(value)
 
281
        else:
 
282
            return self.converter(value)
 
283
 
 
284
    def __init__(self, name, help, registry, converter=None,
 
285
        value_switches=False, title=None, enum_switch=True):
 
286
        """
 
287
        Constructor.
 
288
 
 
289
        :param name: The option name.
 
290
        :param help: Help for the option.
 
291
        :param registry: A Registry containing the values
 
292
        :param converter: Callable to invoke with the value name to produce
 
293
            the value.  If not supplied, self.registry.get is used.
 
294
        :param value_switches: If true, each possible value is assigned its
 
295
            own switch.  For example, instead of '--format knit',
 
296
            '--knit' can be used interchangeably.
 
297
        :param enum_switch: If true, a switch is provided with the option name,
 
298
            which takes a value.
 
299
        """
 
300
        Option.__init__(self, name, help, type=self.convert)
 
301
        self.registry = registry
 
302
        self.name = name
 
303
        self.converter = converter
 
304
        self.value_switches = value_switches
 
305
        self.enum_switch = enum_switch
 
306
        self.title = title
 
307
        if self.title is None:
 
308
            self.title = name
 
309
 
 
310
    @staticmethod
 
311
    def from_kwargs(name_, help=None, title=None, value_switches=False,
 
312
                    enum_switch=True, **kwargs):
 
313
        """Convenience method to generate string-map registry options
 
314
 
 
315
        name, help, value_switches and enum_switch are passed to the
 
316
        RegistryOption constructor.  Any other keyword arguments are treated
 
317
        as values for the option, and they value is treated as the help.
 
318
        """
 
319
        reg = registry.Registry()
 
320
        for name, switch_help in kwargs.iteritems():
 
321
            name = name.replace('_', '-')
 
322
            reg.register(name, name, help=switch_help)
 
323
        return RegistryOption(name_, help, reg, title=title,
 
324
            value_switches=value_switches, enum_switch=enum_switch)
 
325
 
 
326
    def add_option(self, parser, short_name):
 
327
        """Add this option to an Optparse parser"""
 
328
        if self.value_switches:
 
329
            parser = parser.add_option_group(self.title)
 
330
        if self.enum_switch:
 
331
            Option.add_option(self, parser, short_name)
 
332
        if self.value_switches:
 
333
            for key in self.registry.keys():
 
334
                option_strings = ['--%s' % key]
 
335
                if self.is_hidden(key):
 
336
                    help = optparse.SUPPRESS_HELP
 
337
                else:
 
338
                    help = self.registry.get_help(key)
 
339
                parser.add_option(action='callback',
 
340
                              callback=self._optparse_value_callback(key),
 
341
                                  help=help,
 
342
                                  *option_strings)
 
343
 
 
344
    def _optparse_value_callback(self, cb_value):
 
345
        def cb(option, opt, value, parser):
 
346
            setattr(parser.values, self.name, self.type(cb_value))
 
347
            if self.custom_callback is not None:
 
348
                self.custom_callback(option, opt, value, parser)
 
349
        return cb
 
350
 
 
351
    def iter_switches(self):
 
352
        """Iterate through the list of switches provided by the option
 
353
 
 
354
        :return: an iterator of (name, short_name, argname, help)
 
355
        """
 
356
        for value in Option.iter_switches(self):
 
357
            yield value
 
358
        if self.value_switches:
 
359
            for key in sorted(self.registry.keys()):
 
360
                yield key, None, None, self.registry.get_help(key)
 
361
 
 
362
    def is_hidden(self, name):
 
363
        if name == self.name:
 
364
            return Option.is_hidden(self, name)
 
365
        return getattr(self.registry.get_info(name), 'hidden', False)
 
366
 
 
367
 
 
368
class OptionParser(optparse.OptionParser):
 
369
    """OptionParser that raises exceptions instead of exiting"""
 
370
 
 
371
    DEFAULT_VALUE = object()
 
372
 
 
373
    def error(self, message):
 
374
        raise errors.BzrCommandError(message)
 
375
 
 
376
 
 
377
def get_optparser(options):
 
378
    """Generate an optparse parser for bzrlib-style options"""
 
379
 
 
380
    parser = OptionParser()
 
381
    parser.remove_option('--help')
 
382
    for option in options.itervalues():
 
383
        option.add_option(parser, option.short_name())
 
384
    return parser
 
385
 
 
386
 
 
387
def custom_help(name, help):
 
388
    """Clone a common option overriding the help."""
 
389
    import copy
 
390
    o = copy.copy(Option.OPTIONS[name])
 
391
    o.help = help
 
392
    return o
 
393
 
 
394
 
 
395
def _standard_option(name, **kwargs):
 
396
    """Register a standard option."""
 
397
    # All standard options are implicitly 'global' ones
 
398
    Option.STD_OPTIONS[name] = Option(name, **kwargs)
 
399
    Option.OPTIONS[name] = Option.STD_OPTIONS[name]
 
400
 
 
401
 
 
402
def _global_option(name, **kwargs):
 
403
    """Register a global option."""
 
404
    Option.OPTIONS[name] = Option(name, **kwargs)
 
405
 
 
406
 
 
407
def _global_registry_option(name, help, registry, **kwargs):
 
408
    Option.OPTIONS[name] = RegistryOption(name, help, registry, **kwargs)
 
409
 
 
410
 
 
411
class MergeTypeRegistry(registry.Registry):
 
412
 
 
413
    pass
 
414
 
 
415
 
 
416
# This is the verbosity level detected during command line parsing.
 
417
# Note that the final value is dependent on the order in which the
 
418
# various flags (verbose, quiet, no-verbose, no-quiet) are given.
 
419
# The final value will be one of the following:
 
420
#
 
421
# * -ve for quiet
 
422
# * 0 for normal
 
423
# * +ve for verbose
 
424
_verbosity_level = 0
 
425
 
 
426
 
 
427
def _verbosity_level_callback(option, opt_str, value, parser):
 
428
    global _verbosity_level
 
429
    if not value:
 
430
        # Either --no-verbose or --no-quiet was specified
 
431
        _verbosity_level = 0
 
432
    elif opt_str == "verbose":
 
433
        if _verbosity_level > 0:
 
434
            _verbosity_level += 1
 
435
        else:
 
436
            _verbosity_level = 1
 
437
    else:
 
438
        if _verbosity_level < 0:
 
439
            _verbosity_level -= 1
 
440
        else:
 
441
            _verbosity_level = -1
 
442
 
 
443
 
 
444
_merge_type_registry = MergeTypeRegistry()
 
445
_merge_type_registry.register_lazy('merge3', 'bzrlib.merge', 'Merge3Merger',
 
446
                                   "Native diff3-style merge")
 
447
_merge_type_registry.register_lazy('diff3', 'bzrlib.merge', 'Diff3Merger',
 
448
                                   "Merge using external diff3")
 
449
_merge_type_registry.register_lazy('weave', 'bzrlib.merge', 'WeaveMerger',
 
450
                                   "Weave-based merge")
 
451
 
 
452
# Declare the standard options
 
453
_standard_option('help', short_name='h',
 
454
                 help='Show help message.')
 
455
_standard_option('verbose', short_name='v',
 
456
                 help='Display more information.',
 
457
                 custom_callback=_verbosity_level_callback)
 
458
_standard_option('quiet', short_name='q',
 
459
                 help="Only display errors and warnings.",
 
460
                 custom_callback=_verbosity_level_callback)
 
461
 
 
462
# Declare commonly used options
 
463
_global_option('all')
 
464
_global_option('overwrite', help='Ignore differences between branches and '
 
465
               'overwrite unconditionally.')
 
466
_global_option('basis', type=str)
 
467
_global_option('bound')
 
468
_global_option('diff-options', type=str)
 
469
_global_option('file', type=unicode, short_name='F')
 
470
_global_option('force')
 
471
_global_option('format', type=unicode)
 
472
_global_option('forward')
 
473
_global_option('message', type=unicode,
 
474
               short_name='m',
 
475
               help='Message string.')
 
476
_global_option('no-recurse')
 
477
_global_option('profile',
 
478
               help='Show performance profiling information.')
 
479
_global_option('revision',
 
480
               type=_parse_revision_str,
 
481
               short_name='r',
 
482
               help='See \'help revisionspec\' for details.')
 
483
_global_option('show-ids',
 
484
               help='Show internal object ids.')
 
485
_global_option('timezone', 
 
486
               type=str,
 
487
               help='display timezone as local, original, or utc')
 
488
_global_option('unbound')
 
489
_global_option('version')
 
490
_global_option('email')
 
491
_global_option('update')
 
492
_global_registry_option('log-format', "Use specified log format.",
 
493
                        log.log_formatter_registry, value_switches=True,
 
494
                        title='Log format')
 
495
_global_option('long', help='Use detailed log format. Same as --log-format long',
 
496
               short_name='l')
 
497
_global_option('short', help='Use moderately short log format. Same as --log-format short')
 
498
_global_option('line', help='Use log format with one line per revision. Same as --log-format line')
 
499
_global_option('root', type=str)
 
500
_global_option('no-backup')
 
501
_global_registry_option('merge-type', 'Select a particular merge algorithm.',
 
502
                        _merge_type_registry, value_switches=True,
 
503
                        title='Merge algorithm')
 
504
_global_option('pattern', type=str)
 
505
_global_option('remember', help='Remember the specified location as a'
 
506
               ' default.')
 
507
_global_option('reprocess', help='Reprocess to reduce spurious conflicts.')
 
508
_global_option('kind', type=str)
 
509
_global_option('dry-run',
 
510
               help="Show what would be done, but don't actually do anything.")
 
511
_global_option('name-from-revision', help='The path name in the old tree.')