~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/option.py

  • Committer: Dmitry Vasiliev
  • Date: 2007-03-07 13:47:47 UTC
  • mto: (2327.1.1 jam-integration)
  • mto: This revision was merged to the branch mainline in revision 2328.
  • Revision ID: dima@hlabs.spb.ru-20070307134747-clcmwlfck4g9yqh3
Updated note about registry.Registry

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2004, 2005, 2006, 2007 Canonical Ltd
 
1
# Copyright (C) 2004, 2005, 2006 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
# TODO: For things like --diff-prefix, we want a way to customize the display
18
18
# of the option argument.
19
19
 
20
 
import optparse
21
20
import re
22
21
 
23
22
from bzrlib.lazy_import import lazy_import
24
23
lazy_import(globals(), """
 
24
import optparse
 
25
 
25
26
from bzrlib import (
26
27
    errors,
 
28
    log,
 
29
    registry,
27
30
    revisionspec,
 
31
    symbol_versioning,
28
32
    )
29
33
""")
30
 
 
31
 
from bzrlib import (
32
 
    registry as _mod_registry,
33
 
    )
 
34
from bzrlib.trace import warning
34
35
 
35
36
 
36
37
def _parse_revision_str(revstr):
85
86
    [<RevisionSpec_branch branch:../../branch2>]
86
87
    >>> _parse_revision_str('branch:../../branch2..23')
87
88
    [<RevisionSpec_branch branch:../../branch2>, <RevisionSpec_revno 23>]
88
 
    >>> _parse_revision_str('branch:..\\\\branch2')
89
 
    [<RevisionSpec_branch branch:..\\branch2>]
90
 
    >>> _parse_revision_str('branch:..\\\\..\\\\branch2..23')
91
 
    [<RevisionSpec_branch branch:..\\..\\branch2>, <RevisionSpec_revno 23>]
92
89
    """
93
90
    # TODO: Maybe move this into revisionspec.py
94
91
    revs = []
95
 
    # split on .. that is not followed by a / or \
96
 
    sep = re.compile(r'\.\.(?![\\/])')
 
92
    # split on the first .. that is not followed by a / ?
 
93
    sep = re.compile("\\.\\.(?!/)")
97
94
    for x in sep.split(revstr):
98
95
        revs.append(revisionspec.RevisionSpec.from_string(x or None))
99
96
    return revs
100
97
 
101
98
 
102
 
def _parse_change_str(revstr):
103
 
    """Parse the revision string and return a tuple with left-most
104
 
    parent of the revision.
105
 
 
106
 
    >>> _parse_change_str('123')
107
 
    (<RevisionSpec_before before:123>, <RevisionSpec_revno 123>)
108
 
    >>> _parse_change_str('123..124')
109
 
    Traceback (most recent call last):
110
 
      ...
111
 
    RangeInChangeOption: Option --change does not accept revision ranges
112
 
    """
113
 
    revs = _parse_revision_str(revstr)
114
 
    if len(revs) > 1:
115
 
        raise errors.RangeInChangeOption()
116
 
    return (revisionspec.RevisionSpec.from_string('before:' + revstr),
117
 
            revs[0])
118
 
 
119
 
 
120
99
def _parse_merge_type(typestring):
121
100
    return get_merge_type(typestring)
122
101
 
136
115
 
137
116
class Option(object):
138
117
    """Description of a command line option
139
 
 
 
118
    
140
119
    :ivar _short_name: If this option has a single-letter name, this is it.
141
120
    Otherwise None.
142
121
    """
143
122
 
144
 
    # The dictionary of standard options. These are always legal.
145
 
    STD_OPTIONS = {}
 
123
    # TODO: Some way to show in help a description of the option argument
146
124
 
147
 
    # The dictionary of commonly used options. these are only legal
148
 
    # if a command explicitly references them by name in the list
149
 
    # of supported options.
150
125
    OPTIONS = {}
151
126
 
152
127
    def __init__(self, name, help='', type=None, argname=None,
153
 
                 short_name=None, param_name=None, custom_callback=None):
 
128
                 short_name=None):
154
129
        """Make a new command option.
155
130
 
156
 
        :param name: regular name of the command, used in the double-dash
157
 
            form and also as the parameter to the command's run()
158
 
            method (unless param_name is specified).
159
 
 
160
 
        :param help: help message displayed in command help
161
 
 
162
 
        :param type: function called to parse the option argument, or
 
131
        name -- regular name of the command, used in the double-dash
 
132
            form and also as the parameter to the command's run() 
 
133
            method.
 
