~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Andrew Bennetts
  • Date: 2010-01-12 03:53:21 UTC
  • mfrom: (4948 +trunk)
  • mto: This revision was merged to the branch mainline in revision 4964.
  • Revision ID: andrew.bennetts@canonical.com-20100112035321-hofpz5p10224ryj3
Merge lp:bzr, resolving conflicts.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006, 2008 Canonical Ltd
 
1
# Copyright (C) 2006, 2008, 2009 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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
38
39
from warnings import warn
39
40
 
40
41
import bzrlib
41
42
from bzrlib import (
 
43
    cleanup,
42
44
    debug,
43
45
    errors,
44
46
    option,
45
47
    osutils,
46
48
    trace,
 
49
    ui,
47
50
    win32utils,
48
51
    )
49
52
""")
50
53
 
 
54
from bzrlib.hooks import HookPoint, Hooks
 
55
# Compatibility - Option used to be in commands.
 
56
from bzrlib.option import Option
51
57
from bzrlib import registry
52
 
# Compatibility
53
 
from bzrlib.option import Option
 
58
from bzrlib.symbol_versioning import (
 
59
    deprecated_function,
 
60
    deprecated_in,
 
61
    deprecated_method,
 
62
    suppress_deprecation_warnings,
 
63
    )
54
64
 
55
65
 
56
66
class CommandInfo(object):
94
104
            registry.Registry.register(self, k_unsquished, cmd,
95
105
                                       override_existing=decorate, info=info)
96
106
        except KeyError:
97
 
            trace.log_error('Two plugins defined the same command: %r' % k)
98
 
            trace.log_error('Not loading the one in %r' %
99
 
                            sys.modules[cmd.__module__])
100
 
            trace.log_error('Previously this command was registered from %r' %
101
 
                            sys.modules[previous.__module__])
 
107
            trace.warning('Two plugins defined the same command: %r' % k)
 
108
            trace.warning('Not loading the one in %r' %
 
109
                sys.modules[cmd.__module__])
 
110
            trace.warning('Previously this command was registered from %r' %
 
111
                sys.modules[previous.__module__])
102
112
        return previous
103
113
 
104
114
    def register_lazy(self, command_name, aliases, module_name):
131
141
 
132
142
def _builtin_commands():
133
143
    import bzrlib.builtins
 
144
    return _scan_module_for_commands(bzrlib.builtins)
 
145
 
 
146
 
 
147
def _scan_module_for_commands(module):
134
148
    r = {}
135
 
    builtins = bzrlib.builtins.__dict__
136
 
    for name in builtins:
 
149
    for name, obj in module.__dict__.iteritems():
137
150
        if name.startswith("cmd_"):
138
151
            real_name = _unsquish_command_name(name)
139
 
            r[real_name] = builtins[name]
 
152
            r[real_name] = obj
140
153
    return r
141
 
            
 
154
 
 
155
 
 
156
def _list_bzr_commands(names):
 
157
    """Find commands from bzr's core and plugins."""
 
158
    # to eliminate duplicates
 
159
    names.update(builtin_command_names())
 
160
    names.update(plugin_command_names())
 
161
    return names
 
162
 
 
163
 
 
164
def all_command_names():
 
165
    """Return a set of all command names."""
 
166
    names = set()
 
167
    for hook in Command.hooks['list_commands']:
 
168
        names = hook(names)
 
169
        if names is None:
 
170
            raise AssertionError(
 
171
                'hook %s returned None' % Command.hooks.get_hook_name(hook))
 
172
    return names
 
173
 
142
174
 
143
175
def builtin_command_names():
144
 
    """Return list of builtin command names."""
 
176
    """Return list of builtin command names.
 
177
    
 
178
    Use of all_command_names() is encouraged rather than builtin_command_names
 
179
    and/or plugin_command_names.
 
180
    """
145
181
    return _builtin_commands().keys()
146
 
    
 
182
 
147
183
 
148
184
def plugin_command_names():
 
185
    """Returns command names from commands registered by plugins."""
149
186
    return plugin_cmds.keys()
150
187
 
151
188
 
152
 
def _get_cmd_dict(plugins_override=True):
153
 
    """Return name->class mapping for all commands."""
 
189
@deprecated_function(deprecated_in((1, 17, 0)))
 
190
def get_all_cmds(plugins_override=False):
 
191
    """Return canonical name and class for most commands.
 
192
    
 
193
    NB: This does not return all commands since the introduction of
 
194
    command hooks, and returning the class is not sufficient to 
 
195
    get correctly setup commands, which is why it is deprecated.
 
196
 
 
197
    Use 'all_command_names' + 'get_cmd_object' instead.
 
198
    """
