~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Robert Collins
  • Date: 2007-04-19 02:27:44 UTC
  • mto: This revision was merged to the branch mainline in revision 2426.
  • Revision ID: robertc@robertcollins.net-20070419022744-pfdqz42kp1wizh43
``make docs`` now creates a man page at ``man1/bzr.1`` fixing bug 107388.
(Robert Collins)

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
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
 
18
18
# TODO: probably should say which arguments are candidates for glob
35
35
lazy_import(globals(), """
36
36
import codecs
37
37
import errno
38
 
import threading
39
38
from warnings import warn
40
39
 
41
40
import bzrlib
45
44
    option,
46
45
    osutils,
47
46
    trace,
48
 
    win32utils,
49
47
    )
50
48
""")
51
49
 
52
 
from bzrlib import registry
 
50
from bzrlib.symbol_versioning import (
 
51
    deprecated_function,
 
52
    deprecated_method,
 
53
    zero_eight,
 
54
    zero_eleven,
 
55
    )
53
56
# Compatibility
54
 
from bzrlib.hooks import HookPoint, Hooks
55
57
from bzrlib.option import Option
56
58
 
57
59
 
58
 
class CommandInfo(object):
59
 
    """Information about a command."""
60
 
 
61
 
    def __init__(self, aliases):
62
 
        """The list of aliases for the command."""
63
 
        self.aliases = aliases
64
 
 
65
 
    @classmethod
66
 
    def from_command(klass, command):
67
 
        """Factory to construct a CommandInfo from a command."""
68
 
        return klass(command.aliases)
69
 
 
70
 
 
71
 
class CommandRegistry(registry.Registry):
72
 
 
73
 
    @staticmethod
74
 
    def _get_name(command_name):
75
 
        if command_name.startswith("cmd_"):
76
 
            return _unsquish_command_name(command_name)
77
 
        else:
78
 
            return command_name
79
 
 
80
 
    def register(self, cmd, decorate=False):
81
 
        """Utility function to help register a command
82
 
 
83
 
        :param cmd: Command subclass to register
84
 
        :param decorate: If true, allow overriding an existing command
85
 
            of the same name; the old command is returned by this function.
86
 
            Otherwise it is an error to try to override an existing command.
87
 
        """
88
 
        k = cmd.__name__
89
 
        k_unsquished = self._get_name(k)
90
 
        try:
91
 
            previous = self.get(k_unsquished)
92
 
        except KeyError:
93
 
            previous = _builtin_commands().get(k_unsquished)
94
 
        info = CommandInfo.from_command(cmd)
95
 
        try:
96
 
            registry.Registry.register(self, k_unsquished, cmd,
97
 
                                       override_existing=decorate, info=info)
98
 
        except KeyError:
99
 
            trace.log_error('Two plugins defined the same command: %r' % k)
100
 
            trace.log_error('Not loading the one in %r' %
101
 
                            sys.modules[cmd.__module__])
102
 
            trace.log_error('Previously this command was registered from %r' %
103
 
                            sys.modules[previous.__module__])
104
 
        return previous
105
 
 
106
 
    def register_lazy(self, command_name, aliases, module_name):
107
 
        """Register a command without loading its module.
108
 
 
109
 
        :param command_name: The primary name of the command.
110
 
        :param aliases: A list of aliases for the command.
111
 
        :module_name: The module that the command lives in.
112
 
        """
113
 
        key = self._get_name(command_name)
114
 
        registry.Registry.register_lazy(self, key, module_name, command_name,
115
 
                                        info=CommandInfo(aliases))
116
 
 
117
 
 
118
 
plugin_cmds = CommandRegistry()
 
60
plugin_cmds = {}
119
61
 
120
62
 
121
63
def register_command(cmd, decorate=False):
 
64
    """Utility function to help register a command
 
65
 
 
66
    :param cmd: Command subclass to register
 
67
    :param decorate: If true, allow overriding an existing command
 
68
        of the same name; the old command is returned by this function.
 
69
        Otherwise it is an error to try to override an existing command.
 
70
    """
122
71
    global plugin_cmds
123
 
    return plugin_cmds.register(cmd, decorate)
 
72
    k = cmd.__name__
 
73
    if k.startswith("cmd_"):
 
74
        k_unsquished = _unsquish_command_name(k)
 
75
    else:
 
76
        k_unsquished = k
 
77
    if k_unsquished not in plugin_cmds:
 
78
        plugin_cmds[k_unsquished] = cmd
 
79
        ## trace.mutter('registered plugin command %s', k_unsquished)
 
80
        if decorate and k_unsquished in builtin_command_names():
 
81
            return _builtin_commands()[k_unsquished]
 
