/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-09-03 02:37:35 UTC
  • mto: This revision was merged to the branch mainline in revision 2778.
  • Revision ID: ian.clatworthy@internode.on.net-20070903023735-sdgp3h0hooptbhlo
(Ian Clatworthy)(trivial) Fix NEWS indenting and ReST nits in developers/update.txt

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_change_str(revstr):
 
104
    """Parse the revision string and return a tuple with left-most
 
105
    parent of the revision.
 
106
 
 
107
    >>> _parse_change_str('123')
 
108
    (<RevisionSpec_before before:123>, <RevisionSpec_revno 123>)
 
109
    >>> _parse_change_str('123..124')
 
110
    Traceback (most recent call last):
 
111
      ...
 
112
    RangeInChangeOption: Option --change does not accept revision ranges
 
113
    """
 
114
    revs = _parse_revision_str(revstr)
 
115
    if len(revs) > 1:
 
116
        raise errors.RangeInChangeOption()
 
117
    return (revisionspec.RevisionSpec.from_string('before:' + revstr),
 
118
            revs[0])
 
119
 
 
120
 
 
121
def _parse_merge_type(typestring):
 
122
    return get_merge_type(typestring)
 
123
 
 
124
def get_merge_type(typestring):
 
125
    """Attempt to find the merge class/factory associated with a string."""
 
126
    from merge import merge_types
 
127
    try:
 
128
        return merge_types[typestring][0]
 
129
    except KeyError:
 
130
        templ = '%s%%7s: %%s' % (' '*12)
 
131
        lines = [templ % (f[0], f[1][1]) for f in merge_types.iteritems()]
 
132
        type_list = '\n'.join(lines)
 
133
        msg = "No known merge type %s. Supported types are:\n%s" %\
 
134
            (typestring, type_list)
 
135
        raise errors.BzrCommandError(msg)
 
136
 
 
137
 
 
138
class Option(object):
 
139
    """Description of a command line option
 
140
    
 
141
    :ivar _short_name: If this option has a single-letter name, this is it.
 
142
    Otherwise None.
 
143
    """
 
144
 
 
145
    # TODO: Some way to show in help a description of the option argument
 
146
 
 
147
    OPTIONS = {}
 
148
 
 
149
    def __init__(self, name, help='', type=None, argname=None,
 
150
                 short_name=None, param_name=None):
 
151
        """Make a new command option.
 
152
 
 
153
        name -- regular name of the command, used in the double-dash
 
154
            form and also as the parameter to the command's run() 
 
155
            method (unless param_name is specified).
 
156
 
 
157
        help -- help message displayed in command help
 
158
 
 
159
        type -- function called to parse the option argument, or 
 
160
            None (default) if this option doesn't take an argument.
 
161
 
 
162
        argname -- name of option argument, if any
 
163
 
 
164
        param_name -- name of the parameter which will be passed to
 
165
            the command's run() method.
 
166
        """
 
167
        self.name = name
 
168
        self.help = help
 
169
        self.type = type
 
170
        self._short_name = short_name
 
171
        if type is None:
 
172
            assert argname is None
 
173
        elif argname is None:
 
174
            argname = 'ARG'
 
175
        self.argname = argname
 
176
        if param_name is None:
 
177
            self._param_name = self.name
 
178
        else:
 
179
            self._param_name = param_name
 
180
 
 
181
    def short_name(self):
 
182
        if self._short_name:
 
183
            return self._short_name
 
184
 
 
185
    def set_short_name(self, short_name):
 
186
        self._short_name = short_name
 
187
 
 
188
    def get_negation_name(self):
 
189
        if self.name.startswith('no-'):
 
190
            return self.name[3:]
 
191
        else:
 
192
            return 'no-' + self.name
 
193
 
 
194
    def add_option(self, parser, short_name):
 
195
        """Add this option to an Optparse parser"""
 
196
        option_strings = ['--%s' % self.name]
 
197
        if short_name is not None:
 