154
199
    d = _builtin_commands()
155
200
    if plugins_override:
156
201
        d.update(plugin_cmds.iteritems())
157
 
    return d
158
 
 
159
 
    
160
 
def get_all_cmds(plugins_override=True):
161
 
    """Return canonical name and class for all registered commands."""
162
 
    for k, v in _get_cmd_dict(plugins_override=plugins_override).iteritems():
 
202
    for k, v in d.iteritems():
163
203
        yield k,v
164
204
 
165
205
 
166
206
def get_cmd_object(cmd_name, plugins_override=True):
167
 
    """Return the canonical name and command class for a command.
 
207
    """Return the command object for a command.
168
208
 
169
209
    plugins_override
170
210
        If true, plugin commands can override builtins.
176
216
 
177
217
 
178
218
def _get_cmd_object(cmd_name, plugins_override=True):
179
 
    """Worker for get_cmd_object which raises KeyError rather than BzrCommandError."""
180
 
    from bzrlib.externalcommand import ExternalCommand
 
219
    """Get a command object.
181
220
 
 
221
    :param cmd_name: The name of the command.
 
222
    :param plugins_override: Allow plugins to override builtins.
 
223
    :return: A Command object instance
 
224
    :raises KeyError: If no command is found.
 
225
    """
182
226
    # We want only 'ascii' command names, but the user may have typed
183
227
    # in a Unicode name. In that case, they should just get a
184
228
    # 'command not found' error later.
185
229
    # In the future, we may actually support Unicode command names.
186
 
 
187
 
    # first look up this command under the specified name
188
 
    if plugins_override:
 
230
    cmd = None
 
231
    # Get a command
 
232
    for hook in Command.hooks['get_command']:
 
233
        cmd = hook(cmd, cmd_name)
 
234
        if cmd is not None and not plugins_override and not cmd.plugin_name():
 
235
            # We've found a non-plugin command, don't permit it to be
 
236
            # overridden.
 
237
            break
 
238
    if cmd is None:
 
239
        for hook in Command.hooks['get_missing_command']:
 
240
            cmd = hook(cmd_name)
 
241
            if cmd is not None:
 
242
                break
 
243
    if cmd is None:
 
244
        # No command found.
 
245
        raise KeyError
 
246
    # Allow plugins to extend commands
 
247
    for hook in Command.hooks['extend_command']:
 
248
        hook(cmd)
 
249
    return cmd
 
250
 
 
251
 
 
252
def _try_plugin_provider(cmd_name):
 
253
    """Probe for a plugin provider having cmd_name."""
 
254
    try:
 
255
        plugin_metadata, provider = probe_for_provider(cmd_name)
 
256
        raise errors.CommandAvailableInPlugin(cmd_name,
 
257
            plugin_metadata, provider)
 
258
    except errors.NoPluginAvailable:
 
259
        pass
 
260
 
 
261
 
 
262
def probe_for_provider(cmd_name):
 
263
    """Look for a provider for cmd_name.
 
264
 
 
265
    :param cmd_name: The command name.
 
266
    :return: plugin_metadata, provider for getting cmd_name.
 
267
    :raises NoPluginAvailable: When no provider can supply the plugin.
 
268
    """
 
269
    # look for providers that provide this command but aren't installed
 
270
    for provider in command_providers_registry:
189
271
        try:
190
 
            return plugin_cmds.get(cmd_name)()
191
 
        except KeyError:
 
272
            return provider.plugin_for_command(cmd_name), provider
 
273
        except errors.NoPluginAvailable:
192
274
            pass
193
 
    cmds = _get_cmd_dict(plugins_override=False)
 
275
    raise errors.NoPluginAvailable(cmd_name)
 
276
 
 
277
 
 
278
def _get_bzr_command(cmd_or_None, cmd_name):
 
279
    """Get a command from bzr's core."""
 
280
    cmds = _builtin_commands()
194
281
    try:
195
282
        return cmds[cmd_name]()
196
283
    except KeyError:
197
284
        pass
198
 
    if plugins_override:
199
 
        for key in plugin_cmds.keys():
200
 
            info = plugin_cmds.get_info(key)
201
 
            if cmd_name in info.aliases:
202
 
                return plugin_cmds.get(key)()
203
285
    # look for any command which claims this as an alias
204
286
    for real_cmd_name, cmd_class in cmds.iteritems():
205
287
        if cmd_name in cmd_class.aliases:
206
288
            return cmd_class()
207
 
 
 
289
    return cmd_or_None
 