82
    elif decorate:
 
83
        result = plugin_cmds[k_unsquished]
 
84
        plugin_cmds[k_unsquished] = cmd
 
85
        return result
 
86
    else:
 
87
        trace.log_error('Two plugins defined the same command: %r' % k)
 
88
        trace.log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
124
89
 
125
90
 
126
91
def _squish_command_name(cmd):
128
93
 
129
94
 
130
95
def _unsquish_command_name(cmd):
 
96
    assert cmd.startswith("cmd_")
131
97
    return cmd[4:].replace('_','-')
132
98
 
133
99
 
140
106
            real_name = _unsquish_command_name(name)
141
107
            r[real_name] = builtins[name]
142
108
    return r
143
 
 
 
109
            
144
110
 
145
111
def builtin_command_names():
146
112
    """Return list of builtin command names."""
147
113
    return _builtin_commands().keys()
148
 
 
 
114
    
149
115
 
150
116
def plugin_command_names():
151
117
    return plugin_cmds.keys()
155
121
    """Return name->class mapping for all commands."""
156
122
    d = _builtin_commands()
157
123
    if plugins_override:
158
 
        d.update(plugin_cmds.iteritems())
 
124
        d.update(plugin_cmds)
159
125
    return d
160
126
 
161
 
 
 
127
    
162
128
def get_all_cmds(plugins_override=True):
163
129
    """Return canonical name and class for all registered commands."""
164
130
    for k, v in _get_cmd_dict(plugins_override=plugins_override).iteritems():
171
137
    plugins_override
172
138
        If true, plugin commands can override builtins.
173
139
    """
174
 
    try:
175
 
        cmd = _get_cmd_object(cmd_name, plugins_override)
176
 
        # Allow plugins to extend commands
177
 
        for hook in Command.hooks['extend_command']:
178
 
            hook(cmd)
179
 
        return cmd
180
 
    except KeyError:
181
 
        raise errors.BzrCommandError('unknown command "%s"' % cmd_name)
182
 
 
183
 
 
184
 
def _get_cmd_object(cmd_name, plugins_override=True):
185
 
    """Worker for get_cmd_object which raises KeyError rather than BzrCommandError."""
186
140
    from bzrlib.externalcommand import ExternalCommand
187
141
 
188
142
    # We want only 'ascii' command names, but the user may have typed
191
145
    # In the future, we may actually support Unicode command names.
192
146
 
193
147
    # first look up this command under the specified name
194
 
    if plugins_override:
195
 
        try:
196
 
            return plugin_cmds.get(cmd_name)()
197
 
        except KeyError:
198
 
            pass
199
 
    cmds = _get_cmd_dict(plugins_override=False)
 
148
    cmds = _get_cmd_dict(plugins_override=plugins_override)
200
149
    try:
201
150
        return cmds[cmd_name]()
202
151
    except KeyError:
203
152
        pass
204
 
    if plugins_override:
205
 
        for key in plugin_cmds.keys():
206
 
            info = plugin_cmds.get_info(key)
207
 
            if cmd_name in info.aliases:
208
 
                return plugin_cmds.get(key)()
 
153
 
209
154
    # look for any command which claims this as an alias
210
155
    for real_cmd_name, cmd_class in cmds.iteritems():
211
156
        if cmd_name in cmd_class.aliases:
215
160
    if cmd_obj:
216
161
        return cmd_obj
217
162
 
218
 
    # look for plugins that provide this command but aren't installed
219
 
    for provider in command_providers_registry:
220
 
        try:
221
 
            plugin_metadata = provider.plugin_for_command(cmd_name)
222
 
        except errors.NoPluginAvailable:
223
 
            pass
224
 
        else:
225
 
            raise errors.CommandAvailableInPlugin(cmd_name,
226
 
                                                  plugin_metadata, provider)
227
 
    raise KeyError
 
163
    raise errors.BzrCommandError('unknown command "%s"' % cmd_name)
228
164
 
229
165
 
230
166
class Command(object):
284
220
            sys.stdout is forced to be a binary stream, and line-endings
285
221
            will not mangled.
286
222
 
287
 
    :cvar hooks: An instance of CommandHooks.
288
223
    """
289
224
    aliases = []
290
225
    takes_args = []
292
227
    encoding_type = 'strict'
293
228
 
294
229
    hidden = False
295
 
 
 
230
    
296
231
    def __init__(self):
297
232
        """Construct an instance of this command."""
298
233
        if self.__doc__ == Command.__doc__:
299
234
            warn("No help message set for %r" % self)
300
 
        # List of standard options directly supported
301
 
        self.supported_std_options = []
302
 
 
303
 
    def _maybe_expand_globs(self, file_list):