198
            option_strings.append('-%s' % short_name)
 
199
        optargfn = self.type
 
200
        if optargfn is None:
 
201
            parser.add_option(action='store_true', dest=self.name, 
 
202
                              help=self.help,
 
203
                              default=OptionParser.DEFAULT_VALUE,
 
204
                              *option_strings)
 
205
            negation_strings = ['--%s' % self.get_negation_name()]
 
206
            parser.add_option(action='store_false', dest=self.name, 
 
207
                              help=optparse.SUPPRESS_HELP, *negation_strings)
 
208
        else:
 
209
            parser.add_option(action='callback', 
 
210
                              callback=self._optparse_callback, 
 
211
                              type='string', metavar=self.argname.upper(),
 
212
                              help=self.help,
 
213
                              default=OptionParser.DEFAULT_VALUE, 
 
214
                              *option_strings)
 
215
 
 
216
    def _optparse_callback(self, option, opt, value, parser):
 
217
        setattr(parser.values, self._param_name, self.type(value))
 
218
 
 
219
    def iter_switches(self):
 
220
        """Iterate through the list of switches provided by the option
 
221
        
 
222
        :return: an iterator of (name, short_name, argname, help)
 
223
        """
 
224
        argname =  self.argname
 
225
        if argname is not None:
 
226
            argname = argname.upper()
 
227
        yield self.name, self.short_name(), argname, self.help
 
228
 
 
229
    def is_hidden(self, name):
 
230
        return False
 
231
 
 
232
 
 
233
class ListOption(Option):
 
234
    """Option used to provide a list of values.
 
235
 
 
236
    On the command line, arguments are specified by a repeated use of the
 
237
    option. '-' is a special argument that resets the list. For example,
 
238
      --foo=a --foo=b
 
239
    sets the value of the 'foo' option to ['a', 'b'], and
 
240
      --foo=a --foo=b --foo=- --foo=c
 
241
    sets the value of the 'foo' option to ['c'].
 
242
    """
 
243
 
 
244
    def add_option(self, parser, short_name):
 
245
        """Add this option to an Optparse parser."""
 
246
        option_strings = ['--%s' % self.name]
 
247
        if short_name is not None:
 
248
            option_strings.append('-%s' % short_name)
 
249
        parser.add_option(action='callback',
 
250
                          callback=self._optparse_callback,
 
251
                          type='string', metavar=self.argname.upper(),
 
252
                          help=self.help, default=[],
 
253
                          *option_strings)
 
254
 
 
255
    def _optparse_callback(self, option, opt, value, parser):
 
256
        values = getattr(parser.values, self.name)
 
257
        if value == '-':
 
258
            del values[:]
 
259
        else:
 
260
            values.append(self.type(value))
 
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
        return cb
 
348
 
 
349
    def iter_switches(self):
 
350
        """Iterate through the list of switches provided by the option
 
351
 
 
352
        :return: an iterator of (name, short_name, argname, help)
 
353
        """
 
354
        for value in Option.iter_switches(self):
 
355
            yield value
 
356
        if self.value_switches:
 
357
            for key in sorted(self.registry.keys()):
 
358
                yield key, None, None, self.registry.get_help(key)
 
359
 
 
360
    def is_hidden(self, name):
 
361
        if name == self.name:
 
362
            return Option.is_hidden(self, name)
 
363
        return getattr(self.registry.get_info(name), 'hidden', False)
 
364
 
 
365
 
 
366
class OptionParser(optparse.OptionParser):
 
367
    """OptionParser that raises exceptions instead of exiting"""
 
368
 
 
369
    DEFAULT_VALUE = object()
 
370
 
 
371
    def error(self, message):
 
372
        raise errors.BzrCommandError(message)
 
373
 
 
374
 
 
375
def get_optparser(options):
 
376
    """Generate an optparse parser for bzrlib-style options"""
 
377
 
 
378
    parser = OptionParser()
 
379
    parser.remove_option('--help')
 
380
    for option in options.itervalues():
 
