~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Mark Hammond
  • Date: 2009-01-12 01:55:34 UTC
  • mto: (3995.8.2 prepare-1.12)
  • mto: This revision was merged to the branch mainline in revision 4007.
  • Revision ID: mhammond@skippinet.com.au-20090112015534-yfxg50p7mpds9j4v
Include all .html files from the tortoise doc directory.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006, 2008, 2009 Canonical Ltd
 
1
# Copyright (C) 2006, 2008 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
42
41
from bzrlib import (
43
 
    cleanup,
44
42
    debug,
45
43
    errors,
46
44
    option,
47
45
    osutils,
48
46
    trace,
49
 
    ui,
50
47
    win32utils,
51
48
    )
52
49
""")
53
50
 
54
 
from bzrlib.hooks import HookPoint, Hooks
55
 
# Compatibility - Option used to be in commands.
 
51
from bzrlib import registry
 
52
# Compatibility
56
53
from bzrlib.option import Option
57
 
from bzrlib import registry
58
 
from bzrlib.symbol_versioning import (
59
 
    deprecated_function,
60
 
    deprecated_in,
61
 
    deprecated_method,
62
 
    suppress_deprecation_warnings,
63
 
    )
64
54
 
65
55
 
66
56
class CommandInfo(object):
104
94
            registry.Registry.register(self, k_unsquished, cmd,
105
95
                                       override_existing=decorate, info=info)
106
96
        except KeyError:
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__])
 
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__])
112
102
        return previous
113
103
 
114
104
    def register_lazy(self, command_name, aliases, module_name):
141
131
 
142
132
def _builtin_commands():
143
133
    import bzrlib.builtins
144
 
    return _scan_module_for_commands(bzrlib.builtins)
145
 
 
146
 
 
147
 
def _scan_module_for_commands(module):
148
134
    r = {}
149
 
    for name, obj in module.__dict__.iteritems():
 
135
    builtins = bzrlib.builtins.__dict__
 
136
    for name in builtins:
150
137
        if name.startswith("cmd_"):
151
138
            real_name = _unsquish_command_name(name)
152
 
            r[real_name] = obj
 
139
            r[real_name] = builtins[name]
153
140
    return r
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
 
 
 
141
            
174
142
 
175
143
def 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
 
    """
 
144
    """Return list of builtin command names."""
181
145
    return _builtin_commands().keys()
182
 
 
 
146
    
183
147
 
184
148
def plugin_command_names():
185
 
    """Returns command names from commands registered by plugins."""
186
149
    return plugin_cmds.keys()
187
150
 
188
151
 
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
 
    """
 
152
def _get_cmd_dict(plugins_override=True):
 
153
    """Return name->class mapping for all commands."""
199
154
    d = _builtin_commands()
200
155
    if plugins_override:
201
156
        d.update(plugin_cmds.iteritems())
202
 
    for k, v in d.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():
203
163
        yield k,v
204
164
 
205
165
 
206
166
def get_cmd_object(cmd_name, plugins_override=True):
207
 
    """Return the command object for a command.
 
167
    """Return the canonical name and command class for a command.
208
168
 
209
169
    plugins_override
210
170
        If true, plugin commands can override builtins.
216
176
 
217
177
 
218
178
def _get_cmd_object(cmd_name, plugins_override=True):
219
 
    """Get a command object.
 
179
    """Worker for get_cmd_object which raises KeyError rather than BzrCommandError."""
 
180
    from bzrlib.externalcommand import ExternalCommand
220
181
 
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
 
    """
226
182
    # We want only 'ascii' command names, but the user may have typed
227
183
    # in a Unicode name. In that case, they should just get a
228
184
    # 'command not found' error later.
229
185
    # In the future, we may actually support Unicode command names.
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:
 
186
 
 
187
    # first look up this command under the specified name
 
188
    if plugins_override:
271
189
        try:
272
 
            return provider.plugin_for_command(cmd_name), provider
273
 
        except errors.NoPluginAvailable:
 
190
            return plugin_cmds.get(cmd_name)()
 
191
        except KeyError:
274
192
            pass
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()
 
193
    cmds = _get_cmd_dict(plugins_override=False)
281
194
    try:
282
195
        return cmds[cmd_name]()
283
196
    except KeyError:
284
197
        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)()
285
203
    # look for any command which claims this as an alias
286
204
    for real_cmd_name, cmd_class in cmds.iteritems():
287
205
        if cmd_name in cmd_class.aliases:
288
206
            return cmd_class()
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
 
207
 
298
208
    cmd_obj = ExternalCommand.find_command(cmd_name)
299
209
    if cmd_obj:
300
210
        return cmd_obj
301
211
 
 
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)
302
221
 
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
 
222
    raise KeyError
314
223
 
315
224
 
316
225
class Command(object):
370
279
            sys.stdout is forced to be a binary stream, and line-endings
371
280
            will not mangled.
372
281
 
373
 
    :cvar hooks: An instance of CommandHooks.
374
282
    """
