~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

merge with bzr.dev and put the NEWS entry in the IN DEVELOPMENT section

Show diffs side-by-side

added added

removed removed

Lines of Context:
50
50
 
51
51
from bzrlib import registry
52
52
# Compatibility
 
53
from bzrlib.hooks import Hooks
53
54
from bzrlib.option import Option
54
55
 
55
56
 
170
171
        If true, plugin commands can override builtins.
171
172
    """
172
173
    try:
173
 
        return _get_cmd_object(cmd_name, plugins_override)
 
174
        cmd = _get_cmd_object(cmd_name, plugins_override)
 
175
        # Allow plugins to extend commands
 
176
        for hook in Command.hooks['extend_command']:
 
177
            hook(cmd)
 
178
        return cmd
174
179
    except KeyError:
175
180
        raise errors.BzrCommandError('unknown command "%s"' % cmd_name)
176
181
 
216
221
        except errors.NoPluginAvailable:
217
222
            pass
218
223
        else:
219
 
            raise errors.CommandAvailableInPlugin(cmd_name, 
 
224
            raise errors.CommandAvailableInPlugin(cmd_name,
220
225
                                                  plugin_metadata, provider)
221
 
 
222
226
    raise KeyError
223
227
 
224
228
 
279
283
            sys.stdout is forced to be a binary stream, and line-endings
280
284
            will not mangled.
281
285
 
 
286
    :cvar hooks: An instance of CommandHooks.
282
287
    """
283
288
    aliases = []
284
289
    takes_args = []
341
346
            raise NotImplementedError("sorry, no detailed help yet for %r" % self.name())
342
347
 
343
348
        # Extract the summary (purpose) and sections out from the text
344
 
        purpose,sections = self._get_help_parts(doc)
 
349
        purpose,sections,order = self._get_help_parts(doc)
345
350
 
346
351
        # If a custom usage section was provided, use it
347
352
        if sections.has_key('Usage'):
379
384
        # Add the custom sections (e.g. Examples). Note that there's no need
380
385
        # to indent these as they must be indented already in the source.
381
386
        if sections:
382
 
            labels = sorted(sections.keys())
383
 
            for label in labels:
384
 
                result += ':%s:\n%s\n\n' % (label,sections[label])
 
387
            for label in order:
 
388
                if sections.has_key(label):
 
389
                    result += ':%s:\n%s\n\n' % (label,sections[label])
385
390
 
386
391
        # Add the aliases, source (plug-in) and see also links, if any
387
392
        if self.aliases:
416
421
    def _get_help_parts(text):
417
422
        """Split help text into a summary and named sections.
418
423
 
419
 
        :return: (summary,sections) where summary is the top line and
 
424
        :return: (summary,sections,order) where summary is the top line and
420
425
            sections is a dictionary of the rest indexed by section name.
 
426
            order is the order the section appear in the text.
421
427
            A section starts with a heading line of the form ":xxx:".
422
428
            Indented text on following lines is the section value.
423
429
            All text found outside a named section is assigned to the
424
430
            default section which is given the key of None.
425
431
        """
426
 
        def save_section(sections, label, section):
 
432
        def save_section(sections, order, label, section):
427
433
            if len(section) > 0:
428
434
                if sections.has_key(label):
429
435
                    sections[label] += '\n' + section
430
436
                else:
 
437
                    order.append(label)
431
438
                    sections[label] = section
432
439
 
433
440
        lines = text.rstrip().splitlines()
434
441
        summary = lines.pop(0)
435
442
        sections = {}
 
443
        order = []
436
444
        label,section = None,''
437
445
        for line in lines:
438
446
            if line.startswith(':') and line.endswith(':') and len(line) > 2:
439
 
                save_section(sections, label, section)
 
447
                save_section(sections, order, label, section)
440
448
                label,section = line[1:-1],''
441
449
            elif (label is not None) and len(line) > 1 and not line[0].isspace():
442
 
                save_section(sections, label, section)
 
450
                save_section(sections, order, label, section)
443
451
                label,section = None,line
444
452
            else:
445
453
                if len(section) > 0:
446
454
                    section += '\n' + line
447
455
                else:
448
456
                    section = line
449
 
        save_section(sections, label, section)
450
 
        return summary, sections
 
457
        save_section(sections, order, label, section)
 
458
        return summary, sections, order
451
459
 
452
460
    def get_help_topic(self):
453
461
        """Return the commands help topic - its name."""
573
581
            return None
574
582
 
575
583
 
 
584
class CommandHooks(Hooks):
 
585
    """Hooks related to Command object creation/enumeration."""
 
586
 
 
587
    def __init__(self):
 
588
        """Create the default hooks.
 
589
 
 
590
        These are all empty initially, because by default nothing should get
 
591
        notified.
 
592
        """
 
593
        Hooks.__init__(self)
 
594
        # Introduced in 1.13:
 
595
        # invoked after creating a command object to allow modifications such
 
596
        # as adding or removing options, docs etc. Invoked with the command
 
597
        # object.
 
598
        self['extend_command'] = []
 
599
 
 
600
Command.hooks = CommandHooks()
 
601
 
 
602
 
576
603
def parse_args(command, argv, alias_argv=None):
577
604
    """Parse command line.
578
605
    
846
873
        # --verbose in their own way.
847
874
        option._verbosity_level = saved_verbosity_level
848
875
 
 
876
 
849
877
def display_command(func):
850
878
    """Decorator that suppresses pipe/interrupt errors."""
851
879
    def ignore_pipe(*args, **kwargs):