~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Aaron Bentley
  • Date: 2007-12-09 23:53:50 UTC
  • mto: This revision was merged to the branch mainline in revision 3133.
  • Revision ID: aaron.bentley@utoronto.ca-20071209235350-qp39yk0xzx7a4f6p
Don't use the base if not cherrypicking

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006, 2008 Canonical Ltd
 
1
# Copyright (C) 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
48
48
    )
49
49
""")
50
50
 
51
 
from bzrlib import registry
 
51
from bzrlib.symbol_versioning import (
 
52
    deprecated_function,
 
53
    deprecated_method,
 
54
    )
52
55
# Compatibility
53
 
from bzrlib.hooks import Hooks
54
56
from bzrlib.option import Option
55
57
 
56
58
 
57
 
class CommandInfo(object):
58
 
    """Information about a command."""
59
 
 
60
 
    def __init__(self, aliases):
61
 
        """The list of aliases for the command."""
62
 
        self.aliases = aliases
63
 
 
64
 
    @classmethod
65
 
    def from_command(klass, command):
66
 
        """Factory to construct a CommandInfo from a command."""
67
 
        return klass(command.aliases)
68
 
 
69
 
 
70
 
class CommandRegistry(registry.Registry):
71
 
 
72
 
    @staticmethod
73
 
    def _get_name(command_name):
74
 
        if command_name.startswith("cmd_"):
75
 
            return _unsquish_command_name(command_name)
76
 
        else:
77
 
            return command_name
78
 
 
79
 
    def register(self, cmd, decorate=False):
80
 
        """Utility function to help register a command
81
 
 
82
 
        :param cmd: Command subclass to register
83
 
        :param decorate: If true, allow overriding an existing command
84
 
            of the same name; the old command is returned by this function.
85
 
            Otherwise it is an error to try to override an existing command.
86
 
        """
87
 
        k = cmd.__name__
88
 
        k_unsquished = self._get_name(k)
89
 
        try:
90
 
            previous = self.get(k_unsquished)
91
 
        except KeyError:
92
 
            previous = _builtin_commands().get(k_unsquished)
93
 
        info = CommandInfo.from_command(cmd)
94
 
        try:
95
 
            registry.Registry.register(self, k_unsquished, cmd,
96
 
                                       override_existing=decorate, info=info)
97
 
        except KeyError:
98
 
            trace.log_error('Two plugins defined the same command: %r' % k)
99
 
            trace.log_error('Not loading the one in %r' %
100
 
                            sys.modules[cmd.__module__])
101
 
            trace.log_error('Previously this command was registered from %r' %
102
 
                            sys.modules[previous.__module__])
103
 
        return previous
104
 
 
105
 
    def register_lazy(self, command_name, aliases, module_name):
106
 
        """Register a command without loading its module.
107
 
 
108
 
        :param command_name: The primary name of the command.
109
 
        :param aliases: A list of aliases for the command.
110
 
        :module_name: The module that the command lives in.
111
 
        """
112
 
        key = self._get_name(command_name)
113
 
        registry.Registry.register_lazy(self, key, module_name, command_name,
114
 
                                        info=CommandInfo(aliases))
115
 
 
116
 
 
117
 
plugin_cmds = CommandRegistry()
 
59
plugin_cmds = {}
118
60
 
119
61
 
120
62
def register_command(cmd, decorate=False):
 
63
    """Utility function to help register a command
 
64
 
 
65
    :param cmd: Command subclass to register
 
66
    :param decorate: If true, allow overriding an existing command
 
67
        of the same name; the old command is returned by this function.
 
68
        Otherwise it is an error to try to override an existing command.
 