304
 
        """Glob expand file_list if the platform does not do that itself.
305
 
 
306
 
        :return: A possibly empty list of unicode paths.
307
 
 
308
 
        Introduced in bzrlib 0.18.
309
 
        """
310
 
        if not file_list:
311
 
            file_list = []
312
 
        if sys.platform == 'win32':
313
 
            file_list = win32utils.glob_expand(file_list)
314
 
        return list(file_list)
315
 
 
316
 
    def _usage(self):
317
 
        """Return single-line grammar for this command.
318
 
 
319
 
        Only describes arguments, not options.
320
 
        """
321
 
        s = 'bzr ' + self.name() + ' '
322
 
        for aname in self.takes_args:
323
 
            aname = aname.upper()
324
 
            if aname[-1] in ['$', '+']:
325
 
                aname = aname[:-1] + '...'
326
 
            elif aname[-1] == '?':
327
 
                aname = '[' + aname[:-1] + ']'
328
 
            elif aname[-1] == '*':
329
 
                aname = '[' + aname[:-1] + '...]'
330
 
            s += aname + ' '
331
 
        s = s[:-1]      # remove last space
332
 
        return s
333
 
 
334
 
    def get_help_text(self, additional_see_also=None, plain=True,
335
 
                      see_also_as_links=False, verbose=True):
336
 
        """Return a text string with help for this command.
337
 
 
338
 
        :param additional_see_also: Additional help topics to be
339
 
            cross-referenced.
340
 
        :param plain: if False, raw help (reStructuredText) is
341
 
            returned instead of plain text.
342
 
        :param see_also_as_links: if True, convert items in 'See also'
343
 
            list to internal links (used by bzr_man rstx generator)
344
 
        :param verbose: if True, display the full help, otherwise
345
 
            leave out the descriptive sections and just display
346
 
            usage help (e.g. Purpose, Usage, Options) with a
347
 
            message explaining how to obtain full help.
348
 
        """
349
 
        doc = self.help()
350
 
        if doc is None:
351
 
            raise NotImplementedError("sorry, no detailed help yet for %r" % self.name())
352
 
 
353
 
        # Extract the summary (purpose) and sections out from the text
354
 
        purpose,sections,order = self._get_help_parts(doc)
355
 
 
356
 
        # If a custom usage section was provided, use it
357
 
        if sections.has_key('Usage'):
358
 
            usage = sections.pop('Usage')
359
 
        else:
360
 
            usage = self._usage()
361
 
 
362
 
        # The header is the purpose and usage
363
 
        result = ""
364
 
        result += ':Purpose: %s\n' % purpose
365
 
        if usage.find('\n') >= 0:
366
 
            result += ':Usage:\n%s\n' % usage
367
 
        else:
368
 
            result += ':Usage:   %s\n' % usage
369
 
        result += '\n'
370
 
 
371
 
        # Add the options
372
 
        options = option.get_optparser(self.options()).format_option_help()
373
 
        if options.startswith('Options:'):
374
 
            result += ':' + options
375
 
        elif options.startswith('options:'):
376
 
            # Python 2.4 version of optparse
377
 
            result += ':Options:' + options[len('options:'):]
378
 
        else:
379
 
            result += options
380
 
        result += '\n'
381
 
 
382
 
        if verbose:
383
 
            # Add the description, indenting it 2 spaces
384
 
            # to match the indentation of the options
385
 
            if sections.has_key(None):
386
 
                text = sections.pop(None)
387
 
                text = '\n  '.join(text.splitlines())
388
 
                result += ':%s:\n  %s\n\n' % ('Description',text)
389
 
 
390
 
            # Add the custom sections (e.g. Examples). Note that there's no need
391
 
            # to indent these as they must be indented already in the source.
392
 
            if sections:
393
 
                for label in order:
394
 
                    if sections.has_key(label):
395
 
                        result += ':%s:\n%s\n' % (label,sections[label])
396
 
                result += '\n'
397
 
        else:
398
 
            result += ("See bzr help %s for more details and examples.\n\n"
399
 
                % self.name())
400
 
 
401
 
        # Add the aliases, source (plug-in) and see also links, if any
402
 
        if self.aliases:
403
 
            result += ':Aliases:  '
404
 
            result += ', '.join(self.aliases) + '\n'
405
 
        plugin_name = self.plugin_name()
406
 
        if plugin_name is not None:
407
 
            result += ':From:     plugin "%s"\n' % plugin_name
408
 
        see_also = self.get_see_also(additional_see_also)
409
 
        if see_also:
410
 
            if not plain and see_also_as_links:
411
 
                see_also_links = []
412
 
                for item in see_also:
413
 
                    if item == 'topics':
414
 
                        # topics doesn't have an independent section
415
 
                        # so don't create a real link
416
 
                        see_also_links.append(item)
417
 
                    else:
418
 
                        # Use a reST link for this entry
419
 
                        see_also_links.append("`%s`_" % (item,))
420
 
                see_also = see_also_links
421
 
            result += ':See also: '
422
 
            result += ', '.join(see_also) + '\n'
423
 
 
424
 
        # If this will be rendered as plain text, convert it
425
 
        if plain:
426
 
            import bzrlib.help_topics
427
 
            result = bzrlib.help_topics.help_as_plain_text(result)
428
 
        return result
429
 
 
430
 
    @staticmethod
431
 
    def _get_help_parts(text):
432
 
        """Split help text into a summary and named sections.
433
 
 
434
 
        :return: (summary,sections,order) where summary is the top line and
435
 
            sections is a dictionary of the rest indexed by section name.
436
 
            order is the order the section appear in the text.
437
 
            A section starts with a heading line of the form ":xxx:".
438
 
            Indented text on following lines is the section value.
439
 
            All text found outside a named section is assigned to the
440
 
            default section which is given the key of None.
441
 
        """
442
 
        def save_section(sections, order, label, section):
443
 
            if len(section) > 0:
444
 
                if sections.has_key(label):
445
 
                    sections[label] += '\n' + section
446
 
                else:
447
 
                    order.append(label)
448
 
                    sections[label] = section
449
 
 
450
 
        lines = text.rstrip().splitlines()
451
 
        summary = lines.pop(0)
452
 
        sections = {}
453
 
        order = []
454
 
        label,section = None,''
455
 
        for line in lines:
456
 
            if line.startswith(':') and line.endswith(':') and len(line) > 2:
457
 
                save_section(sections, order, label, section)
458
 
                label,section = line[1:-1],''
459
 
            elif (label is not None) and len(line) > 1 and not line[0].isspace():
460
 
                save_section(sections, order, label, section)
461
 
                label,section = None,line
462
 
            else:
463
 
                if len(section) > 0:
464
 
                    section += '\n' + line
465
 
                else:
466
 
                    section = line
467
 
        save_section(sections, order, label, section)
468
 
        return summary, sections, order
469
 
 
470
 
    def get_help_topic(self):
471
 
        """Return the commands help topic - its name."""
472
 
        return self.name()
473
 
 
474
 
    def get_see_also(self, additional_terms=None):
475
 
        """Return a list of help topics that are related to this command.
476
 
 
477
 
        The list is derived from the content of the _see_also attribute. Any
478
 
        duplicates are removed and the result is in lexical order.
479
 
        :param additional_terms: Additional help topics to cross-reference.
480
 
        :return: A list of help topics.
481
 
        """
482
 
        see_also = set(getattr(self, '_see_also', []))
483
 
        if additional_terms:
484
 
            see_also.update(additional_terms)
485
 
        return sorted(see_also)
486
235
 
487
236
    def options(self):
488
237
        """Return dict of valid options for this command.
489
238
 
490
239
        Maps from long option name to option object."""
491
 
        r = Option.STD_OPTIONS.copy()
492
 
        std_names = r.keys()
 
240
        r = dict()
 
241
        r['help'] = option.Option.OPTIONS['help']
493
242
        for o in self.takes_options:
494
243
            if isinstance(o, basestring):
495
244
                o = option.Option.OPTIONS[o]
496
245
            r[o.name] = o
497
 
            if o.name in std_names:
498
 
                self.supported_std_options.append(o.name)
499
246
        return r
500
247
 
501
248
    def _setup_outf(self):
502
249
        """Return a file linked to stdout, which has proper encoding."""
 
250
        assert self.encoding_type in ['strict', 'exact', 'replace']
 
251
 
503
252
        # Originally I was using self.stdout, but that looks
504
253
        # *way* too much like sys.stdout
505
254
        if self.encoding_type == 'exact':
514
263
 
515
264
        output_encoding = osutils.get_terminal_encoding()
516
265
 
517
 
        self.outf = codecs.getwriter(output_encoding)(sys.stdout,
518
 
                        errors=self.encoding_type)
 
266
        # use 'replace' so that we don't abort if trying to write out
 
267
        # in e.g. the default C locale.
 
268
        self.outf = codecs.getwriter(output_encoding)(sys.stdout, errors=self.encoding_type)
519
269
        # For whatever reason codecs.getwriter() does not advertise its encoding
520
270
        # it just returns the encoding of the wrapped file, which is completely
521
271
        # bogus. So set the attribute, so we can find the correct encoding later.
528
278
                 DeprecationWarning, stacklevel=2)