290
 
 
291
 
 
292
def _get_external_command(cmd_or_None, cmd_name):
 
293
    """Lookup a command that is a shell script."""
 
294
    # Only do external command lookups when no command is found so far.
 
295
    if cmd_or_None is not None:
 
296
        return cmd_or_None
 
297
    from bzrlib.externalcommand import ExternalCommand
208
298
    cmd_obj = ExternalCommand.find_command(cmd_name)
209
299
    if cmd_obj:
210
300
        return cmd_obj
211
301
 
212
 
    # look for plugins that provide this command but aren't installed
213
 
    for provider in command_providers_registry:
214
 
        try:
215
 
            plugin_metadata = provider.plugin_for_command(cmd_name)
216
 
        except errors.NoPluginAvailable:
217
 
            pass
218
 
        else:
219
 
            raise errors.CommandAvailableInPlugin(cmd_name, 
220
 
                                                  plugin_metadata, provider)
221
302
 
222
 
    raise KeyError
 
303
def _get_plugin_command(cmd_or_None, cmd_name):
 
304
    """Get a command from bzr's plugins."""
 
305
    try:
 
306
        return plugin_cmds.get(cmd_name)()
 
307
    except KeyError:
 
308
        pass
 
309
    for key in plugin_cmds.keys():
 
310
        info = plugin_cmds.get_info(key)
 
311
        if cmd_name in info.aliases:
 
312
            return plugin_cmds.get(key)()
 
313
    return cmd_or_None
223
314
 
224
315
 
225
316
class Command(object):
279
370
            sys.stdout is forced to be a binary stream, and line-endings
280
371
            will not mangled.
281
372
 
 
373
    :cvar hooks: An instance of CommandHooks.
282
374
    """
283
375
    aliases = []
284
376
    takes_args = []
286
378
    encoding_type = 'strict'
287
379
 
288
380
    hidden = False
289
 
    
 
381
 
290
382
    def __init__(self):
291
383
        """Construct an instance of this command."""
292
384
        if self.__doc__ == Command.__doc__:
293
385
            warn("No help message set for %r" % self)
294
386
        # List of standard options directly supported
295
387
        self.supported_std_options = []
296
 
 
 
388
        self._operation = cleanup.OperationWithCleanups(self.run)
 
389
    
 
390
    def add_cleanup(self, cleanup_func, *args, **kwargs):
 
391
        """Register a function to call after self.run returns or raises.
 
392
 
 
393
        Functions will be called in LIFO order.
 
394
        """
 
395
        self._operation.add_cleanup(cleanup_func, *args, **kwargs)
 
396
 
 
397
    def cleanup_now(self):
 
398
        """Execute and empty pending cleanup functions immediately.
 
399
 
 
400
        After cleanup_now all registered cleanups are forgotten.  add_cleanup
 
401
        may be called again after cleanup_now; these cleanups will be called
 
402
        after self.run returns or raises (or when cleanup_now is next called).
 
403
 
 
404
        This is useful for releasing expensive or contentious resources (such
 
405
        as write locks) before doing further work that does not require those
 
406
        resources (such as writing results to self.outf).
 
407
        """
 
408
        self._operation.cleanup_now()
 
409
        
 
410
    @deprecated_method(deprecated_in((2, 1, 0)))
297
411
    def _maybe_expand_globs(self, file_list):
298
412
        """Glob expand file_list if the platform does not do that itself.
299
 
        
 
413
 
 
414
        Not used anymore, now that the bzr command-line parser globs on
 
415
        Windows.
 
416
 
300
417
        :return: A possibly empty list of unicode paths.
301
418
 
302
419
        Introduced in bzrlib 0.18.
303
420
        """
304
 
        if not file_list:
305
 
            file_list = []
306
 
        if sys.platform == 'win32':
307
 
            file_list = win32utils.glob_expand(file_list)
308
 
        return list(file_list)
 
421
        return file_list
309
422
 
310
423
    def _usage(self):
311
424
        """Return single-line grammar for this command.
326
439
        return s
327
440
 
328
441
    def get_help_text(self, additional_see_also=None, plain=True,
329
 
                      see_also_as_links=False):
 
442
                      see_also_as_links=False, verbose=True):
330
443
        """Return a text string with help for this command.
331
 
        
 
444
 
332
445
        :param additional_see_also: Additional help topics to be
333
446
            cross-referenced.
334
447
        :param plain: if False, raw help (reStructuredText) is
335
448
            returned instead of plain text.
336
449
        :param see_also_as_links: if True, convert items in 'See also'
337
450
            list to internal links (used by bzr_man rstx generator)
 
451
        :param verbose: if True, display the full help, otherwise
 
452
            leave out the descriptive sections and just display
 
453
            usage help (e.g. Purpose, Usage, Options) with a
 
454
            message explaining how to obtain full help.
338
455
        """