134
 
 
135
        help -- help message displayed in command help
 
136
 
 
137
        type -- function called to parse the option argument, or 
163
138
            None (default) if this option doesn't take an argument.
164
139
 
165
 
        :param argname: name of option argument, if any
166
 
 
167
 
        :param short_name: short option code for use with a single -, e.g.
168
 
            short_name="v" to enable parsing of -v.
169
 
 
170
 
        :param param_name: name of the parameter which will be passed to
171
 
            the command's run() method.
172
 
 
173
 
        :param custom_callback: a callback routine to be called after normal
174
 
            processing. The signature of the callback routine is
175
 
            (option, name, new_value, parser).
 
140
        argname -- name of option argument, if any
176
141
        """
177
142
        self.name = name
178
143
        self.help = help
179
144
        self.type = type
180
145
        self._short_name = short_name
181
146
        if type is None:
182
 
            if argname:
183
 
                raise ValueError('argname not valid for booleans')
 
147
            assert argname is None
184
148
        elif argname is None:
185
149
            argname = 'ARG'
186
150
        self.argname = argname
187
 
        if param_name is None:
188
 
            self._param_name = self.name.replace('-', '_')
189
 
        else:
190
 
            self._param_name = param_name
191
 
        self.custom_callback = custom_callback
192
151
 
193
152
    def short_name(self):
194
153
        if self._short_name:
195
154
            return self._short_name
 
155
        else:
 
156
            # remove this when SHORT_OPTIONS is removed
 
157
            # XXX: This is accessing a DeprecatedDict, so we call the super 
 
158
            # method to avoid warnings
 
159
            for (k, v) in dict.iteritems(Option.SHORT_OPTIONS):
 
160
                if v == self:
 
161
                    return k
196
162
 
197
163
    def set_short_name(self, short_name):
198
164
        self._short_name = short_name
210
176
            option_strings.append('-%s' % short_name)
211
177
        optargfn = self.type
212
178
        if optargfn is None:
213
 
            parser.add_option(action='callback',
214
 
                              callback=self._optparse_bool_callback,
215
 
                              callback_args=(True,),
 
179
            parser.add_option(action='store_true', dest=self.name, 
216
180
                              help=self.help,
 
181
                              default=OptionParser.DEFAULT_VALUE,
217
182
                              *option_strings)
218
183
            negation_strings = ['--%s' % self.get_negation_name()]
219
 
            parser.add_option(action='callback',
220
 
                              callback=self._optparse_bool_callback,
221
 
                              callback_args=(False,),
 
184
            parser.add_option(action='store_false', dest=self.name, 
222
185
                              help=optparse.SUPPRESS_HELP, *negation_strings)
223
186
        else:
224
 
            parser.add_option(action='callback',
225
 
                              callback=self._optparse_callback,
 
187
            parser.add_option(action='callback', 
 
188
                              callback=self._optparse_callback, 
226
189
                              type='string', metavar=self.argname.upper(),
227
190
                              help=self.help,
228
 
                              default=OptionParser.DEFAULT_VALUE,
 
191
                              default=OptionParser.DEFAULT_VALUE, 
229
192
                              *option_strings)
230
193
 
231
 
    def _optparse_bool_callback(self, option, opt_str, value, parser, bool_v):
232
 
        setattr(parser.values, self._param_name, bool_v)
233
 
        if self.custom_callback is not None:
234
 
            self.custom_callback(option, self._param_name, bool_v, parser)
235
 
 
236
194
    def _optparse_callback(self, option, opt, value, parser):
237
 
        v = self.type(value)
238
 
        setattr(parser.values, self._param_name, v)
239
 
        if self.custom_callback is not None:
240
 
            self.custom_callback(option, self.name, v, parser)
 
195
        setattr(parser.values, self.name, self.type(value))
241
196
 
242
197
    def iter_switches(self):
243
198
        """Iterate through the list of switches provided by the option
244
 
 
 
199
        
245
200
        :return: an iterator of (name, short_name, argname, help)
246
201
        """
247
202
        argname =  self.argname
249
204
            argname = argname.upper()
250
205
        yield self.name, self.short_name(), argname, self.help
251
206
 
252
 
    def is_hidden(self, name):
253
 
        return False
254
 
 
255
 
 
256
 
class ListOption(Option):
257
 
    """Option used to provide a list of values.
258
 
 
259
 
    On the command line, arguments are specified by a repeated use of the
260
 
    option. '-' is a special argument that resets the list. For example,
261
 
      --foo=a --foo=b
262
 
    sets the value of the 'foo' option to ['a', 'b'], and