375
283
    aliases = []
376
284
    takes_args = []
378
286
    encoding_type = 'strict'
379
287
 
380
288
    hidden = False
381
 
 
 
289
    
382
290
    def __init__(self):
383
291
        """Construct an instance of this command."""
384
292
        if self.__doc__ == Command.__doc__:
385
293
            warn("No help message set for %r" % self)
386
294
        # List of standard options directly supported
387
295
        self.supported_std_options = []
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)))
 
296
 
411
297
    def _maybe_expand_globs(self, file_list):
412
298
        """Glob expand file_list if the platform does not do that itself.
413
 
 
414
 
        Not used anymore, now that the bzr command-line parser globs on
415
 
        Windows.
416
 
 
 
299
        
417
300
        :return: A possibly empty list of unicode paths.
418
301
 
419
302
        Introduced in bzrlib 0.18.
420
303
        """
421
 
        return file_list
 
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)
422
309
 
423
310
    def _usage(self):
424
311
        """Return single-line grammar for this command.
439
326
        return s
440
327
 
441
328
    def get_help_text(self, additional_see_also=None, plain=True,
442
 
                      see_also_as_links=False, verbose=True):
 
329
                      see_also_as_links=False):
443
330
        """Return a text string with help for this command.
444
 
 
 
331
        
445
332
        :param additional_see_also: Additional help topics to be
446
333
            cross-referenced.
447
334
        :param plain: if False, raw help (reStructuredText) is
448
335
            returned instead of plain text.
449
336
        :param see_also_as_links: if True, convert items in 'See also'
450
337
            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.
455
338
        """
456
339
        doc = self.help()
457
340
        if doc is None:
458
341
            raise NotImplementedError("sorry, no detailed help yet for %r" % self.name())
459
342
 
460
343
        # Extract the summary (purpose) and sections out from the text
461
 
        purpose,sections,order = self._get_help_parts(doc)
 
344
        purpose,sections = self._get_help_parts(doc)
462
345
 
463
346
        # If a custom usage section was provided, use it
464
347
        if sections.has_key('Usage'):
476
359
        result += '\n'
477
360
 
478
361
        # 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
483
362
        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)
491
363
        if options.startswith('Options:'):
492
364
            result += ':' + options
493
365
        elif options.startswith('options:'):
497
369
            result += options
498
370
        result += '\n'
499
371
 
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)
 
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)
507
378
 
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())
 
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])
518
385
 
519
386
        # Add the aliases, source (plug-in) and see also links, if any
520
387
        if self.aliases:
533
400
                        # so don't create a real link
534
401
                        see_also_links.append(item)
535
402
                    else:
536
 
                        # Use a Sphinx link for this entry
537
 
                        link_text = ":doc:`%s <%s-help>`" % (item, item)
538
 
                        see_also_links.append(link_text)
 
403
                        # Use a reST link for this entry
 
404
                        see_also_links.append("`%s`_" % (item,))
539
405
                see_also = see_also_links
540
406
            result += ':See also: '
541
407
            result += ', '.join(see_also) + '\n'
550
416
    def _get_help_parts(text):
551
417
        """Split help text into a summary and named sections.
552
418
 
553
 
        :return: (summary,sections,order) where summary is the top line and
 
419
        :return: (summary,sections) where summary is the top line and
554
420
            sections is a dictionary of the rest indexed by section name.
555
 
            order is the order the section appear in the text.
556
421
            A section starts with a heading line of the form ":xxx:".
557
422
            Indented text on following lines is the section value.
558
423
            All text found outside a named section is assigned to the
559
424
            default section which is given the key of None.