339
456
        doc = self.help()
340
457
        if doc is None:
341
458
            raise NotImplementedError("sorry, no detailed help yet for %r" % self.name())
342
459
 
343
460
        # Extract the summary (purpose) and sections out from the text
344
 
        purpose,sections = self._get_help_parts(doc)
 
461
        purpose,sections,order = self._get_help_parts(doc)
345
462
 
346
463
        # If a custom usage section was provided, use it
347
464
        if sections.has_key('Usage'):
359
476
        result += '\n'
360
477
 
361
478
        # Add the options
 
479
        #
 
480
        # XXX: optparse implicitly rewraps the help, and not always perfectly,
 
481
        # so we get <https://bugs.launchpad.net/bzr/+bug/249908>.  -- mbp
 
482
        # 20090319
362
483
        options = option.get_optparser(self.options()).format_option_help()
 
484
        # XXX: According to the spec, ReST option lists actually don't support 
 
485
        # options like --1.9 so that causes syntax errors (in Sphinx at least).
 
486
        # As that pattern always appears in the commands that break, we trap
 
487
        # on that and then format that block of 'format' options as a literal
 
488
        # block.
 
489
        if not plain and options.find('  --1.9  ') != -1:
 
490
            options = options.replace(' format:\n', ' format::\n\n', 1)
363
491
        if options.startswith('Options:'):
364
492
            result += ':' + options
365
493
        elif options.startswith('options:'):
369
497
            result += options
370
498
        result += '\n'
371
499
 
372
 
        # Add the description, indenting it 2 spaces
373
 
        # to match the indentation of the options
374
 
        if sections.has_key(None):
375
 
            text = sections.pop(None)
376
 
            text = '\n  '.join(text.splitlines())
377
 
            result += ':%s:\n  %s\n\n' % ('Description',text)
 
500
        if verbose:
 
501
            # Add the description, indenting it 2 spaces
 
502
            # to match the indentation of the options
 
503
            if sections.has_key(None):
 
504
                text = sections.pop(None)
 
505
                text = '\n  '.join(text.splitlines())
 
506
                result += ':%s:\n  %s\n\n' % ('Description',text)
378
507
 
379
 
        # Add the custom sections (e.g. Examples). Note that there's no need
380
 
        # to indent these as they must be indented already in the source.
381
 
        if sections:
382
 
            labels = sorted(sections.keys())
383
 
            for label in labels:
384
 
                result += ':%s:\n%s\n\n' % (label,sections[label])
 
508
            # Add the custom sections (e.g. Examples). Note that there's no need
 
509
            # to indent these as they must be indented already in the source.
 
510
            if sections:
 
511
                for label in order:
 
512
                    if sections.has_key(label):
 
513
                        result += ':%s:\n%s\n' % (label,sections[label])
 
514
                result += '\n'
 
515
        else:
 
516
            result += ("See bzr help %s for more details and examples.\n\n"
 
517
                % self.name())
385
518
 
386
519
        # Add the aliases, source (plug-in) and see also links, if any
387
520
        if self.aliases:
400
533
                        # so don't create a real link
401
534
                        see_also_links.append(item)
402
535
                    else:
403
 
                        # Use a reST link for this entry
404
 
                        see_also_links.append("`%s`_" % (item,))
 
536
                        # Use a Sphinx link for this entry
 
537
                        link_text = ":doc:`%s <%s-help>`" % (item, item)
 
538
                        see_also_links.append(link_text)
405
539
                see_also = see_also_links
406
540
            result += ':See also: '
407
541
            result += ', '.join(see_also) + '\n'
416
550
    def _get_help_parts(text):
417
551
        """Split help text into a summary and named sections.
418
552
 
419
 
        :return: (summary,sections) where summary is the top line and
 
553
        :return: (summary,sections,order) where summary is the top line and
420
554
            sections is a dictionary of the rest indexed by section name.
 
555
            order is the order the section appear in the text.
421
556
            A section starts with a heading line of the form ":xxx:".
422
557
            Indented text on following lines is the section value.
423
558
            All text found outside a named section is assigned to the
424
559
            default section which is given the key of None.
425
560
        """
426
 
        def save_section(sections, label, section):
 
561
        def save_section(sections, order, label, section):
427
562
            if len(section) > 0:
428
563
                if sections.has_key(label):
429
564
                    sections[label] += '\n' + section
430
565
                else:
 