263
 
      --foo=a --foo=b --foo=- --foo=c
264
 
    sets the value of the 'foo' option to ['c'].
265
 
    """
266
 
 
267
 
    def add_option(self, parser, short_name):
268
 
        """Add this option to an Optparse parser."""
269
 
        option_strings = ['--%s' % self.name]
270
 
        if short_name is not None:
271
 
            option_strings.append('-%s' % short_name)
272
 
        parser.add_option(action='callback',
273
 
                          callback=self._optparse_callback,
274
 
                          type='string', metavar=self.argname.upper(),
275
 
                          help=self.help, default=[],
276
 
                          *option_strings)
277
 
 
278
 
    def _optparse_callback(self, option, opt, value, parser):
279
 
        values = getattr(parser.values, self._param_name)
280
 
        if value == '-':
281
 
            del values[:]
282
 
        else:
283
 
            values.append(self.type(value))
284
 
        if self.custom_callback is not None:
285
 
            self.custom_callback(option, self._param_name, values, parser)
286
 
 
287
207
 
288
208
class RegistryOption(Option):
289
209
    """Option based on a registry
306
226
        else:
307
227
            return self.converter(value)
308
228
 
309
 
    def __init__(self, name, help, registry=None, converter=None,
310
 
        value_switches=False, title=None, enum_switch=True,
311
 
        lazy_registry=None):
 
229
    def __init__(self, name, help, registry, converter=None,
 
230
        value_switches=False, title=None):
312
231
        """
313
232
        Constructor.
314
233
 
320
239
        :param value_switches: If true, each possible value is assigned its
321
240
            own switch.  For example, instead of '--format knit',
322
241
            '--knit' can be used interchangeably.
323
 
        :param enum_switch: If true, a switch is provided with the option name,
324
 
            which takes a value.
325
 
        :param lazy_registry: A tuple of (module name, attribute name) for a
326
 
            registry to be lazily loaded.
327
242
        """
328
243
        Option.__init__(self, name, help, type=self.convert)
329
 
        self._registry = registry
330
 
        if registry is None:
331
 
            if lazy_registry is None:
332
 
                raise AssertionError(
333
 
                    'One of registry or lazy_registry must be given.')
334
 
            self._lazy_registry = _mod_registry._LazyObjectGetter(
335
 
                *lazy_registry)
336
 
        if registry is not None and lazy_registry is not None:
337
 
            raise AssertionError(
338
 
                'registry and lazy_registry are mutually exclusive')
 
244
        self.registry = registry
339
245
        self.name = name
340
246
        self.converter = converter
341
247
        self.value_switches = value_switches
342
 
        self.enum_switch = enum_switch
343
248
        self.title = title
344
249
        if self.title is None:
345
250
            self.title = name
346
251
 
347
 
    @property
348
 
    def registry(self):
349
 
        if self._registry is None:
350
 
            self._registry = self._lazy_registry.get_obj()
351
 
        return self._registry
352
 
 
353
 
    @staticmethod
354
 
    def from_kwargs(name_, help=None, title=None, value_switches=False,
355
 
                    enum_switch=True, **kwargs):
356
 
        """Convenience method to generate string-map registry options
357
 
 
358
 
        name, help, value_switches and enum_switch are passed to the
359
 
        RegistryOption constructor.  Any other keyword arguments are treated
360
 
        as values for the option, and they value is treated as the help.
361
 
        """
362
 
        reg = _mod_registry.Registry()
363
 
        for name, switch_help in kwargs.iteritems():
364
 
            name = name.replace('_', '-')
365
 
            reg.register(name, name, help=switch_help)
366
 
            if not value_switches:
367
 
                help = help + '  "' + name + '": ' + switch_help
368
 
                if not help.endswith("."):
369
 
                    help = help + "."
370
 
        return RegistryOption(name_, help, reg, title=title,
371
 
            value_switches=value_switches, enum_switch=enum_switch)
372
 
 
373
252
    def add_option(self, parser, short_name):
374
253
        """Add this option to an Optparse parser"""
375
254
        if self.value_switches:
376
255
            parser = parser.add_option_group(self.title)
377
 
        if self.enum_switch:
378
 
            Option.add_option(self, parser, short_name)
 
256
        Option.add_option(self, parser, short_name)
379
257
        if self.value_switches:
380
258
            for key in self.registry.keys():
381
259
                option_strings = ['--%s' % key]
382
 
                if self.is_hidden(key):
383
 
                    help = optparse.SUPPRESS_HELP
384
 
                else:
385
 
                    help = self.registry.get_help(key)