69
    """
121
70
    global plugin_cmds
122
 
    return plugin_cmds.register(cmd, decorate)
 
71
    k = cmd.__name__
 
72
    if k.startswith("cmd_"):
 
73
        k_unsquished = _unsquish_command_name(k)
 
74
    else:
 
75
        k_unsquished = k
 
76
    if k_unsquished not in plugin_cmds:
 
77
        plugin_cmds[k_unsquished] = cmd
 
78
        ## trace.mutter('registered plugin command %s', k_unsquished)
 
79
        if decorate and k_unsquished in builtin_command_names():
 
80
            return _builtin_commands()[k_unsquished]
 
81
    elif decorate:
 
82
        result = plugin_cmds[k_unsquished]
 
83
        plugin_cmds[k_unsquished] = cmd
 
84
        return result
 
85
    else:
 
86
        trace.log_error('Two plugins defined the same command: %r' % k)
 
87
        trace.log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
 
88
        trace.log_error('Previously this command was registered from %r' %
 
89
                        sys.modules[plugin_cmds[k_unsquished].__module__])
123
90
 
124
91
 
125
92
def _squish_command_name(cmd):
127
94
 
128
95
 
129
96
def _unsquish_command_name(cmd):
 
97
    assert cmd.startswith("cmd_")
130
98
    return cmd[4:].replace('_','-')
131
99
 
132
100
 
154
122
    """Return name->class mapping for all commands."""
155
123
    d = _builtin_commands()
156
124
    if plugins_override:
157
 
        d.update(plugin_cmds.iteritems())
 
125
        d.update(plugin_cmds)
158
126
    return d
159
127
 
160
128
    
171
139
        If true, plugin commands can override builtins.
172
140
    """
173
141
    try:
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
 
142
        return _get_cmd_object(cmd_name, plugins_override)
179
143
    except KeyError:
180
144
        raise errors.BzrCommandError('unknown command "%s"' % cmd_name)
181
145
 
190
154
    # In the future, we may actually support Unicode command names.
191
155
 
192
156
    # first look up this command under the specified name
193
 
    if plugins_override:
194
 
        try:
195
 
            return plugin_cmds.get(cmd_name)()
196
 
        except KeyError:
197
 
            pass
198
 
    cmds = _get_cmd_dict(plugins_override=False)
 
157
    cmds = _get_cmd_dict(plugins_override=plugins_override)
199
158
    try:
200
159
        return cmds[cmd_name]()
201
160
    except KeyError:
202
161
        pass
203
 
    if plugins_override:
204
 
        for key in plugin_cmds.keys():
205
 
            info = plugin_cmds.get_info(key)
206
 
            if cmd_name in info.aliases:
207
 
                return plugin_cmds.get(key)()
 
162
 
208
163
    # look for any command which claims this as an alias
209
164
    for real_cmd_name, cmd_class in cmds.iteritems():
210
165
        if cmd_name in cmd_class.aliases:
213
168
    cmd_obj = ExternalCommand.find_command(cmd_name)
214
169
    if cmd_obj:
215
170
        return cmd_obj
216
 
 
217
 
    # look for plugins that provide this command but aren't installed
218
 
    for provider in command_providers_registry:
219
 
        try:
220
 
            plugin_metadata = provider.plugin_for_command(cmd_name)
221
 
        except errors.NoPluginAvailable:
222
 
            pass
223
 
        else:
224
 
            raise errors.CommandAvailableInPlugin(cmd_name,
225
 
                                                  plugin_metadata, provider)
226
171
    raise KeyError
227
172
 
228
173
 
283
228
            sys.stdout is forced to be a binary stream, and line-endings
284
229
            will not mangled.
285
230
 
286
 
    :cvar hooks: An instance of CommandHooks.
287
231
    """
288
232
    aliases = []
289
233
    takes_args = []
327
271
            elif aname[-1] == '*':
328
272
                aname = '[' + aname[:-1] + '...]'
329
273
            s += aname + ' '
330
 
        s = s[:-1]      # remove last space
 
274
                
 
275
        assert s[-1] == ' '
 
276
        s = s[:-1]
331
277
        return s
332
278
 
333
279
    def get_help_text(self, additional_see_also=None, plain=True,
346
292
            raise NotImplementedError("sorry, no detailed help yet for %r" % self.name())
347
293
 
348
294
        # Extract the summary (purpose) and sections out from the text
349
 
        purpose,sections,order = self._get_help_parts(doc)
 
295
        purpose,sections = self._get_help_parts(doc)
350
296
 
351
297
        # If a custom usage section was provided, use it
352
298
        if sections.has_key('Usage'):
384
330
        # Add the custom sections (e.g. Examples). Note that there's no need
385
331
        # to indent these as they must be indented already in the source.
386
332
        if sections:
387
 
            for label in order:
388
 
                if sections.has_key(label):
389
 
                    result += ':%s:\n%s\n\n' % (label,sections[label])
 
333
            labels = sorted(sections.keys())
 
334
            for label in labels:
 
335
                result += ':%s:\n%s\n\n' % (label,sections[label])
390
336
 
391
337
        # Add the aliases, source (plug-in) and see also links, if any
392
338
        if self.aliases:
411
357
            result += ':See also: '
412
358
            result += ', '.join(see_also) + '\n'
413
359
 
414
 
        # If this will be rendered as plain text, convert it
 
360
        # If this will be rendered as plan text, convert it
415
361
        if plain:
416
362
            import bzrlib.help_topics
417
363
            result = bzrlib.help_topics.help_as_plain_text(result)
421
367
    def _get_help_parts(text):
422
368
        """Split help text into a summary and named sections.