566
                    order.append(label)
431
567
                    sections[label] = section
432
568
 
433
569
        lines = text.rstrip().splitlines()
434
570
        summary = lines.pop(0)
435
571
        sections = {}
 
572
        order = []
436
573
        label,section = None,''
437
574
        for line in lines:
438
575
            if line.startswith(':') and line.endswith(':') and len(line) > 2:
439
 
                save_section(sections, label, section)
 
576
                save_section(sections, order, label, section)
440
577
                label,section = line[1:-1],''
441
578
            elif (label is not None) and len(line) > 1 and not line[0].isspace():
442
 
                save_section(sections, label, section)
 
579
                save_section(sections, order, label, section)
443
580
                label,section = None,line
444
581
            else:
445
582
                if len(section) > 0:
446
583
                    section += '\n' + line
447
584
                else:
448
585
                    section = line
449
 
        save_section(sections, label, section)
450
 
        return summary, sections
 
586
        save_section(sections, order, label, section)
 
587
        return summary, sections, order
451
588
 
452
589
    def get_help_topic(self):
453
590
        """Return the commands help topic - its name."""
455
592
 
456
593
    def get_see_also(self, additional_terms=None):
457
594
        """Return a list of help topics that are related to this command.
458
 
        
 
595
 
459
596
        The list is derived from the content of the _see_also attribute. Any
460
597
        duplicates are removed and the result is in lexical order.
461
598
        :param additional_terms: Additional help topics to cross-reference.
482
619
 
483
620
    def _setup_outf(self):
484
621
        """Return a file linked to stdout, which has proper encoding."""
485
 
        # Originally I was using self.stdout, but that looks
486
 
        # *way* too much like sys.stdout
487
 
        if self.encoding_type == 'exact':
488
 
            # force sys.stdout to be binary stream on win32
489
 
            if sys.platform == 'win32':
490
 
                fileno = getattr(sys.stdout, 'fileno', None)
491
 
                if fileno:
492
 
                    import msvcrt
493
 
                    msvcrt.setmode(fileno(), os.O_BINARY)
494
 
            self.outf = sys.stdout
495
 
            return
496
 
 
497
 
        output_encoding = osutils.get_terminal_encoding()
498
 
 
499
 
        self.outf = codecs.getwriter(output_encoding)(sys.stdout,
500
 
                        errors=self.encoding_type)
501
 
        # For whatever reason codecs.getwriter() does not advertise its encoding
502
 
        # it just returns the encoding of the wrapped file, which is completely
503
 
        # bogus. So set the attribute, so we can find the correct encoding later.
504
 
        self.outf.encoding = output_encoding
 
622
        self.outf = ui.ui_factory.make_output_stream(
 
623
            encoding_type=self.encoding_type)
505
624
 
506
625
    def run_argv_aliases(self, argv, alias_argv=None):
507
626
        """Parse the command line and run with extra aliases in alias_argv."""
515
634
        if 'help' in opts:  # e.g. bzr add --help
516
635
            sys.stdout.write(self.get_help_text())
517
636
            return 0
 
637
        if 'usage' in opts:  # e.g. bzr add --usage
 
638
            sys.stdout.write(self.get_help_text(verbose=False))
 
639
            return 0
518
640
        trace.set_verbosity_level(option._verbosity_level)
519
641
        if 'verbose' in self.supported_std_options:
520
642
            opts['verbose'] = trace.is_verbose()
536
658
 
537
659
        self._setup_outf()
538
660
 
539
 
        return self.run(**all_cmd_args)
 
661
        return self.run_direct(**all_cmd_args)
 
662
 
 
663
    def run_direct(self, *args, **kwargs):
 
664
        """Call run directly with objects (without parsing an argv list)."""
 
665
        return self._operation.run_simple(*args, **kwargs)
540
666
 
541
667
    def run(self):
542
668
        """Actually run the command.
573
699
            return None
574
700
 
575
701
 
 
702
class CommandHooks(Hooks):
 
703
    """Hooks related to Command object creation/enumeration."""
 
704
 
 
705
    def __init__(self):
 
706
        """Create the default hooks.
 
707
 
 
708
        These are all empty initially, because by default nothing should get
 
709
        notified.
 
710
        """
 
711
        Hooks.__init__(self)
 
712
        self.create_hook(HookPoint('extend_command',
 
713
            "Called after creating a command object to allow modifications "
 
714
            "such as adding or removing options, docs etc. Called with the "
 
715
            "new bzrlib.commands.Command object.", (1, 13), None))
 