529
279
            argv = []
530
280
        args, opts = parse_args(self, argv, alias_argv)
531
 
 
532
 
        # Process the standard options
533
281
        if 'help' in opts:  # e.g. bzr add --help
534
 
            sys.stdout.write(self.get_help_text())
535
 
            return 0
536
 
        if 'usage' in opts:  # e.g. bzr add --usage
537
 
            sys.stdout.write(self.get_help_text(verbose=False))
538
 
            return 0
539
 
        trace.set_verbosity_level(option._verbosity_level)
540
 
        if 'verbose' in self.supported_std_options:
541
 
            opts['verbose'] = trace.is_verbose()
542
 
        elif opts.has_key('verbose'):
543
 
            del opts['verbose']
544
 
        if 'quiet' in self.supported_std_options:
545
 
            opts['quiet'] = trace.is_quiet()
546
 
        elif opts.has_key('quiet'):
547
 
            del opts['quiet']
548
 
 
 
282
            from bzrlib.help import help_on_command
 
283
            help_on_command(self.name())
 
284
            return 0
549
285
        # mix arguments and options into one dictionary
550
286
        cmdargs = _match_argform(self.name(), self.takes_args, args)
551
287
        cmdopts = {}
558
294
        self._setup_outf()
559
295
 
560
296
        return self.run(**all_cmd_args)
561
 
 
 
297
    
562
298
    def run(self):
563
299
        """Actually run the command.
564
300
 
594
330
            return None
595
331
 
596
332
 
597
 
class CommandHooks(Hooks):
598
 
    """Hooks related to Command object creation/enumeration."""
599
 
 
600
 
    def __init__(self):
601
 
        """Create the default hooks.
602
 
 
603
 
        These are all empty initially, because by default nothing should get
604
 
        notified.
605
 
        """
606
 
        Hooks.__init__(self)
607
 
        self.create_hook(HookPoint('extend_command',
608
 
            "Called after creating a command object to allow modifications "
609
 
            "such as adding or removing options, docs etc. Called with the "
610
 
            "new bzrlib.commands.Command object.", (1, 13), None))
611
 
 
612
 
Command.hooks = CommandHooks()
613
 
 
 
333
# Technically, this function hasn't been use in a *really* long time
 
334
# but we are only deprecating it now.
 
335
@deprecated_function(zero_eleven)
 
336
def parse_spec(spec):
 
337
    """
 
338
    >>> parse_spec(None)
 
339
    [None, None]
 
340
    >>> parse_spec("./")
 
341
    ['./', None]
 
342
    >>> parse_spec("../@")
 
343
    ['..', -1]
 
344
    >>> parse_spec("../f/@35")
 
345
    ['../f', 35]
 
346
    >>> parse_spec('./@revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67')
 
347
    ['.', 'revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67']
 
348
    """
 
349
    if spec is None:
 
350
        return [None, None]
 
351
    if '/@' in spec:
 
352
        parsed = spec.split('/@')
 
353
        assert len(parsed) == 2
 
354
        if parsed[1] == "":
 
355
            parsed[1] = -1
 
356
        else:
 
357
            try:
 
358
                parsed[1] = int(parsed[1])
 
359
            except ValueError:
 
360
                pass # We can allow stuff like ./@revid:blahblahblah
 
361
            else:
 
362
                assert parsed[1] >=0
 
363
    else:
 
364
        parsed = [spec, None]
 
365
    return parsed
614
366
 
615
367
def parse_args(command, argv, alias_argv=None):
616
368
    """Parse command line.
617
 
 
 
369
    
618
370
    Arguments and options are parsed at this level before being passed
619
371
    down to specific command handlers.  This routine knows, from a
620
372
    lookup table, something about the available options, what optargs
669
421
                               % (cmd, argname.upper()))
670
422
            else:
671
423
                argdict[argname] = args.pop(0)
672
 
 
 
424
            
673
425
    if args:
674
426
        raise errors.BzrCommandError("extra argument to command %s: %s"
675
427
                                     % (cmd, args[0]))
676
428
 
677
429
    return argdict
678
430
 
679
 
def apply_coveraged(dirname, the_callable, *args, **kwargs):
680
 
    # Cannot use "import trace", as that would import bzrlib.trace instead of
681
 
    # the standard library's trace.
682
 
    trace = __import__('trace')
683
 
 
684
 
    tracer = trace.Trace(count=1, trace=0)
685
 
    sys.settrace(tracer.globaltrace)
686
 
    threading.settrace(tracer.globaltrace)
687
 
 
688
 
    try:
689
 
        return exception_to_return_code(the_callable, *args, **kwargs)
690
 
    finally:
691
 
        sys.settrace(None)
692
 
        results = tracer.results()
693
 
        results.write_results(show_missing=1, summary=False,
694
 
                              coverdir=dirname)
695
431
 
696
432
 
697
433
def apply_profiled(the_callable, *args, **kwargs):
702
438
    try:
703
439
        prof = hotshot.Profile(pfname)
704
440
        try:
705
 
            ret = prof.runcall(exception_to_return_code, the_callable, *args,
706
 
                **kwargs) or 0
 
441
            ret = prof.runcall(the_callable, *args, **kwargs) or 0
707
442
        finally:
708
443
            prof.close()
709
444
        stats = hotshot.stats.load(pfname)
718
453
        os.remove(pfname)
719
454
 
720
455
 
721
 
def exception_to_return_code(the_callable, *args, **kwargs):
722
 
    """UI level helper for profiling and coverage.
723
 
 
724
 
    This transforms exceptions into a return value of 3. As such its only
725
 
    relevant to the UI layer, and should never be called where catching
726
 
    exceptions may be desirable.
727
 
    """
728
 
    try:
729
 
        return the_callable(*args, **kwargs)
730
 
    except (KeyboardInterrupt, Exception), e:
731
 
        # used to handle AssertionError and KeyboardInterrupt
732
 
        # specially here, but hopefully they're handled ok by the logger now
733
 
        exc_info = sys.exc_info()
734
 
        exitcode = trace.report_exception(exc_info, sys.stderr)
735
 
        if os.environ.get('BZR_PDB'):
736
 
            print '**** entering debugger'
737
 
            tb = exc_info[2]
738
 
            import pdb
739
 
            if sys.version_info[:2] < (2, 6):
740
 
                # XXX: we want to do
741
 
                #    pdb.post_mortem(tb)
742
 
                # but because pdb.post_mortem gives bad results for tracebacks
743
 
                # from inside generators, we do it manually.
744
 
                # (http://bugs.python.org/issue4150, fixed in Python 2.6)
745
 
 
746
 
                # Setup pdb on the traceback
747
 
                p = pdb.Pdb()
748
 
                p.reset()
749
 
                p.setup(tb.tb_frame, tb)
750
 
                # Point the debugger at the deepest frame of the stack
751
 
                p.curindex = len(p.stack) - 1
752
 
                p.curframe = p.stack[p.curindex][0]
753
 
                # Start the pdb prompt.
754
 
                p.print_stack_entry(p.stack[p.curindex])
755
 
                p.execRcLines()
756
 
                p.cmdloop()
757
 
            else:
758
 
                pdb.post_mortem(tb)
759
 
        return exitcode
760
 
 
761
 
 
762
456
def apply_lsprofiled(filename, the_callable, *args, **kwargs):
763
457
    from bzrlib.lsprof import profile
764
 
    ret, stats = profile(exception_to_return_code, the_callable, *args, **kwargs)
 
458
    import cPickle
 
459
    ret, stats = profile(the_callable, *args, **kwargs)
765
460
    stats.sort()
766
461
    if filename is None:
767
462
        stats.pprint()
768
463
    else:
769
 
        stats.save(filename)
770
 
        trace.note('Profile data written to "%s".', filename)
 
464
        stats.freeze()
 
465
        cPickle.dump(stats, open(filename, 'w'), 2)
 
466
        print 'Profile data written to %r.' % filename
771
467
    return ret
772
468
 
773
469
 
774
 
def shlex_split_unicode(unsplit):
775
 
    import shlex
776
 
    return [u.decode('utf-8') for u in shlex.split(unsplit.encode('utf-8'))]
777
 
 
778
 
 
779
470
def get_alias(cmd, config=None):
780
471
    """Return an expanded alias, or None if no alias exists.
781
472
 
791
482
        config = bzrlib.config.GlobalConfig()
792
483
    alias = config.get_alias(cmd)
793
484
    if (alias):
794
 
        return shlex_split_unicode(alias)
 
485
        import shlex
 
486
        return [a.decode('utf-8') for a in shlex.split(alias.encode('utf-8'))]
795
487
    return None
796
488
 
797
489
 
798
490
def run_bzr(argv):
799
491
    """Execute a command.
800
492
 
 
493
    This is similar to main(), but without all the trappings for
 
494
    logging and error handling.  
 
495
    
801
496
    argv
802
497
       The command-line arguments, without the program name from argv[0]
803
498
       These should already be decoded. All library/test code calling