423
369
 
424
 
        :return: (summary,sections,order) where summary is the top line and
 
370
        :return: (summary,sections) where summary is the top line and
425
371
            sections is a dictionary of the rest indexed by section name.
426
 
            order is the order the section appear in the text.
427
372
            A section starts with a heading line of the form ":xxx:".
428
373
            Indented text on following lines is the section value.
429
374
            All text found outside a named section is assigned to the
430
375
            default section which is given the key of None.
431
376
        """
432
 
        def save_section(sections, order, label, section):
 
377
        def save_section(sections, label, section):
433
378
            if len(section) > 0:
434
379
                if sections.has_key(label):
435
380
                    sections[label] += '\n' + section
436
381
                else:
437
 
                    order.append(label)
438
382
                    sections[label] = section
439
 
 
 
383
            
440
384
        lines = text.rstrip().splitlines()
441
385
        summary = lines.pop(0)
442
386
        sections = {}
443
 
        order = []
444
387
        label,section = None,''
445
388
        for line in lines:
446
389
            if line.startswith(':') and line.endswith(':') and len(line) > 2:
447
 
                save_section(sections, order, label, section)
 
390
                save_section(sections, label, section)
448
391
                label,section = line[1:-1],''
449
 
            elif (label is not None) and len(line) > 1 and not line[0].isspace():
450
 
                save_section(sections, order, label, section)
 
392
            elif label != None and len(line) > 1 and not line[0].isspace():
 
393
                save_section(sections, label, section)
451
394
                label,section = None,line
452
395
            else:
453
396
                if len(section) > 0:
454
397
                    section += '\n' + line
455
398
                else:
456
399
                    section = line
457
 
        save_section(sections, order, label, section)
458
 
        return summary, sections, order
 
400
        save_section(sections, label, section)
 
401
        return summary, sections
459
402
 
460
403
    def get_help_topic(self):
461
404
        """Return the commands help topic - its name."""
490
433
 
491
434
    def _setup_outf(self):
492
435
        """Return a file linked to stdout, which has proper encoding."""
 
436
        assert self.encoding_type in ['strict', 'exact', 'replace']
 
437
 
493
438
        # Originally I was using self.stdout, but that looks
494
439
        # *way* too much like sys.stdout
495
440
        if self.encoding_type == 'exact':
545
490
        self._setup_outf()
546
491
 
547
492
        return self.run(**all_cmd_args)
548
 
 
 
493
    
549
494
    def run(self):
550
495
        """Actually run the command.
551
496
 
581
526
            return None
582
527
 
583
528
 
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
 
 
603
529
def parse_args(command, argv, alias_argv=None):
604
530
    """Parse command line.
605
531
    
664
590
 
665
591
    return argdict
666
592
 
667
 
def apply_coveraged(dirname, the_callable, *args, **kwargs):
668
 
    # Cannot use "import trace", as that would import bzrlib.trace instead of
669
 
    # the standard library's trace.
670
 
    trace = __import__('trace')
671
 
 
672
 
    tracer = trace.Trace(count=1, trace=0)
673
 
    sys.settrace(tracer.globaltrace)
674
 
 
675
 
    ret = the_callable(*args, **kwargs)
676
 
 
677
 
    sys.settrace(None)
678
 
    results = tracer.results()
679
 
    results.write_results(show_missing=1, summary=False,
680
 
                          coverdir=dirname)
681
593
 
682
594
 
683
595
def apply_profiled(the_callable, *args, **kwargs):
715
627
    return ret
716
628
 
717
629
 
718
 
def shlex_split_unicode(unsplit):
719
 
    import shlex
720
 
    return [u.decode('utf-8') for u in shlex.split(unsplit.encode('utf-8'))]
721
 
 
722
 
 
723
630
def get_alias(cmd, config=None):
724
631
    """Return an expanded alias, or None if no alias exists.