386
260
                parser.add_option(action='callback',
387
261
                              callback=self._optparse_value_callback(key),
388
 
                                  help=help,
 
262
                                  help=self.registry.get_help(key),
389
263
                                  *option_strings)
390
264
 
391
265
    def _optparse_value_callback(self, cb_value):
392
266
        def cb(option, opt, value, parser):
393
 
            v = self.type(cb_value)
394
 
            setattr(parser.values, self._param_name, v)
395
 
            if self.custom_callback is not None:
396
 
                self.custom_callback(option, self._param_name, v, parser)
 
267
            setattr(parser.values, self.name, self.type(cb_value))
397
268
        return cb
398
269
 
399
270
    def iter_switches(self):
407
278
            for key in sorted(self.registry.keys()):
408
279
                yield key, None, None, self.registry.get_help(key)
409
280
 
410
 
    def is_hidden(self, name):
411
 
        if name == self.name:
412
 
            return Option.is_hidden(self, name)
413
 
        return getattr(self.registry.get_info(name), 'hidden', False)
414
 
 
415
281
 
416
282
class OptionParser(optparse.OptionParser):
417
283
    """OptionParser that raises exceptions instead of exiting"""
432
298
    return parser
433
299
 
434
300
 
435
 
def custom_help(name, help):
436
 
    """Clone a common option overriding the help."""
437
 
    import copy
438
 
    o = copy.copy(Option.OPTIONS[name])
439
 
    o.help = help
440
 
    return o
441
 
 
442
 
 
443
 
def _standard_option(name, **kwargs):
444
 
    """Register a standard option."""
445
 
    # All standard options are implicitly 'global' ones
446
 
    Option.STD_OPTIONS[name] = Option(name, **kwargs)
447
 
    Option.OPTIONS[name] = Option.STD_OPTIONS[name]
448
 
 
449
 
 
450
301
def _global_option(name, **kwargs):
451
 
    """Register a global option."""
 
302
    """Register o as a global option."""
452
303
    Option.OPTIONS[name] = Option(name, **kwargs)
453
304
 
454
305
 
455
 
def _global_registry_option(name, help, registry=None, **kwargs):
 
306
def _global_registry_option(name, help, registry, **kwargs):
456
307
    Option.OPTIONS[name] = RegistryOption(name, help, registry, **kwargs)
457
308
 
458
309
 
459
 
# This is the verbosity level detected during command line parsing.
460
 
# Note that the final value is dependent on the order in which the
461
 
# various flags (verbose, quiet, no-verbose, no-quiet) are given.
462
 
# The final value will be one of the following:
463
 
#
464
 
# * -ve for quiet
465
 
# * 0 for normal
466
 
# * +ve for verbose
467
 
_verbosity_level = 0
468
 
 
469
 
 
470
 
def _verbosity_level_callback(option, opt_str, value, parser):
471
 
    global _verbosity_level
472
 
    if not value:
473
 
        # Either --no-verbose or --no-quiet was specified
474
 
        _verbosity_level = 0
475
 
    elif opt_str == "verbose":
476
 
        if _verbosity_level > 0:
477
 
            _verbosity_level += 1
478
 
        else:
479
 
            _verbosity_level = 1
480
 
    else:
481
 
        if _verbosity_level < 0:
482
 
            _verbosity_level -= 1
483
 
        else:
484
 
            _verbosity_level = -1
485
 
 
486
 
 
487
 
class MergeTypeRegistry(_mod_registry.Registry):
 
310
class MergeTypeRegistry(registry.Registry):
488
311
 
489
312
    pass
490
313
 
496
319
                                   "Merge using external diff3")
497
320
_merge_type_registry.register_lazy('weave', 'bzrlib.merge', 'WeaveMerger',
498
321
                                   "Weave-based merge")
499
 
_merge_type_registry.register_lazy('lca', 'bzrlib.merge', 'LCAMerger',
500
 
                                   "LCA-newness merge")
501
 
 
502
 
# Declare the standard options
503
 
_standard_option('help', short_name='h',
504
 
                 help='Show help message.')
505
 
_standard_option('usage',
506
 
                 help='Show usage message and options.')
507
 
_standard_option('verbose', short_name='v',
508
 
                 help='Display more information.',
509
 
                 custom_callback=_verbosity_level_callback)
510
 
_standard_option('quiet', short_name='q',
511
 
                 help="Only display errors and warnings.",
512
 
                 custom_callback=_verbosity_level_callback)
513
 
 
514
 
# Declare commonly used options
 
322
 