716
        self.create_hook(HookPoint('get_command',
 
717
            "Called when creating a single command. Called with "
 
718
            "(cmd_or_None, command_name). get_command should either return "
 
719
            "the cmd_or_None parameter, or a replacement Command object that "
 
720
            "should be used for the command. Note that the Command.hooks "
 
721
            "hooks are core infrastructure. Many users will prefer to use "
 
722
            "bzrlib.commands.register_command or plugin_cmds.register_lazy.",
 
723
            (1, 17), None))
 
724
        self.create_hook(HookPoint('get_missing_command',
 
725
            "Called when creating a single command if no command could be "
 
726
            "found. Called with (command_name). get_missing_command should "
 
727
            "either return None, or a Command object to be used for the "
 
728
            "command.", (1, 17), None))
 
729
        self.create_hook(HookPoint('list_commands',
 
730
            "Called when enumerating commands. Called with a set of "
 
731
            "cmd_name strings for all the commands found so far. This set "
 
732
            " is safe to mutate - e.g. to remove a command. "
 
733
            "list_commands should return the updated set of command names.",
 
734
            (1, 17), None))
 
735
 
 
736
Command.hooks = CommandHooks()
 
737
 
 
738
 
576
739
def parse_args(command, argv, alias_argv=None):
577
740
    """Parse command line.
578
 
    
 
741
 
579
742
    Arguments and options are parsed at this level before being passed
580
743
    down to specific command handlers.  This routine knows, from a
581
744
    lookup table, something about the available options, what optargs
630
793
                               % (cmd, argname.upper()))
631
794
            else:
632
795
                argdict[argname] = args.pop(0)
633
 
            
 
796
 
634
797
    if args:
635
798
        raise errors.BzrCommandError("extra argument to command %s: %s"
636
799
                                     % (cmd, args[0]))
644
807
 
645
808
    tracer = trace.Trace(count=1, trace=0)
646
809
    sys.settrace(tracer.globaltrace)
647
 
 
648
 
    ret = the_callable(*args, **kwargs)
649
 
 
650
 
    sys.settrace(None)
651
 
    results = tracer.results()
652
 
    results.write_results(show_missing=1, summary=False,
653
 
                          coverdir=dirname)
 
810
    threading.settrace(tracer.globaltrace)
 
811
 
 
812
    try:
 
813
        return exception_to_return_code(the_callable, *args, **kwargs)
 
814
    finally:
 
815
        sys.settrace(None)
 
816
        results = tracer.results()
 
817
        results.write_results(show_missing=1, summary=False,
 
818
                              coverdir=dirname)
654
819
 
655
820
 
656
821
def apply_profiled(the_callable, *args, **kwargs):
661
826
    try:
662
827
        prof = hotshot.Profile(pfname)
663
828
        try:
664
 
            ret = prof.runcall(the_callable, *args, **kwargs) or 0
 
829
            ret = prof.runcall(exception_to_return_code, the_callable, *args,
 
830
                **kwargs) or 0
665
831
        finally:
666
832
            prof.close()
667
833
        stats = hotshot.stats.load(pfname)
676
842
        os.remove(pfname)
677
843
 
678
844
 
 
845
def exception_to_return_code(the_callable, *args, **kwargs):
 
846
    """UI level helper for profiling and coverage.
 
847
 
 
848
    This transforms exceptions into a return value of 3. As such its only
 
849
    relevant to the UI layer, and should never be called where catching
 
850
    exceptions may be desirable.
 