725
632
 
735
642
        config = bzrlib.config.GlobalConfig()
736
643
    alias = config.get_alias(cmd)
737
644
    if (alias):
738
 
        return shlex_split_unicode(alias)
 
645
        import shlex
 
646
        return [a.decode('utf-8') for a in shlex.split(alias.encode('utf-8'))]
739
647
    return None
740
648
 
741
649
 
742
650
def run_bzr(argv):
743
651
    """Execute a command.
744
652
 
 
653
    This is similar to main(), but without all the trappings for
 
654
    logging and error handling.  
 
655
    
745
656
    argv
746
657
       The command-line arguments, without the program name from argv[0]
747
658
       These should already be decoded. All library/test code calling
767
678
 
768
679
    --lsprof
769
680
        Run under the Python lsprof profiler.
770
 
 
771
 
    --coverage
772
 
        Generate line coverage report in the specified directory.
773
681
    """
774
682
    argv = list(argv)
775
683
    trace.mutter("bzr arguments: %r", argv)
776
684
 
777
685
    opt_lsprof = opt_profile = opt_no_plugins = opt_builtin =  \
778
686
                opt_no_aliases = False
779
 
    opt_lsprof_file = opt_coverage_dir = None
 
687
    opt_lsprof_file = None
780
688
 
781
689
    # --no-plugins is handled specially at a very early stage. We need
782
690
    # to load plugins before doing other command parsing so that they
800
708
            opt_no_aliases = True
801
709
        elif a == '--builtin':
802
710
            opt_builtin = True
803
 
        elif a == '--coverage':
804
 
            opt_coverage_dir = argv[i + 1]
805
 
            i += 1
806
711
        elif a.startswith('-D'):
807
712
            debug.debug_flags.add(a[2:])
808
713
        else:
819
724
        from bzrlib.builtins import cmd_version
820
725
        cmd_version().run_argv_aliases([])
821
726
        return 0
822
 
 
 
727
        
823
728
    if not opt_no_plugins:
824
729
        from bzrlib.plugin import load_plugins
825
730
        load_plugins()
832
737
    if not opt_no_aliases:
833
738
        alias_argv = get_alias(argv[0])
834
739
        if alias_argv:
835
 
            user_encoding = osutils.get_user_encoding()
836
 
            alias_argv = [a.decode(user_encoding) for a in alias_argv]
 
740
            alias_argv = [a.decode(bzrlib.user_encoding) for a in alias_argv]
837
741
            argv[0] = alias_argv.pop(0)
838
742
 
839
743
    cmd = argv.pop(0)
846
750
    run_argv = [argv, alias_argv]
847
751
 
848
752
    try:
849
 
        # We can be called recursively (tests for example), but we don't want
850
 
        # the verbosity level to propagate.
851
 
        saved_verbosity_level = option._verbosity_level
852
 
        option._verbosity_level = 0
853
753
        if opt_lsprof:
854
 
            if opt_coverage_dir:
855
 
                trace.warning(
856
 
                    '--coverage ignored, because --lsprof is in use.')
857
754
            ret = apply_lsprofiled(opt_lsprof_file, run, *run_argv)
858
755
        elif opt_profile:
859
 
            if opt_coverage_dir:
860
 
                trace.warning(
861
 
                    '--coverage ignored, because --profile is in use.')
862
756
            ret = apply_profiled(run, *run_argv)
863
 
        elif opt_coverage_dir:
864
 
            ret = apply_coveraged(opt_coverage_dir, run, *run_argv)
865
757
        else:
866
758
            ret = run(*run_argv)
867
 
        if 'memory' in debug.debug_flags:
868
 
            trace.debug_memory('Process status after command:', short=False)