515
323
_global_option('all')
516
324
_global_option('overwrite', help='Ignore differences between branches and '
517
 
               'overwrite unconditionally.')
 
325
               'overwrite unconditionally')
518
326
_global_option('basis', type=str)
519
327
_global_option('bound')
520
328
_global_option('diff-options', type=str)
 
329
_global_option('help',
 
330
               help='show help message',
 
331
               short_name='h')
521
332
_global_option('file', type=unicode, short_name='F')
522
333
_global_option('force')
523
334
_global_option('format', type=unicode)
524
335
_global_option('forward')
525
336
_global_option('message', type=unicode,
526
 
               short_name='m',
527
 
               help='Message string.')
 
337
               short_name='m')
528
338
_global_option('no-recurse')
529
339
_global_option('profile',
530
 
               help='Show performance profiling information.')
 
340
               help='show performance profiling information')
531
341
_global_option('revision',
532
342
               type=_parse_revision_str,
533
343
               short_name='r',
534
 
               help='See "help revisionspec" for details.')
535
 
_global_option('change',
536
 
               type=_parse_change_str,
537
 
               short_name='c',
538
 
               param_name='revision',
539
 
               help='Select changes introduced by the specified revision. See also "help revisionspec".')
540
 
_global_option('show-ids',
541
 
               help='Show internal object ids.')
542
 
_global_option('timezone',
 
344
               help='See \'help revisionspec\' for details')
 
345
_global_option('show-ids', 
 
346
               help='show internal object ids')
 
347
_global_option('timezone', 
543
348
               type=str,
544
 
               help='Display timezone as local, original, or utc.')
 
349
               help='display timezone as local, original, or utc')
545
350
_global_option('unbound')
 
351
_global_option('verbose',
 
352
               help='display more information',
 
353
               short_name='v')
546
354
_global_option('version')
547
355
_global_option('email')
548
356
_global_option('update')
549
 
_global_registry_option('log-format', "Use specified log format.",
550
 
                        lazy_registry=('bzrlib.log', 'log_formatter_registry'),
551
 
                        value_switches=True, title='Log format')
 
357
_global_registry_option('log-format', "Use this log format",
 
358
                        log.log_formatter_registry, value_switches=True,
 
359
                        title='Log format')
552
360
_global_option('long', help='Use detailed log format. Same as --log-format long',
553
361
               short_name='l')
554
362
_global_option('short', help='Use moderately short log format. Same as --log-format short')
555
363
_global_option('line', help='Use log format with one line per revision. Same as --log-format line')
556
364
_global_option('root', type=str)
557
365
_global_option('no-backup')
558
 
_global_registry_option('merge-type', 'Select a particular merge algorithm.',
 
366
_global_registry_option('merge-type', 'Select a particular merge algorithm',
559
367
                        _merge_type_registry, value_switches=True,
560
368
                        title='Merge algorithm')
561
369
_global_option('pattern', type=str)
 
370
_global_option('quiet', short_name='q')
562
371
_global_option('remember', help='Remember the specified location as a'
563
372
               ' default.')
564
 
_global_option('reprocess', help='Reprocess to reduce spurious conflicts.')
 
373
_global_option('reprocess', help='Reprocess to reduce spurious conflicts')
565
374
_global_option('kind', type=str)
566
375
_global_option('dry-run',
567
 
               help="Show what would be done, but don't actually do anything.")
 
376
               help="show what would be done, but don't actually do anything")
568
377
_global_option('name-from-revision', help='The path name in the old tree.')
569
378
 
570
 
diff_writer_registry = _mod_registry.Registry()
571
 
diff_writer_registry.register('plain', lambda x: x, 'Plaintext diff output.')
572
 
diff_writer_registry.default_key = 'plain'
 
379
 
 
380
# prior to 0.14 these were always globally registered; the old dict is
 
381
# available for plugins that use it but it should not be used.
 
382
Option.SHORT_OPTIONS = symbol_versioning.DeprecatedDict(
 
383
    symbol_versioning.zero_fourteen,
 
384
    'SHORT_OPTIONS',
 
385
    {
 
386
        'F': Option.OPTIONS['file'],
 
387
        'h': Option.OPTIONS['help'],
 
388
        'm': Option.OPTIONS['message'],
 
389
        'r': Option.OPTIONS['revision'],
 
390
        'v': Option.OPTIONS['verbose'],
 
391
        'l': Option.OPTIONS['long'],
 
392
        'q': Option.OPTIONS['quiet'],
 
393
    },
 
394
    'Set the short option name when constructing the Option.',
 
395
    )