804
499
       run_bzr should be passing valid strings (don't need decoding).
805
 
 
 
500
    
806
501
    Returns a command status or raises an exception.
807
502
 
808
503
    Special master options: these must come before the command because
823
518
 
824
519
    --lsprof
825
520
        Run under the Python lsprof profiler.
826
 
 
827
 
    --coverage
828
 
        Generate line coverage report in the specified directory.
829
521
    """
830
522
    argv = list(argv)
831
523
    trace.mutter("bzr arguments: %r", argv)
832
524
 
833
525
    opt_lsprof = opt_profile = opt_no_plugins = opt_builtin =  \
834
526
                opt_no_aliases = False
835
 
    opt_lsprof_file = opt_coverage_dir = None
 
527
    opt_lsprof_file = None
836
528
 
837
529
    # --no-plugins is handled specially at a very early stage. We need
838
530
    # to load plugins before doing other command parsing so that they
856
548
            opt_no_aliases = True
857
549
        elif a == '--builtin':
858
550
            opt_builtin = True
859
 
        elif a == '--coverage':
860
 
            opt_coverage_dir = argv[i + 1]
861
 
            i += 1
 
551
        elif a in ('--quiet', '-q'):
 
552
            trace.be_quiet()
862
553
        elif a.startswith('-D'):
863
554
            debug.debug_flags.add(a[2:])
864
555
        else:
865
556
            argv_copy.append(a)
866
557
        i += 1
867
558
 
868
 
    debug.set_debug_flags_from_config()
869
 
 
870
559
    argv = argv_copy
871
560
    if (not argv):
872
561
        from bzrlib.builtins import cmd_help
874
563
        return 0
875
564
 
876
565
    if argv[0] == '--version':
877
 
        from bzrlib.builtins import cmd_version
878
 
        cmd_version().run_argv_aliases([])
 
566
        from bzrlib.version import show_version
 
567
        show_version()
879
568
        return 0
880
 
 
 
569
        
881
570
    if not opt_no_plugins:
882
571
        from bzrlib.plugin import load_plugins
883
572
        load_plugins()
890
579
    if not opt_no_aliases:
891
580
        alias_argv = get_alias(argv[0])
892
581
        if alias_argv:
893
 
            user_encoding = osutils.get_user_encoding()
894
 
            alias_argv = [a.decode(user_encoding) for a in alias_argv]
 
582
            alias_argv = [a.decode(bzrlib.user_encoding) for a in alias_argv]
895
583
            argv[0] = alias_argv.pop(0)
896
584
 
897
585
    cmd = argv.pop(0)
904
592
    run_argv = [argv, alias_argv]
905
593
 
906
594
    try:
907
 
        # We can be called recursively (tests for example), but we don't want
908
 
        # the verbosity level to propagate.
909
 
        saved_verbosity_level = option._verbosity_level
910
 
        option._verbosity_level = 0
911
595
        if opt_lsprof:
912
 
            if opt_coverage_dir:
913
 
                trace.warning(
914
 
                    '--coverage ignored, because --lsprof is in use.')
915
596
            ret = apply_lsprofiled(opt_lsprof_file, run, *run_argv)
916
597
        elif opt_profile:
917
 
            if opt_coverage_dir:
918
 
                trace.warning(
919
 
                    '--coverage ignored, because --profile is in use.')
920
598
            ret = apply_profiled(run, *run_argv)
921
 
        elif opt_coverage_dir:
922
 
            ret = apply_coveraged(opt_coverage_dir, run, *run_argv)
923
599
        else:
924
600
            ret = run(*run_argv)
925
 
        if 'memory' in debug.debug_flags:
926
 
            trace.debug_memory('Process status after command:', short=False)
927
601
        return ret or 0
928
602
    finally:
929
 
        # reset, in case we may do other commands later within the same
930
 
        # process. Commands that want to execute sub-commands must propagate
931
 
        # --verbose in their own way.
932
 
        option._verbosity_level = saved_verbosity_level
933
 
 
 
603
        # reset, in case we may do other commands later within the same process
 
604
        trace.be_quiet(False)
934
605
 
935
606
def display_command(func):
936
607
    """Decorator that suppresses pipe/interrupt errors."""
952
623
    return ignore_pipe
953
624
 
954
625
 
955
 
def main(argv=None):
956
 
    """Main entry point of command-line interface.
957
 
 
958
 
    :param argv: list of unicode command-line arguments similar to sys.argv.
959
 
        argv[0] is script name usually, it will be ignored.
960
 
        Don't pass here sys.argv because this list contains plain strings
961
 
        and not unicode; pass None instead.
962
 
 
963
 
    :return: exit code of bzr command.
964
 
    """
 
626
def main(argv):
965
627
    import bzrlib.ui
966
 
    bzrlib.ui.ui_factory = bzrlib.ui.make_ui_for_terminal(
967
 
        sys.stdin, sys.stdout, sys.stderr)
968
 
 
969
 
    # Is this a final release version? If so, we should suppress warnings
970
 
    if bzrlib.version_info[3] == 'final':
971
 
        from bzrlib import symbol_versioning
972
 
        symbol_versioning.suppress_deprecation_warnings(override=False)
973
 
    if argv is None:
974
 
        argv = osutils.get_unicode_argv()
975
 
    else:
976
 
        new_argv = []
977
 
        try:
978
 
            # ensure all arguments are unicode strings
979
 
            for a in argv[1:]:
980
 
                if isinstance(a, unicode):
981
 
                    new_argv.append(a)
982
 
                else:
983
 
                    new_argv.append(a.decode('ascii'))
984
 
        except UnicodeDecodeError:
985
 
            raise errors.BzrError("argv should be list of unicode strings.")
986
 
        argv = new_argv
 
628
    from bzrlib.ui.text import TextUIFactory
 
629
    bzrlib.ui.ui_factory = TextUIFactory()
 
630
    argv = [a.decode(bzrlib.user_encoding) for a in argv[1:]]
987
631
    ret = run_bzr_catch_errors(argv)
988
632
    trace.mutter("return code %d", ret)
989
633
    return ret
990
634
 
991
635
 
992
636
def run_bzr_catch_errors(argv):
993
 
    """Run a bzr command with parameters as described by argv.
994
 
 
995
 
    This function assumed that that UI layer is setup, that symbol deprecations
996
 
    are already applied, and that unicode decoding has already been performed on argv.
997
 
    """
998
 
    return exception_to_return_code(run_bzr, argv)
999
 
 
1000
 
 
1001
 
def run_bzr_catch_user_errors(argv):
1002
 
    """Run bzr and report user errors, but let internal errors propagate.
1003
 
 
1004
 
    This is used for the test suite, and might be useful for other programs
1005
 
    that want to wrap the commandline interface.
1006
 
    """
1007
637
    try:
1008
638
        return run_bzr(argv)
1009
 
    except Exception, e:
1010
 
        if (isinstance(e, (OSError, IOError))
1011
 
            or not getattr(e, 'internal_error', True)):
1012
 
            trace.report_exception(sys.exc_info(), sys.stderr)
1013
 
            return 3
1014
 
        else:
1015
 
            raise
1016
 
 
1017
 
 
1018
 
class HelpCommandIndex(object):
1019
 
    """A index for bzr help that returns commands."""
1020
 
 
1021
 
    def __init__(self):
1022
 
        self.prefix = 'commands/'
1023
 
 
1024
 
    def get_topics(self, topic):
1025
 
        """Search for topic amongst commands.
1026
 
 
1027
 
        :param topic: A topic to search for.
1028
 
        :return: A list which is either empty or contains a single
1029
 
            Command entry.
1030
 
        """
1031
 
        if topic and topic.startswith(self.prefix):
1032
 
            topic = topic[len(self.prefix):]
1033
 
        try:
1034
 
            cmd = _get_cmd_object(topic)
1035
 
        except KeyError:
1036
 
            return []
1037
 
        else:
1038
 
            return [cmd]
1039
 
 
1040
 
 
1041
 
class Provider(object):
1042
 
    '''Generic class to be overriden by plugins'''
1043
 
 
1044
 
    def plugin_for_command(self, cmd_name):
1045
 
        '''Takes a command and returns the information for that plugin
1046
 
 
1047
 
        :return: A dictionary with all the available information
1048
 
        for the requested plugin
1049
 
        '''
1050
 
        raise NotImplementedError
1051
 
 
1052
 
 
1053
 
class ProvidersRegistry(registry.Registry):
1054
 
    '''This registry exists to allow other providers to exist'''
1055
 
 
1056
 
    def __iter__(self):
1057
 
        for key, provider in self.iteritems():
1058
 
            yield provider
1059
 
 
1060
 
command_providers_registry = ProvidersRegistry()
1061
 
 
 
639
        # do this here inside the exception wrappers to catch EPIPE
 
640
        sys.stdout.flush()
 
641
    except (KeyboardInterrupt, Exception), e:
 
642
        # used to handle AssertionError and KeyboardInterrupt
 
643
        # specially here, but hopefully they're handled ok by the logger now
 
644
        trace.report_exception(sys.exc_info(), sys.stderr)
 
645
        if os.environ.get('BZR_PDB'):
 
646
            print '**** entering debugger'
 
647
            import pdb
 
648
            pdb.post_mortem(sys.exc_traceback)
 
649
        return 3
1062
650
 
1063
651
if __name__ == '__main__':
1064
652
    sys.exit(main(sys.argv))