869
759
        return ret or 0
870
760
    finally:
871
 
        # reset, in case we may do other commands later within the same
872
 
        # process. Commands that want to execute sub-commands must propagate
873
 
        # --verbose in their own way.
874
 
        option._verbosity_level = saved_verbosity_level
875
 
 
 
761
        # reset, in case we may do other commands later within the same process
 
762
        option._verbosity_level = 0
876
763
 
877
764
def display_command(func):
878
765
    """Decorator that suppresses pipe/interrupt errors."""
896
783
 
897
784
def main(argv):
898
785
    import bzrlib.ui
899
 
    bzrlib.ui.ui_factory = bzrlib.ui.make_ui_for_terminal(
900
 
        sys.stdin, sys.stdout, sys.stderr)
901
 
 
902
 
    # Is this a final release version? If so, we should suppress warnings
903
 
    if bzrlib.version_info[3] == 'final':
904
 
        from bzrlib import symbol_versioning
905
 
        symbol_versioning.suppress_deprecation_warnings(override=False)
 
786
    from bzrlib.ui.text import TextUIFactory
 
787
    bzrlib.ui.ui_factory = TextUIFactory()
906
788
    try:
907
 
        user_encoding = osutils.get_user_encoding()
908
 
        argv = [a.decode(user_encoding) for a in argv[1:]]
 
789
        argv = [a.decode(bzrlib.user_encoding) for a in argv[1:]]
909
790
    except UnicodeDecodeError:
910
791
        raise errors.BzrError(("Parameter '%r' is unsupported by the current "
911
792
                                                            "encoding." % a))
922
803
    except (KeyboardInterrupt, Exception), e:
923
804
        # used to handle AssertionError and KeyboardInterrupt
924
805
        # specially here, but hopefully they're handled ok by the logger now
925
 
        exc_info = sys.exc_info()
926
 
        exitcode = trace.report_exception(exc_info, sys.stderr)
 
806
        exitcode = trace.report_exception(sys.exc_info(), sys.stderr)
927
807
        if os.environ.get('BZR_PDB'):
928
808
            print '**** entering debugger'
929
 
            tb = exc_info[2]
930
809
            import pdb
931
 
            if sys.version_info[:2] < (2, 6):
932
 
                # XXX: we want to do
933
 
                #    pdb.post_mortem(tb)
934
 
                # but because pdb.post_mortem gives bad results for tracebacks
935
 
                # from inside generators, we do it manually.
936
 
                # (http://bugs.python.org/issue4150, fixed in Python 2.6)
937
 
                
938
 
                # Setup pdb on the traceback
939
 
                p = pdb.Pdb()
940
 
                p.reset()
941
 
                p.setup(tb.tb_frame, tb)
942
 
                # Point the debugger at the deepest frame of the stack
943
 
                p.curindex = len(p.stack) - 1
944
 
                p.curframe = p.stack[p.curindex]
945
 
                # Start the pdb prompt.
946
 
                p.print_stack_entry(p.stack[p.curindex])
947
 
                p.execRcLines()
948
 
                p.cmdloop()
949
 
            else:
950
 
                pdb.post_mortem(tb)
 
810
            pdb.post_mortem(sys.exc_traceback)
951
811
        return exitcode
952
812
 
953
813
 
991
851
            return [cmd]
992
852
 
993
853
 
994
 
class Provider(object):
995
 
    '''Generic class to be overriden by plugins'''
996
 
 
997
 
    def plugin_for_command(self, cmd_name):
998
 
        '''Takes a command and returns the information for that plugin
999
 
        
1000
 
        :return: A dictionary with all the available information 
1001
 
        for the requested plugin
1002
 
        '''
1003
 
        raise NotImplementedError
1004
 
 
1005
 
 
1006
 
class ProvidersRegistry(registry.Registry):
1007
 
    '''This registry exists to allow other providers to exist'''
1008
 
 
1009
 
    def __iter__(self):
1010
 
        for key, provider in self.iteritems():
1011
 
            yield provider
1012
 
 
1013
 
command_providers_registry = ProvidersRegistry()
1014
 
 
1015
 
 
1016
854
if __name__ == '__main__':
1017
855
    sys.exit(main(sys.argv))