381
        option.add_option(parser, option.short_name())
 
382
    return parser
 
383
 
 
384
 
 
385
def _global_option(name, **kwargs):
 
386
    """Register o as a global option."""
 
387
    Option.OPTIONS[name] = Option(name, **kwargs)
 
388
 
 
389
 
 
390
def _global_registry_option(name, help, registry, **kwargs):
 
391
    Option.OPTIONS[name] = RegistryOption(name, help, registry, **kwargs)
 
392
 
 
393
 
 
394
class MergeTypeRegistry(registry.Registry):
 
395
 
 
396
    pass
 
397
 
 
398
 
 
399
_merge_type_registry = MergeTypeRegistry()
 
400
_merge_type_registry.register_lazy('merge3', 'bzrlib.merge', 'Merge3Merger',
 
401
                                   "Native diff3-style merge")
 
402
_merge_type_registry.register_lazy('diff3', 'bzrlib.merge', 'Diff3Merger',
 
403
                                   "Merge using external diff3")
 
404
_merge_type_registry.register_lazy('weave', 'bzrlib.merge', 'WeaveMerger',
 
405
                                   "Weave-based merge")
 
406
 
 
407
_global_option('all')
 
408
_global_option('overwrite', help='Ignore differences between branches and '
 
409
               'overwrite unconditionally.')
 
410
_global_option('basis', type=str)
 
411
_global_option('bound')
 
412
_global_option('diff-options', type=str)
 
413
_global_option('file', type=unicode, short_name='F')
 
414
_global_option('force')
 
415
_global_option('format', type=unicode)
 
416
_global_option('forward')
 
417
_global_option('message', type=unicode,
 
418
               short_name='m',
 
419
               help='Message string.')
 
420
_global_option('no-recurse')
 
421
_global_option('profile',
 
422
               help='Show performance profiling information.')
 
423
_global_option('revision',
 
424
               type=_parse_revision_str,
 
425
               short_name='r',
 
426
               help='See "help revisionspec" for details.')
 
427
_global_option('change',
 
428
               type=_parse_change_str,
 
429
               short_name='c',
 
430
               param_name='revision',
 
431
               help='Select changes introduced by the specified revision. See also "help revisionspec".')
 
432
_global_option('show-ids',
 
433
               help='Show internal object ids.')
 
434
_global_option('timezone', 
 
435
               type=str,
 
436
               help='display timezone as local, original, or utc')
 
437
_global_option('unbound')
 
438
_global_option('verbose',
 
439
               help='Display more information.',
 
440
               short_name='v')
 
441
_global_option('version')
 
442
_global_option('email')
 
443
_global_option('update')
 
444
_global_registry_option('log-format', "Use specified log format.",
 
445
                        log.log_formatter_registry, value_switches=True,
 
446
                        title='Log format')
 
447
_global_option('long', help='Use detailed log format. Same as --log-format long',
 
448
               short_name='l')
 
449
_global_option('short', help='Use moderately short log format. Same as --log-format short')
 
450
_global_option('line', help='Use log format with one line per revision. Same as --log-format line')
 
451
_global_option('root', type=str)
 
452
_global_option('no-backup')
 
453
_global_registry_option('merge-type', 'Select a particular merge algorithm.',
 
454
                        _merge_type_registry, value_switches=True,
 
455
                        title='Merge algorithm')
 
456
_global_option('pattern', type=str)
 
457
_global_option('quiet', short_name='q')
 
458
_global_option('remember', help='Remember the specified location as a'
 
459
               ' default.')
 
460
_global_option('reprocess', help='Reprocess to reduce spurious conflicts.')
 
461
_global_option('kind', type=str)
 
462
_global_option('dry-run',
 
463
               help="Show what would be done, but don't actually do anything.")
 
464
_global_option('name-from-revision', help='The path name in the old tree.')
 
465
 
 
466
_help_option = Option('help',
 
467
                      help='Show help message.',
 
468
                      short_name='h')