560
425
        """
561
 
        def save_section(sections, order, label, section):
 
426
        def save_section(sections, label, section):
562
427
            if len(section) > 0:
563
428
                if sections.has_key(label):
564
429
                    sections[label] += '\n' + section
565
430
                else:
566
 
                    order.append(label)
567
431
                    sections[label] = section
568
432
 
569
433
        lines = text.rstrip().splitlines()
570
434
        summary = lines.pop(0)
571
435
        sections = {}
572
 
        order = []
573
436
        label,section = None,''
574
437
        for line in lines:
575
438
            if line.startswith(':') and line.endswith(':') and len(line) > 2:
576
 
                save_section(sections, order, label, section)
 
439
                save_section(sections, label, section)
577
440
                label,section = line[1:-1],''
578
441
            elif (label is not None) and len(line) > 1 and not line[0].isspace():
579
 
                save_section(sections, order, label, section)
 
442
                save_section(sections, label, section)
580
443
                label,section = None,line
581
444
            else:
582
445
                if len(section) > 0:
583
446
                    section += '\n' + line
584
447
                else:
585
448
                    section = line
586
 
        save_section(sections, order, label, section)
587
 
        return summary, sections, order
 
449
        save_section(sections, label, section)
 
450
        return summary, sections
588
451
 
589
452
    def get_help_topic(self):
590
453
        """Return the commands help topic - its name."""
592
455
 
593
456
    def get_see_also(self, additional_terms=None):
594
457
        """Return a list of help topics that are related to this command.
595
 
 
 
458
        
596
459
        The list is derived from the content of the _see_also attribute. Any
597
460
        duplicates are removed and the result is in lexical order.
598
461
        :param additional_terms: Additional help topics to cross-reference.
619
482
 
620
483
    def _setup_outf(self):
621
484
        """Return a file linked to stdout, which has proper encoding."""
622
 
        self.outf = ui.ui_factory.make_output_stream(
623
 
            encoding_type=self.encoding_type)
 
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
624
505
 
625
506
    def run_argv_aliases(self, argv, alias_argv=None):
626
507
        """Parse the command line and run with extra aliases in alias_argv."""
634
515
        if 'help' in opts:  # e.g. bzr add --help
635
516
            sys.stdout.write(self.get_help_text())
636
517
            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
640
518
        trace.set_verbosity_level(option._verbosity_level)
641
519
        if 'verbose' in self.supported_std_options:
642
520
            opts['verbose'] = trace.is_verbose()
658
536
 
659
537
        self._setup_outf()
660
538
 
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)
 
539
        return self.run(**all_cmd_args)
666
540
 
667
541
    def run(self):
668
542
        """Actually run the command.
699
573
            return None
700
574
 
701
575
 
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
 
 
739
576
def parse_args(command, argv, alias_argv=None):
740
577
    """Parse command line.
741
 
 
 
578
    
742
579
    Arguments and options are parsed at this level before being passed
743
580
    down to specific command handlers.  This routine knows, from a
744
581
    lookup table, something about the available options, what optargs
793
630
                               % (cmd, argname.upper()))
794
631
            else:
795
632
                argdict[argname] = args.pop(0)
796
 
 
 
633
            
797
634
    if args:
798
635
        raise errors.BzrCommandError("extra argument to command %s: %s"
799
636
                                     % (cmd, args[0]))
807
644
 
808
645
    tracer = trace.Trace(count=1, trace=0)
809
646
    sys.settrace(tracer.globaltrace)
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)
 
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)
819
654
 
820
655
 
821
656
def apply_profiled(the_callable, *args, **kwargs):
826
661
    try:
827
662
        prof = hotshot.Profile(pfname)
828
663
        try:
829
 
            ret = prof.runcall(exception_to_return_code, the_callable, *args,
830
 
                **kwargs) or 0
 
664
            ret = prof.runcall(the_callable, *args, **kwargs) or 0
831
665
        finally:
832
666
            prof.close()
833
667
        stats = hotshot.stats.load(pfname)
842
676
        os.remove(pfname)
843
677
 
844
678
 
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
 
 
886
679
def apply_lsprofiled(filename, the_callable, *args, **kwargs):
887
680
    from bzrlib.lsprof import profile
888
 
    ret, stats = profile(exception_to_return_code, the_callable, *args, **kwargs)
 
681
    ret, stats = profile(the_callable, *args, **kwargs)
889
682
    stats.sort()
890
683
    if filename is None:
891
684
        stats.pprint()
926
719
       The command-line arguments, without the program name from argv[0]