851
    """
 
852
    try:
 
853
        return the_callable(*args, **kwargs)
 
854
    except (KeyboardInterrupt, Exception), e:
 
855
        # used to handle AssertionError and KeyboardInterrupt
 
856
        # specially here, but hopefully they're handled ok by the logger now
 
857
        exc_info = sys.exc_info()
 
858
        exitcode = trace.report_exception(exc_info, sys.stderr)
 
859
        if os.environ.get('BZR_PDB'):
 
860
            print '**** entering debugger'
 
861
            tb = exc_info[2]
 
862
            import pdb
 
863
            if sys.version_info[:2] < (2, 6):
 
864
                # XXX: we want to do
 
865
                #    pdb.post_mortem(tb)
 
866
                # but because pdb.post_mortem gives bad results for tracebacks
 
867
                # from inside generators, we do it manually.
 
868
                # (http://bugs.python.org/issue4150, fixed in Python 2.6)
 
869
 
 
870
                # Setup pdb on the traceback
 
871
                p = pdb.Pdb()
 
872
                p.reset()
 
873
                p.setup(tb.tb_frame, tb)
 
874
                # Point the debugger at the deepest frame of the stack
 
875
                p.curindex = len(p.stack) - 1
 
876
                p.curframe = p.stack[p.curindex][0]
 
877
                # Start the pdb prompt.
 
878
                p.print_stack_entry(p.stack[p.curindex])
 
879
                p.execRcLines()
 
880
                p.cmdloop()
 
881
            else:
 
882
                pdb.post_mortem(tb)
 
883
        return exitcode
 
884
 
 
885
 
679
886
def apply_lsprofiled(filename, the_callable, *args, **kwargs):
680
887
    from bzrlib.lsprof import profile
681
 
    ret, stats = profile(the_callable, *args, **kwargs)
 
888
    ret, stats = profile(exception_to_return_code, the_callable, *args, **kwargs)
682
889
    stats.sort()
683
890
    if filename is None:
684
891
        stats.pprint()
719
926
       The command-line arguments, without the program name from argv[0]
720
927
       These should already be decoded. All library/test code calling
721
928
       run_bzr should be passing valid strings (don't need decoding).
722
 
    
 
929
 
723
930
    Returns a command status or raises an exception.
724
931
 
725
932
    Special master options: these must come before the command because
743
950
 
744
951
    --coverage
745
952
        Generate line coverage report in the specified directory.
 
953
 
 
954
    --concurrency
 
955
        Specify the number of processes that can be run concurrently (selftest).
746
956
    """
 
957
    trace.mutter("bazaar version: " + bzrlib.__version__)
747
958
    argv = list(argv)
748
959
    trace.mutter("bzr arguments: %r", argv)
749
960
 
773
984
            opt_no_aliases = True
774
985
        elif a == '--builtin':
775
986
            opt_builtin = True
 
987
        elif a == '--concurrency':
 
988
            os.environ['BZR_CONCURRENCY'] = argv[i + 1]
 
989
            i += 1
776
990
        elif a == '--coverage':
777
991
            opt_coverage_dir = argv[i + 1]
778
992
            i += 1
782
996
            argv_copy.append(a)
783
997
        i += 1
784
998
 
 
999
    debug.set_debug_flags_from_config()
 
1000
 
785
1001
    argv = argv_copy
786
1002
    if (not argv):
787
1003
        from bzrlib.builtins import cmd_help
837
1053
            ret = apply_coveraged(opt_coverage_dir, run, *run_argv)
838
1054
        else:
839
1055
            ret = run(*run_argv)
840
 
        if 'memory' in debug.debug_flags:
841
 
            trace.debug_memory('Process status after command:', short=False)
842
1056
        return ret or 0
843
1057
    finally:
844
1058
        # reset, in case we may do other commands later within the same
845
1059
        # process. Commands that want to execute sub-commands must propagate
846
1060
        # --verbose in their own way.
 
1061
        if 'memory' in debug.debug_flags:
 
1062
            trace.debug_memory('Process status after command:', short=False)
847
1063
        option._verbosity_level = saved_verbosity_level
848
1064
 
 
1065
 
849
1066
def display_command(func):
850
1067
    """Decorator that suppresses pipe/interrupt errors."""
851
1068
    def ignore_pipe(*args, **kwargs):
866
1083
    return ignore_pipe
867
1084
 
868
1085
 
869
 
def main(argv):
 
1086
def install_bzr_command_hooks():
 
1087
    """Install the hooks to supply bzr's own commands."""
 
1088
    if _list_bzr_commands in Command.hooks["list_commands"]:
 
1089
        return
 
1090
    Command.hooks.install_named_hook("list_commands", _list_bzr_commands,
 
1091
        "bzr commands")
 
1092
    Command.hooks.install_named_hook("get_command", _get_bzr_command,
 
1093
        "bzr commands")
 
1094
    Command.hooks.install_named_hook("get_command", _get_plugin_command,
 
1095
        "bzr plugin commands")
 
1096
    Command.hooks.install_named_hook("get_command", _get_external_command,
 
1097
        "bzr external command lookup")
 
1098
    Command.hooks.install_named_hook("get_missing_command", _try_plugin_provider,
 
1099
        "bzr plugin-provider-db check")
 
1100
 
 
1101
 
 
1102
def main(argv=None):
 
1103
    """Main entry point of command-line interface.
 
1104
 
 
1105
    :param argv: list of unicode command-line arguments similar to sys.argv.
 
1106
        argv[0] is script name usually, it will be ignored.
 
1107
        Don't pass here sys.argv because this list contains plain strings
 
1108
        and not unicode; pass None instead.
 
1109
 
 
1110
    :return: exit code of bzr command.
 
1111
    """
870
1112
    import bzrlib.ui