927
720
       These should already be decoded. All library/test code calling
928
721
       run_bzr should be passing valid strings (don't need decoding).
929
 
 
 
722
    
930
723
    Returns a command status or raises an exception.
931
724
 
932
725
    Special master options: these must come before the command because
950
743
 
951
744
    --coverage
952
745
        Generate line coverage report in the specified directory.
953
 
 
954
 
    --concurrency
955
 
        Specify the number of processes that can be run concurrently (selftest).
956
746
    """
957
 
    trace.mutter("bazaar version: " + bzrlib.__version__)
958
747
    argv = list(argv)
959
748
    trace.mutter("bzr arguments: %r", argv)
960
749
 
984
773
            opt_no_aliases = True
985
774
        elif a == '--builtin':
986
775
            opt_builtin = True
987
 
        elif a == '--concurrency':
988
 
            os.environ['BZR_CONCURRENCY'] = argv[i + 1]
989
 
            i += 1
990
776
        elif a == '--coverage':
991
777
            opt_coverage_dir = argv[i + 1]
992
778
            i += 1
996
782
            argv_copy.append(a)
997
783
        i += 1
998
784
 
999
 
    debug.set_debug_flags_from_config()
1000
 
 
1001
785
    argv = argv_copy
1002
786
    if (not argv):
1003
787
        from bzrlib.builtins import cmd_help
1053
837
            ret = apply_coveraged(opt_coverage_dir, run, *run_argv)
1054
838
        else:
1055
839
            ret = run(*run_argv)
 
840
        if 'memory' in debug.debug_flags:
 
841
            trace.debug_memory('Process status after command:', short=False)
1056
842
        return ret or 0
1057
843
    finally:
1058
844
        # reset, in case we may do other commands later within the same
1059
845
        # process. Commands that want to execute sub-commands must propagate
1060
846
        # --verbose in their own way.
1061
 
        if 'memory' in debug.debug_flags:
1062
 
            trace.debug_memory('Process status after command:', short=False)
1063
847
        option._verbosity_level = saved_verbosity_level
1064
848
 
1065
 
 
1066
849
def display_command(func):
1067
850
    """Decorator that suppresses pipe/interrupt errors."""
1068
851
    def ignore_pipe(*args, **kwargs):
1083
866
    return ignore_pipe
1084
867
 
1085
868
 
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
 
    """
 
869
def main(argv):
1112
870
    import bzrlib.ui
1113
 
    bzrlib.ui.ui_factory = bzrlib.ui.make_ui_for_terminal(
1114
 
        sys.stdin, sys.stdout, sys.stderr)
 
871
    from bzrlib.ui.text import TextUIFactory
 
872
    bzrlib.ui.ui_factory = TextUIFactory()
1115
873
 
1116
874
    # Is this a final release version? If so, we should suppress warnings
1117
875
    if bzrlib.version_info[3] == 'final':
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
 
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))
1133
884
    ret = run_bzr_catch_errors(argv)
1134
 
    bzrlib.ui.ui_factory.log_transport_activity(
1135
 
        display=('bytes' in debug.debug_flags))
1136
885
    trace.mutter("return code %d", ret)
1137
 
    osutils.report_extension_load_failures()
1138
886
    return ret
1139
887
 
1140
888
 
1141
889
def run_bzr_catch_errors(argv):
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)
 
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
        exitcode = trace.report_exception(sys.exc_info(), sys.stderr)
 
898
        if os.environ.get('BZR_PDB'):
 
899
            print '**** entering debugger'
 
900
            import pdb
 
901
            pdb.post_mortem(sys.exc_traceback)
 
902
        return exitcode
1149
903
 
1150
904
 
1151
905
def run_bzr_catch_user_errors(argv):
1154
908
    This is used for the test suite, and might be useful for other programs
1155
909
    that want to wrap the commandline interface.
1156
910
    """
1157
 
    install_bzr_command_hooks()
1158
911
    try:
1159
912
        return run_bzr(argv)
1160
913
    except Exception, e:
1194
947
 
1195
948
    def plugin_for_command(self, cmd_name):
1196
949
        '''Takes a command and returns the information for that plugin
1197
 
 
1198
 
        :return: A dictionary with all the available information
 
950
        
 
951
        :return: A dictionary with all the available information 
1199
952
        for the requested plugin
1200
953
        '''
1201
954
        raise NotImplementedError