871
1113
    bzrlib.ui.ui_factory = bzrlib.ui.make_ui_for_terminal(
872
1114
        sys.stdin, sys.stdout, sys.stderr)
873
1115
 
874
1116
    # Is this a final release version? If so, we should suppress warnings
875
1117
    if bzrlib.version_info[3] == 'final':
876
 
        from bzrlib import symbol_versioning
877
 
        symbol_versioning.suppress_deprecation_warnings(override=False)
878
 
    try:
879
 
        user_encoding = osutils.get_user_encoding()
880
 
        argv = [a.decode(user_encoding) for a in argv[1:]]
881
 
    except UnicodeDecodeError:
882
 
        raise errors.BzrError(("Parameter '%r' is unsupported by the current "
883
 
                                                            "encoding." % a))
 
1118
        suppress_deprecation_warnings(override=True)
 
1119
    if argv is None:
 
1120
        argv = osutils.get_unicode_argv()
 
1121
    else:
 
1122
        new_argv = []
 
1123
        try:
 
1124
            # ensure all arguments are unicode strings
 
1125
            for a in argv[1:]:
 
1126
                if isinstance(a, unicode):
 
1127
                    new_argv.append(a)
 
1128
                else:
 
1129
                    new_argv.append(a.decode('ascii'))
 
1130
        except UnicodeDecodeError:
 
1131
            raise errors.BzrError("argv should be list of unicode strings.")
 
1132
        argv = new_argv
884
1133
    ret = run_bzr_catch_errors(argv)
 
1134
    bzrlib.ui.ui_factory.log_transport_activity(
 
1135
        display=('bytes' in debug.debug_flags))
885
1136
    trace.mutter("return code %d", ret)
 
1137
    osutils.report_extension_load_failures()
886
1138
    return ret
887
1139
 
888
1140
 
889
1141
def run_bzr_catch_errors(argv):
890
 
    # Note: The except clause logic below should be kept in sync with the
891
 
    # profile() routine in lsprof.py.
892
 
    try:
893
 
        return run_bzr(argv)
894
 
    except (KeyboardInterrupt, Exception), e:
895
 
        # used to handle AssertionError and KeyboardInterrupt
896
 
        # specially here, but hopefully they're handled ok by the logger now
897
 
        exc_info = sys.exc_info()
898
 
        exitcode = trace.report_exception(exc_info, sys.stderr)
899
 
        if os.environ.get('BZR_PDB'):
900
 
            print '**** entering debugger'
901
 
            tb = exc_info[2]
902
 
            import pdb
903
 
            if sys.version_info[:2] < (2, 6):
904
 
                # XXX: we want to do
905
 
                #    pdb.post_mortem(tb)
906
 
                # but because pdb.post_mortem gives bad results for tracebacks
907
 
                # from inside generators, we do it manually.
908
 
                # (http://bugs.python.org/issue4150, fixed in Python 2.6)
909
 
                
910
 
                # Setup pdb on the traceback
911
 
                p = pdb.Pdb()
912
 
                p.reset()
913
 
                p.setup(tb.tb_frame, tb)
914
 
                # Point the debugger at the deepest frame of the stack
915
 
                p.curindex = len(p.stack) - 1
916
 
                p.curframe = p.stack[p.curindex]
917
 
                # Start the pdb prompt.
918
 
                p.print_stack_entry(p.stack[p.curindex])
919
 
                p.execRcLines()
920
 
                p.cmdloop()
921
 
            else:
922
 
                pdb.post_mortem(tb)
923
 
        return exitcode
 
1142
    """Run a bzr command with parameters as described by argv.
 
1143
 
 
1144
    This function assumed that that UI layer is setup, that symbol deprecations
 
1145
    are already applied, and that unicode decoding has already been performed on argv.
 
1146
    """
 
1147
    install_bzr_command_hooks()
 
1148
    return exception_to_return_code(run_bzr, argv)
924
1149
 
925
1150
 
926
1151
def run_bzr_catch_user_errors(argv):
929
1154
    This is used for the test suite, and might be useful for other programs
930
1155
    that want to wrap the commandline interface.
931
1156
    """
 
1157
    install_bzr_command_hooks()
932
1158
    try:
933
1159
        return run_bzr(argv)
934
1160
    except Exception, e:
968
1194
 
969
1195
    def plugin_for_command(self, cmd_name):
970
1196
        '''Takes a command and returns the information for that plugin
971
 
        
972
 
        :return: A dictionary with all the available information 
 
1197
 
 
1198
        :return: A dictionary with all the available information
973
1199
        for the requested plugin
974
1200
        '''
975
1201
        raise NotImplementedError