/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/commands.py

  • Committer: Jelmer Vernooij
  • Date: 2020-03-22 01:35:14 UTC
  • mfrom: (7490.7.6 work)
  • mto: This revision was merged to the branch mainline in revision 7499.
  • Revision ID: jelmer@jelmer.uk-20200322013514-7vw1ntwho04rcuj3
merge lp:brz/3.1.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005-2011 Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
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
 
16
 
 
17
# TODO: Define arguments by objects, rather than just using names.
 
18
# Those objects can specify the expected type of the argument, which
 
19
# would help with validation and shell completion.  They could also provide
 
20
# help/explanation for that argument in a structured way.
 
21
 
 
22
# TODO: Specific "examples" property on commands for consistent formatting.
 
23
 
 
24
import contextlib
 
25
import os
 
26
import sys
 
27
 
 
28
from . import (
 
29
    i18n,
 
30
    option,
 
31
    osutils,
 
32
    )
 
33
 
 
34
from .lazy_import import lazy_import
 
35
lazy_import(globals(), """
 
36
import errno
 
37
 
 
38
import breezy
 
39
from breezy import (
 
40
    cmdline,
 
41
    debug,
 
42
    trace,
 
43
    ui,
 
44
    )
 
45
""")
 
46
 
 
47
from .hooks import Hooks
 
48
from .i18n import gettext
 
49
# Compatibility - Option used to be in commands.
 
50
from .option import Option
 
51
from .plugin import disable_plugins, load_plugins, plugin_name
 
52
from . import errors, registry
 
53
 
 
54
 
 
55
class BzrOptionError(errors.BzrCommandError):
 
56
 
 
57
    _fmt = "Error in command line options"
 
58
 
 
59
 
 
60
class CommandAvailableInPlugin(Exception):
 
61
 
 
62
    internal_error = False
 
63
 
 
64
    def __init__(self, cmd_name, plugin_metadata, provider):
 
65
 
 
66
        self.plugin_metadata = plugin_metadata
 
67
        self.cmd_name = cmd_name
 
68
        self.provider = provider
 
69
 
 
70
    def __str__(self):
 
71
 
 
72
        _fmt = ('"%s" is not a standard brz command. \n'
 
73
                'However, the following official plugin provides this command: %s\n'
 
74
                'You can install it by going to: %s'
 
75
                % (self.cmd_name, self.plugin_metadata['name'],
 
76
                    self.plugin_metadata['url']))
 
77
 
 
78
        return _fmt
 
79
 
 
80
 
 
81
class CommandInfo(object):
 
82
    """Information about a command."""
 
83
 
 
84
    def __init__(self, aliases):
 
85
        """The list of aliases for the command."""
 
86
        self.aliases = aliases
 
87
 
 
88
    @classmethod
 
89
    def from_command(klass, command):
 
90
        """Factory to construct a CommandInfo from a command."""
 
91
        return klass(command.aliases)
 
92
 
 
93
 
 
94
class CommandRegistry(registry.Registry):
 
95
    """Special registry mapping command names to command classes.
 
96
 
 
97
    :ivar overridden_registry: Look in this registry for commands being
 
98
        overridden by this registry.  This can be used to tell plugin commands
 
99
        about the builtin they're decorating.
 
100
    """
 
101
 
 
102
    def __init__(self):
 
103
        registry.Registry.__init__(self)
 
104
        self.overridden_registry = None
 
105
        # map from aliases to the real command that implements the name
 
106
        self._alias_dict = {}
 
107
 
 
108
    def get(self, command_name):
 
109
        real_name = self._alias_dict.get(command_name, command_name)
 
110
        return registry.Registry.get(self, real_name)
 
111
 
 
112
    @staticmethod
 
113
    def _get_name(command_name):
 
114
        if command_name.startswith("cmd_"):
 
115
            return _unsquish_command_name(command_name)
 
116
        else:
 
117
            return command_name
 
118
 
 
119
    def register(self, cmd, decorate=False):
 
120
        """Utility function to help register a command
 
121
 
 
122
        :param cmd: Command subclass to register
 
123
        :param decorate: If true, allow overriding an existing command
 
124
            of the same name; the old command is returned by this function.
 
125
            Otherwise it is an error to try to override an existing command.
 
126
        """
 
127
        k = cmd.__name__
 
128
        k_unsquished = self._get_name(k)
 
129
        try:
 
130
            previous = self.get(k_unsquished)
 
131
        except KeyError:
 
132
            previous = None
 
133
            if self.overridden_registry:
 
134
                try:
 
135
                    previous = self.overridden_registry.get(k_unsquished)
 
136
                except KeyError:
 
137
                    pass
 
138
        info = CommandInfo.from_command(cmd)
 
139
        try:
 
140
            registry.Registry.register(self, k_unsquished, cmd,
 
141
                                       override_existing=decorate, info=info)
 
142
        except KeyError:
 
143
            trace.warning('Two plugins defined the same command: %r' % k)
 
144
            trace.warning('Not loading the one in %r' %
 
145
                          sys.modules[cmd.__module__])
 
146
            trace.warning('Previously this command was registered from %r' %
 
147
                          sys.modules[previous.__module__])
 
148
        for a in cmd.aliases:
 
149
            self._alias_dict[a] = k_unsquished
 
150
        return previous
 
151
 
 
152
    def register_lazy(self, command_name, aliases, module_name):
 
153
        """Register a command without loading its module.
 
154
 
 
155
        :param command_name: The primary name of the command.
 
156
        :param aliases: A list of aliases for the command.
 
157
        :module_name: The module that the command lives in.
 
158
        """
 
159
        key = self._get_name(command_name)
 
160
        registry.Registry.register_lazy(self, key, module_name, command_name,
 
161
                                        info=CommandInfo(aliases))
 
162
        for a in aliases:
 
163
            self._alias_dict[a] = key
 
164
 
 
165
 
 
166
plugin_cmds = CommandRegistry()
 
167
builtin_command_registry = CommandRegistry()
 
168
plugin_cmds.overridden_registry = builtin_command_registry
 
169
 
 
170
 
 
171
def register_command(cmd, decorate=False):
 
172
    """Register a plugin command.
 
173
 
 
174
    Should generally be avoided in favor of lazy registration.
 
175
    """
 
176
    global plugin_cmds
 
177
    return plugin_cmds.register(cmd, decorate)
 
178
 
 
179
 
 
180
def _squish_command_name(cmd):
 
181
    return 'cmd_' + cmd.replace('-', '_')
 
182
 
 
183
 
 
184
def _unsquish_command_name(cmd):
 
185
    return cmd[4:].replace('_', '-')
 
186
 
 
187
 
 
188
def _register_builtin_commands():
 
189
    if builtin_command_registry.keys():
 
190
        # only load once
 
191
        return
 
192
    import breezy.builtins
 
193
    for cmd_class in _scan_module_for_commands(breezy.builtins):
 
194
        builtin_command_registry.register(cmd_class)
 
195
    breezy.builtins._register_lazy_builtins()
 
196
 
 
197
 
 
198
def _scan_module_for_commands(module):
 
199
    module_dict = module.__dict__
 
200
    for name in module_dict:
 
201
        if name.startswith("cmd_"):
 
202
            yield module_dict[name]
 
203
 
 
204
 
 
205
def _list_bzr_commands(names):
 
206
    """Find commands from bzr's core and plugins.
 
207
 
 
208
    This is not the public interface, just the default hook called by
 
209
    all_command_names.
 
210
    """
 
211
    # to eliminate duplicates
 
212
    names.update(builtin_command_names())
 
213
    names.update(plugin_command_names())
 
214
    return names
 
215
 
 
216
 
 
217
def all_command_names():
 
218
    """Return a set of all command names."""
 
219
    names = set()
 
220
    for hook in Command.hooks['list_commands']:
 
221
        names = hook(names)
 
222
        if names is None:
 
223
            raise AssertionError(
 
224
                'hook %s returned None' % Command.hooks.get_hook_name(hook))
 
225
    return names
 
226
 
 
227
 
 
228
def builtin_command_names():
 
229
    """Return list of builtin command names.
 
230
 
 
231
    Use of all_command_names() is encouraged rather than builtin_command_names
 
232
    and/or plugin_command_names.
 
233
    """
 
234
    _register_builtin_commands()
 
235
    return builtin_command_registry.keys()
 
236
 
 
237
 
 
238
def plugin_command_names():
 
239
    """Returns command names from commands registered by plugins."""
 
240
    return plugin_cmds.keys()
 
241
 
 
242
 
 
243
# Overrides for common mispellings that heuristics get wrong
 
244
_GUESS_OVERRIDES = {
 
245
    'ic': {'ci': 0},  # heuristic finds nick
 
246
    }
 
247
 
 
248
 
 
249
def guess_command(cmd_name):
 
250
    """Guess what command a user typoed.
 
251
 
 
252
    :param cmd_name: Command to search for
 
253
    :return: None if no command was found, name of a command otherwise
 
254
    """
 
255
    names = set()
 
256
    for name in all_command_names():
 
257
        names.add(name)
 
258
        cmd = get_cmd_object(name)
 
259
        names.update(cmd.aliases)
 
260
    # candidate: modified levenshtein distance against cmd_name.
 
261
    costs = {}
 
262
    import patiencediff
 
263
    for name in sorted(names):
 
264
        matcher = patiencediff.PatienceSequenceMatcher(None, cmd_name, name)
 
265
        distance = 0.0
 
266
        opcodes = matcher.get_opcodes()
 
267
        for opcode, l1, l2, r1, r2 in opcodes:
 
268
            if opcode == 'delete':
 
269
                distance += l2 - l1
 
270
            elif opcode == 'replace':
 
271
                distance += max(l2 - l1, r2 - l1)
 
272
            elif opcode == 'insert':
 
273
                distance += r2 - r1
 
274
            elif opcode == 'equal':
 
275
                # Score equal ranges lower, making similar commands of equal
 
276
                # length closer than arbitrary same length commands.
 
277
                distance -= 0.1 * (l2 - l1)
 
278
        costs[name] = distance
 
279
    costs.update(_GUESS_OVERRIDES.get(cmd_name, {}))
 
280
    costs = sorted((costs[key], key) for key in costs)
 
281
    if not costs:
 
282
        return
 
283
    if costs[0][0] > 4:
 
284
        return
 
285
    candidate = costs[0][1]
 
286
    return candidate
 
287
 
 
288
 
 
289
def get_cmd_object(cmd_name, plugins_override=True):
 
290
    """Return the command object for a command.
 
291
 
 
292
    plugins_override
 
293
        If true, plugin commands can override builtins.
 
294
    """
 
295
    try:
 
296
        return _get_cmd_object(cmd_name, plugins_override)
 
297
    except KeyError:
 
298
        # No command found, see if this was a typo
 
299
        candidate = guess_command(cmd_name)
 
300
        if candidate is not None:
 
301
            raise errors.BzrCommandError(
 
302
                gettext('unknown command "%s". Perhaps you meant "%s"')
 
303
                % (cmd_name, candidate))
 
304
        raise errors.BzrCommandError(gettext('unknown command "%s"')
 
305
                                     % cmd_name)
 
306
 
 
307
 
 
308
def _get_cmd_object(cmd_name, plugins_override=True, check_missing=True):
 
309
    """Get a command object.
 
310
 
 
311
    :param cmd_name: The name of the command.
 
312
    :param plugins_override: Allow plugins to override builtins.
 
313
    :param check_missing: Look up commands not found in the regular index via
 
314
        the get_missing_command hook.
 
315
    :return: A Command object instance
 
316
    :raises KeyError: If no command is found.
 
317
    """
 
318
    # We want only 'ascii' command names, but the user may have typed
 
319
    # in a Unicode name. In that case, they should just get a
 
320
    # 'command not found' error later.
 
321
    # In the future, we may actually support Unicode command names.
 
322
    cmd = None
 
323
    # Get a command
 
324
    for hook in Command.hooks['get_command']:
 
325
        cmd = hook(cmd, cmd_name)
 
326
        if cmd is not None and not plugins_override and not cmd.plugin_name():
 
327
            # We've found a non-plugin command, don't permit it to be
 
328
            # overridden.
 
329
            break
 
330
    if cmd is None and check_missing:
 
331
        for hook in Command.hooks['get_missing_command']:
 
332
            cmd = hook(cmd_name)
 
333
            if cmd is not None:
 
334
                break
 
335
    if cmd is None:
 
336
        # No command found.
 
337
        raise KeyError
 
338
    # Allow plugins to extend commands
 
339
    for hook in Command.hooks['extend_command']:
 
340
        hook(cmd)
 
341
    if getattr(cmd, 'invoked_as', None) is None:
 
342
        cmd.invoked_as = cmd_name
 
343
    return cmd
 
344
 
 
345
 
 
346
class NoPluginAvailable(errors.BzrError):
 
347
    pass
 
348
 
 
349
 
 
350
def _try_plugin_provider(cmd_name):
 
351
    """Probe for a plugin provider having cmd_name."""
 
352
    try:
 
353
        plugin_metadata, provider = probe_for_provider(cmd_name)
 
354
        raise CommandAvailableInPlugin(cmd_name, plugin_metadata, provider)
 
355
    except NoPluginAvailable:
 
356
        pass
 
357
 
 
358
 
 
359
def probe_for_provider(cmd_name):
 
360
    """Look for a provider for cmd_name.
 
361
 
 
362
    :param cmd_name: The command name.
 
363
    :return: plugin_metadata, provider for getting cmd_name.
 
364
    :raises NoPluginAvailable: When no provider can supply the plugin.
 
365
    """
 
366
    # look for providers that provide this command but aren't installed
 
367
    for provider in command_providers_registry:
 
368
        try:
 
369
            return provider.plugin_for_command(cmd_name), provider
 
370
        except NoPluginAvailable:
 
371
            pass
 
372
    raise NoPluginAvailable(cmd_name)
 
373
 
 
374
 
 
375
def _get_bzr_command(cmd_or_None, cmd_name):
 
376
    """Get a command from bzr's core."""
 
377
    try:
 
378
        cmd_class = builtin_command_registry.get(cmd_name)
 
379
    except KeyError:
 
380
        pass
 
381
    else:
 
382
        return cmd_class()
 
383
    return cmd_or_None
 
384
 
 
385
 
 
386
def _get_external_command(cmd_or_None, cmd_name):
 
387
    """Lookup a command that is a shell script."""
 
388
    # Only do external command lookups when no command is found so far.
 
389
    if cmd_or_None is not None:
 
390
        return cmd_or_None
 
391
    from breezy.externalcommand import ExternalCommand
 
392
    cmd_obj = ExternalCommand.find_command(cmd_name)
 
393
    if cmd_obj:
 
394
        return cmd_obj
 
395
 
 
396
 
 
397
def _get_plugin_command(cmd_or_None, cmd_name):
 
398
    """Get a command from brz's plugins."""
 
399
    try:
 
400
        return plugin_cmds.get(cmd_name)()
 
401
    except KeyError:
 
402
        pass
 
403
    for key in plugin_cmds.keys():
 
404
        info = plugin_cmds.get_info(key)
 
405
        if cmd_name in info.aliases:
 
406
            return plugin_cmds.get(key)()
 
407
    return cmd_or_None
 
408
 
 
409
 
 
410
class Command(object):
 
411
    """Base class for commands.
 
412
 
 
413
    Commands are the heart of the command-line brz interface.
 
414
 
 
415
    The command object mostly handles the mapping of command-line
 
416
    parameters into one or more breezy operations, and of the results
 
417
    into textual output.
 
418
 
 
419
    Commands normally don't have any state.  All their arguments are
 
420
    passed in to the run method.  (Subclasses may take a different
 
421
    policy if the behaviour of the instance needs to depend on e.g. a
 
422
    shell plugin and not just its Python class.)
 
423
 
 
424
    The docstring for an actual command should give a single-line
 
425
    summary, then a complete description of the command.  A grammar
 
426
    description will be inserted.
 
427
 
 
428
    :cvar aliases: Other accepted names for this command.
 
429
 
 
430
    :cvar takes_args: List of argument forms, marked with whether they are
 
431
        optional, repeated, etc.  Examples::
 
432
 
 
433
            ['to_location', 'from_branch?', 'file*']
 
434
 
 
435
        * 'to_location' is required
 
436
        * 'from_branch' is optional
 
437
        * 'file' can be specified 0 or more times
 
438
 
 
439
    :cvar takes_options: List of options that may be given for this command.
 
440
        These can be either strings, referring to globally-defined options, or
 
441
        option objects.  Retrieve through options().
 
442
 
 
443
    :cvar hidden: If true, this command isn't advertised.  This is typically
 
444
        for commands intended for expert users.
 
445
 
 
446
    :cvar encoding_type: Command objects will get a 'outf' attribute, which has
 
447
        been setup to properly handle encoding of unicode strings.
 
448
        encoding_type determines what will happen when characters cannot be
 
449
        encoded:
 
450
 
 
451
        * strict - abort if we cannot decode
 
452
        * replace - put in a bogus character (typically '?')
 
453
        * exact - do not encode sys.stdout
 
454
 
 
455
        NOTE: by default on Windows, sys.stdout is opened as a text stream,
 
456
        therefore LF line-endings are converted to CRLF.  When a command uses
 
457
        encoding_type = 'exact', then sys.stdout is forced to be a binary
 
458
        stream, and line-endings will not mangled.
 
459
 
 
460
    :cvar invoked_as:
 
461
        A string indicating the real name under which this command was
 
462
        invoked, before expansion of aliases.
 
463
        (This may be None if the command was constructed and run in-process.)
 
464
 
 
465
    :cvar hooks: An instance of CommandHooks.
 
466
 
 
467
    :cvar __doc__: The help shown by 'brz help command' for this command.
 
468
        This is set by assigning explicitly to __doc__ so that -OO can
 
469
        be used::
 
470
 
 
471
            class Foo(Command):
 
472
                __doc__ = "My help goes here"
 
473
    """
 
474
    aliases = []
 
475
    takes_args = []
 
476
    takes_options = []
 
477
    encoding_type = 'strict'
 
478
    invoked_as = None
 
479
    l10n = True
 
480
 
 
481
    hidden = False
 
482
 
 
483
    def __init__(self):
 
484
        """Construct an instance of this command."""
 
485
        # List of standard options directly supported
 
486
        self.supported_std_options = []
 
487
        self._setup_run()
 
488
 
 
489
    def add_cleanup(self, cleanup_func, *args, **kwargs):
 
490
        """Register a function to call after self.run returns or raises.
 
491
 
 
492
        Functions will be called in LIFO order.
 
493
        """
 
494
        self._exit_stack.callback(cleanup_func, *args, **kwargs)
 
495
 
 
496
    def cleanup_now(self):
 
497
        """Execute and empty pending cleanup functions immediately.
 
498
 
 
499
        After cleanup_now all registered cleanups are forgotten.  add_cleanup
 
500
        may be called again after cleanup_now; these cleanups will be called
 
501
        after self.run returns or raises (or when cleanup_now is next called).
 
502
 
 
503
        This is useful for releasing expensive or contentious resources (such
 
504
        as write locks) before doing further work that does not require those
 
505
        resources (such as writing results to self.outf). Note though, that
 
506
        as it releases all resources, this may release locks that the command
 
507
        wants to hold, so use should be done with care.
 
508
        """
 
509
        self._exit_stack.close()
 
510
 
 
511
    def enter_context(self, cm):
 
512
        return self._exit_stack.enter_context(cm)
 
513
 
 
514
    def _usage(self):
 
515
        """Return single-line grammar for this command.
 
516
 
 
517
        Only describes arguments, not options.
 
518
        """
 
519
        s = 'brz ' + self.name() + ' '
 
520
        for aname in self.takes_args:
 
521
            aname = aname.upper()
 
522
            if aname[-1] in ['$', '+']:
 
523
                aname = aname[:-1] + '...'
 
524
            elif aname[-1] == '?':
 
525
                aname = '[' + aname[:-1] + ']'
 
526
            elif aname[-1] == '*':
 
527
                aname = '[' + aname[:-1] + '...]'
 
528
            s += aname + ' '
 
529
        s = s[:-1]      # remove last space
 
530
        return s
 
531
 
 
532
    def get_help_text(self, additional_see_also=None, plain=True,
 
533
                      see_also_as_links=False, verbose=True):
 
534
        """Return a text string with help for this command.
 
535
 
 
536
        :param additional_see_also: Additional help topics to be
 
537
            cross-referenced.
 
538
        :param plain: if False, raw help (reStructuredText) is
 
539
            returned instead of plain text.
 
540
        :param see_also_as_links: if True, convert items in 'See also'
 
541
            list to internal links (used by bzr_man rstx generator)
 
542
        :param verbose: if True, display the full help, otherwise
 
543
            leave out the descriptive sections and just display
 
544
            usage help (e.g. Purpose, Usage, Options) with a
 
545
            message explaining how to obtain full help.
 
546
        """
 
547
        if self.l10n:
 
548
            i18n.install()  # Install i18n only for get_help_text for now.
 
549
        doc = self.help()
 
550
        if doc:
 
551
            # Note: If self.gettext() translates ':Usage:\n', the section will
 
552
            # be shown after "Description" section and we don't want to
 
553
            # translate the usage string.
 
554
            # Though, brz export-pot don't exports :Usage: section and it must
 
555
            # not be translated.
 
556
            doc = self.gettext(doc)
 
557
        else:
 
558
            doc = gettext("No help for this command.")
 
559
 
 
560
        # Extract the summary (purpose) and sections out from the text
 
561
        purpose, sections, order = self._get_help_parts(doc)
 
562
 
 
563
        # If a custom usage section was provided, use it
 
564
        if 'Usage' in sections:
 
565
            usage = sections.pop('Usage')
 
566
        else:
 
567
            usage = self._usage()
 
568
 
 
569
        # The header is the purpose and usage
 
570
        result = ""
 
571
        result += gettext(':Purpose: %s\n') % (purpose,)
 
572
        if usage.find('\n') >= 0:
 
573
            result += gettext(':Usage:\n%s\n') % (usage,)
 
574
        else:
 
575
            result += gettext(':Usage:   %s\n') % (usage,)
 
576
        result += '\n'
 
577
 
 
578
        # Add the options
 
579
        #
 
580
        # XXX: optparse implicitly rewraps the help, and not always perfectly,
 
581
        # so we get <https://bugs.launchpad.net/bzr/+bug/249908>.  -- mbp
 
582
        # 20090319
 
583
        parser = option.get_optparser(
 
584
            [v for k, v in sorted(self.options().items())])
 
585
        options = parser.format_option_help()
 
586
        # FIXME: According to the spec, ReST option lists actually don't
 
587
        # support options like --1.14 so that causes syntax errors (in Sphinx
 
588
        # at least).  As that pattern always appears in the commands that
 
589
        # break, we trap on that and then format that block of 'format' options
 
590
        # as a literal block. We use the most recent format still listed so we
 
591
        # don't have to do that too often -- vila 20110514
 
592
        if not plain and options.find('  --1.14  ') != -1:
 
593
            options = options.replace(' format:\n', ' format::\n\n', 1)
 
594
        if options.startswith('Options:'):
 
595
            result += gettext(':Options:%s') % (options[len('options:'):],)
 
596
        else:
 
597
            result += options
 
598
        result += '\n'
 
599
 
 
600
        if verbose:
 
601
            # Add the description, indenting it 2 spaces
 
602
            # to match the indentation of the options
 
603
            if None in sections:
 
604
                text = sections.pop(None)
 
605
                text = '\n  '.join(text.splitlines())
 
606
                result += gettext(':Description:\n  %s\n\n') % (text,)
 
607
 
 
608
            # Add the custom sections (e.g. Examples). Note that there's no need
 
609
            # to indent these as they must be indented already in the source.
 
610
            if sections:
 
611
                for label in order:
 
612
                    if label in sections:
 
613
                        result += ':%s:\n%s\n' % (label, sections[label])
 
614
                result += '\n'
 
615
        else:
 
616
            result += (gettext("See brz help %s for more details and examples.\n\n")
 
617
                       % self.name())
 
618
 
 
619
        # Add the aliases, source (plug-in) and see also links, if any
 
620
        if self.aliases:
 
621
            result += gettext(':Aliases:  ')
 
622
            result += ', '.join(self.aliases) + '\n'
 
623
        plugin_name = self.plugin_name()
 
624
        if plugin_name is not None:
 
625
            result += gettext(':From:     plugin "%s"\n') % plugin_name
 
626
        see_also = self.get_see_also(additional_see_also)
 
627
        if see_also:
 
628
            if not plain and see_also_as_links:
 
629
                see_also_links = []
 
630
                for item in see_also:
 
631
                    if item == 'topics':
 
632
                        # topics doesn't have an independent section
 
633
                        # so don't create a real link
 
634
                        see_also_links.append(item)
 
635
                    else:
 
636
                        # Use a Sphinx link for this entry
 
637
                        link_text = gettext(":doc:`{0} <{1}-help>`").format(
 
638
                            item, item)
 
639
                        see_also_links.append(link_text)
 
640
                see_also = see_also_links
 
641
            result += gettext(':See also: %s') % ', '.join(see_also) + '\n'
 
642
 
 
643
        # If this will be rendered as plain text, convert it
 
644
        if plain:
 
645
            import breezy.help_topics
 
646
            result = breezy.help_topics.help_as_plain_text(result)
 
647
        return result
 
648
 
 
649
    @staticmethod
 
650
    def _get_help_parts(text):
 
651
        """Split help text into a summary and named sections.
 
652
 
 
653
        :return: (summary,sections,order) where summary is the top line and
 
654
            sections is a dictionary of the rest indexed by section name.
 
655
            order is the order the section appear in the text.
 
656
            A section starts with a heading line of the form ":xxx:".
 
657
            Indented text on following lines is the section value.
 
658
            All text found outside a named section is assigned to the
 
659
            default section which is given the key of None.
 
660
        """
 
661
        def save_section(sections, order, label, section):
 
662
            if len(section) > 0:
 
663
                if label in sections:
 
664
                    sections[label] += '\n' + section
 
665
                else:
 
666
                    order.append(label)
 
667
                    sections[label] = section
 
668
 
 
669
        lines = text.rstrip().splitlines()
 
670
        summary = lines.pop(0)
 
671
        sections = {}
 
672
        order = []
 
673
        label, section = None, ''
 
674
        for line in lines:
 
675
            if line.startswith(':') and line.endswith(':') and len(line) > 2:
 
676
                save_section(sections, order, label, section)
 
677
                label, section = line[1:-1], ''
 
678
            elif (label is not None and len(line) > 1 and
 
679
                    not line[0].isspace()):
 
680
                save_section(sections, order, label, section)
 
681
                label, section = None, line
 
682
            else:
 
683
                if len(section) > 0:
 
684
                    section += '\n' + line
 
685
                else:
 
686
                    section = line
 
687
        save_section(sections, order, label, section)
 
688
        return summary, sections, order
 
689
 
 
690
    def get_help_topic(self):
 
691
        """Return the commands help topic - its name."""
 
692
        return self.name()
 
693
 
 
694
    def get_see_also(self, additional_terms=None):
 
695
        """Return a list of help topics that are related to this command.
 
696
 
 
697
        The list is derived from the content of the _see_also attribute. Any
 
698
        duplicates are removed and the result is in lexical order.
 
699
        :param additional_terms: Additional help topics to cross-reference.
 
700
        :return: A list of help topics.
 
701
        """
 
702
        see_also = set(getattr(self, '_see_also', []))
 
703
        if additional_terms:
 
704
            see_also.update(additional_terms)
 
705
        return sorted(see_also)
 
706
 
 
707
    def options(self):
 
708
        """Return dict of valid options for this command.
 
709
 
 
710
        Maps from long option name to option object."""
 
711
        r = Option.STD_OPTIONS.copy()
 
712
        std_names = set(r)
 
713
        for o in self.takes_options:
 
714
            if isinstance(o, str):
 
715
                o = option.Option.OPTIONS[o]
 
716
            r[o.name] = o
 
717
            if o.name in std_names:
 
718
                self.supported_std_options.append(o.name)
 
719
        return r
 
720
 
 
721
    def _setup_outf(self):
 
722
        """Return a file linked to stdout, which has proper encoding."""
 
723
        self.outf = ui.ui_factory.make_output_stream(
 
724
            encoding_type=self.encoding_type)
 
725
 
 
726
    def run_argv_aliases(self, argv, alias_argv=None):
 
727
        """Parse the command line and run with extra aliases in alias_argv."""
 
728
        args, opts = parse_args(self, argv, alias_argv)
 
729
        self._setup_outf()
 
730
 
 
731
        # Process the standard options
 
732
        if 'help' in opts:  # e.g. brz add --help
 
733
            self.outf.write(self.get_help_text())
 
734
            return 0
 
735
        if 'usage' in opts:  # e.g. brz add --usage
 
736
            self.outf.write(self.get_help_text(verbose=False))
 
737
            return 0
 
738
        trace.set_verbosity_level(option._verbosity_level)
 
739
        if 'verbose' in self.supported_std_options:
 
740
            opts['verbose'] = trace.is_verbose()
 
741
        elif 'verbose' in opts:
 
742
            del opts['verbose']
 
743
        if 'quiet' in self.supported_std_options:
 
744
            opts['quiet'] = trace.is_quiet()
 
745
        elif 'quiet' in opts:
 
746
            del opts['quiet']
 
747
        # mix arguments and options into one dictionary
 
748
        cmdargs = _match_argform(self.name(), self.takes_args, args)
 
749
        cmdopts = {}
 
750
        for k, v in opts.items():
 
751
            cmdopts[k.replace('-', '_')] = v
 
752
 
 
753
        all_cmd_args = cmdargs.copy()
 
754
        all_cmd_args.update(cmdopts)
 
755
 
 
756
        try:
 
757
            return self.run(**all_cmd_args)
 
758
        finally:
 
759
            # reset it, so that other commands run in the same process won't
 
760
            # inherit state. Before we reset it, log any activity, so that it
 
761
            # gets properly tracked.
 
762
            ui.ui_factory.log_transport_activity(
 
763
                display=('bytes' in debug.debug_flags))
 
764
            trace.set_verbosity_level(0)
 
765
 
 
766
    def _setup_run(self):
 
767
        """Wrap the defined run method on self with a cleanup.
 
768
 
 
769
        This is called by __init__ to make the Command be able to be run
 
770
        by just calling run(), as it could be before cleanups were added.
 
771
 
 
772
        If a different form of cleanups are in use by your Command subclass,
 
773
        you can override this method.
 
774
        """
 
775
        class_run = self.run
 
776
 
 
777
        def run(*args, **kwargs):
 
778
            for hook in Command.hooks['pre_command']:
 
779
                hook(self)
 
780
            try:
 
781
                with contextlib.ExitStack() as self._exit_stack:
 
782
                    return class_run(*args, **kwargs)
 
783
            finally:
 
784
                for hook in Command.hooks['post_command']:
 
785
                    hook(self)
 
786
        self.run = run
 
787
 
 
788
    def run(self):
 
789
        """Actually run the command.
 
790
 
 
791
        This is invoked with the options and arguments bound to
 
792
        keyword parameters.
 
793
 
 
794
        Return 0 or None if the command was successful, or a non-zero
 
795
        shell error code if not.  It's OK for this method to allow
 
796
        an exception to raise up.
 
797
 
 
798
        This method is automatically wrapped by Command.__init__ with a
 
799
        ExitStack, stored as self._exit_stack. This can be used
 
800
        via self.add_cleanup to perform automatic cleanups at the end of
 
801
        run().
 
802
 
 
803
        The argument for run are assembled by introspection. So for instance,
 
804
        if your command takes an argument files, you would declare::
 
805
 
 
806
            def run(self, files=None):
 
807
                pass
 
808
        """
 
809
        raise NotImplementedError('no implementation of command %r'
 
810
                                  % self.name())
 
811
 
 
812
    def help(self):
 
813
        """Return help message for this class."""
 
814
        from inspect import getdoc
 
815
        if self.__doc__ is Command.__doc__:
 
816
            return None
 
817
        return getdoc(self)
 
818
 
 
819
    def gettext(self, message):
 
820
        """Returns the gettext function used to translate this command's help.
 
821
 
 
822
        Commands provided by plugins should override this to use their
 
823
        own i18n system.
 
824
        """
 
825
        return i18n.gettext_per_paragraph(message)
 
826
 
 
827
    def name(self):
 
828
        """Return the canonical name for this command.
 
829
 
 
830
        The name under which it was actually invoked is available in invoked_as
 
831
        """
 
832
        return _unsquish_command_name(self.__class__.__name__)
 
833
 
 
834
    def plugin_name(self):
 
835
        """Get the name of the plugin that provides this command.
 
836
 
 
837
        :return: The name of the plugin or None if the command is builtin.
 
838
        """
 
839
        return plugin_name(self.__module__)
 
840
 
 
841
 
 
842
class CommandHooks(Hooks):
 
843
    """Hooks related to Command object creation/enumeration."""
 
844
 
 
845
    def __init__(self):
 
846
        """Create the default hooks.
 
847
 
 
848
        These are all empty initially, because by default nothing should get
 
849
        notified.
 
850
        """
 
851
        Hooks.__init__(self, "breezy.commands", "Command.hooks")
 
852
        self.add_hook(
 
853
            'extend_command',
 
854
            "Called after creating a command object to allow modifications "
 
855
            "such as adding or removing options, docs etc. Called with the "
 
856
            "new breezy.commands.Command object.", (1, 13))
 
857
        self.add_hook(
 
858
            'get_command',
 
859
            "Called when creating a single command. Called with "
 
860
            "(cmd_or_None, command_name). get_command should either return "
 
861
            "the cmd_or_None parameter, or a replacement Command object that "
 
862
            "should be used for the command. Note that the Command.hooks "
 
863
            "hooks are core infrastructure. Many users will prefer to use "
 
864
            "breezy.commands.register_command or plugin_cmds.register_lazy.",
 
865
            (1, 17))
 
866
        self.add_hook(
 
867
            'get_missing_command',
 
868
            "Called when creating a single command if no command could be "
 
869
            "found. Called with (command_name). get_missing_command should "
 
870
            "either return None, or a Command object to be used for the "
 
871
            "command.", (1, 17))
 
872
        self.add_hook(
 
873
            'list_commands',
 
874
            "Called when enumerating commands. Called with a set of "
 
875
            "cmd_name strings for all the commands found so far. This set "
 
876
            " is safe to mutate - e.g. to remove a command. "
 
877
            "list_commands should return the updated set of command names.",
 
878
            (1, 17))
 
879
        self.add_hook(
 
880
            'pre_command',
 
881
            "Called prior to executing a command. Called with the command "
 
882
            "object.", (2, 6))
 
883
        self.add_hook(
 
884
            'post_command',
 
885
            "Called after executing a command. Called with the command "
 
886
            "object.", (2, 6))
 
887
 
 
888
 
 
889
Command.hooks = CommandHooks()
 
890
 
 
891
 
 
892
def parse_args(command, argv, alias_argv=None):
 
893
    """Parse command line.
 
894
 
 
895
    Arguments and options are parsed at this level before being passed
 
896
    down to specific command handlers.  This routine knows, from a
 
897
    lookup table, something about the available options, what optargs
 
898
    they take, and which commands will accept them.
 
899
    """
 
900
    # TODO: make it a method of the Command?
 
901
    parser = option.get_optparser(
 
902
        [v for k, v in sorted(command.options().items())])
 
903
    if alias_argv is not None:
 
904
        args = alias_argv + argv
 
905
    else:
 
906
        args = argv
 
907
 
 
908
    # python 2's optparse raises this exception if a non-ascii
 
909
    # option name is given.  See http://bugs.python.org/issue2931
 
910
    try:
 
911
        options, args = parser.parse_args(args)
 
912
    except UnicodeEncodeError:
 
913
        raise errors.BzrCommandError(
 
914
            gettext('Only ASCII permitted in option names'))
 
915
 
 
916
    opts = dict((k, v) for k, v in options.__dict__.items() if
 
917
                v is not option.OptionParser.DEFAULT_VALUE)
 
918
    return args, opts
 
919
 
 
920
 
 
921
def _match_argform(cmd, takes_args, args):
 
922
    argdict = {}
 
923
 
 
924
    # step through args and takes_args, allowing appropriate 0-many matches
 
925
    for ap in takes_args:
 
926
        argname = ap[:-1]
 
927
        if ap[-1] == '?':
 
928
            if args:
 
929
                argdict[argname] = args.pop(0)
 
930
        elif ap[-1] == '*':  # all remaining arguments
 
931
            if args:
 
932
                argdict[argname + '_list'] = args[:]
 
933
                args = []
 
934
            else:
 
935
                argdict[argname + '_list'] = None
 
936
        elif ap[-1] == '+':
 
937
            if not args:
 
938
                raise errors.BzrCommandError(gettext(
 
939
                    "command {0!r} needs one or more {1}").format(
 
940
                    cmd, argname.upper()))
 
941
            else:
 
942
                argdict[argname + '_list'] = args[:]
 
943
                args = []
 
944
        elif ap[-1] == '$':  # all but one
 
945
            if len(args) < 2:
 
946
                raise errors.BzrCommandError(
 
947
                    gettext("command {0!r} needs one or more {1}").format(
 
948
                        cmd, argname.upper()))
 
949
            argdict[argname + '_list'] = args[:-1]
 
950
            args[:-1] = []
 
951
        else:
 
952
            # just a plain arg
 
953
            argname = ap
 
954
            if not args:
 
955
                raise errors.BzrCommandError(
 
956
                    gettext("command {0!r} requires argument {1}").format(
 
957
                        cmd, argname.upper()))
 
958
            else:
 
959
                argdict[argname] = args.pop(0)
 
960
 
 
961
    if args:
 
962
        raise errors.BzrCommandError(gettext(
 
963
            "extra argument to command {0}: {1}").format(
 
964
            cmd, args[0]))
 
965
 
 
966
    return argdict
 
967
 
 
968
 
 
969
def apply_coveraged(the_callable, *args, **kwargs):
 
970
    import coverage
 
971
    cov = coverage.Coverage()
 
972
    try:
 
973
        config_file = cov.config.config_file
 
974
    except AttributeError:  # older versions of coverage
 
975
        config_file = cov.config_file
 
976
    os.environ['COVERAGE_PROCESS_START'] = config_file
 
977
    cov.start()
 
978
    try:
 
979
        return exception_to_return_code(the_callable, *args, **kwargs)
 
980
    finally:
 
981
        cov.stop()
 
982
        cov.save()
 
983
 
 
984
 
 
985
def apply_profiled(the_callable, *args, **kwargs):
 
986
    import hotshot
 
987
    import tempfile
 
988
    import hotshot.stats
 
989
    pffileno, pfname = tempfile.mkstemp()
 
990
    try:
 
991
        prof = hotshot.Profile(pfname)
 
992
        try:
 
993
            ret = prof.runcall(exception_to_return_code, the_callable, *args,
 
994
                               **kwargs) or 0
 
995
        finally:
 
996
            prof.close()
 
997
        stats = hotshot.stats.load(pfname)
 
998
        stats.strip_dirs()
 
999
        stats.sort_stats('cum')   # 'time'
 
1000
        # XXX: Might like to write to stderr or the trace file instead but
 
1001
        # print_stats seems hardcoded to stdout
 
1002
        stats.print_stats(20)
 
1003
        return ret
 
1004
    finally:
 
1005
        os.close(pffileno)
 
1006
        os.remove(pfname)
 
1007
 
 
1008
 
 
1009
def exception_to_return_code(the_callable, *args, **kwargs):
 
1010
    """UI level helper for profiling and coverage.
 
1011
 
 
1012
    This transforms exceptions into a return value of 3. As such its only
 
1013
    relevant to the UI layer, and should never be called where catching
 
1014
    exceptions may be desirable.
 
1015
    """
 
1016
    try:
 
1017
        return the_callable(*args, **kwargs)
 
1018
    except (KeyboardInterrupt, Exception):
 
1019
        # used to handle AssertionError and KeyboardInterrupt
 
1020
        # specially here, but hopefully they're handled ok by the logger now
 
1021
        exc_info = sys.exc_info()
 
1022
        exitcode = trace.report_exception(exc_info, sys.stderr)
 
1023
        if os.environ.get('BRZ_PDB'):
 
1024
            print('**** entering debugger')
 
1025
            import pdb
 
1026
            pdb.post_mortem(exc_info[2])
 
1027
        return exitcode
 
1028
 
 
1029
 
 
1030
def apply_lsprofiled(filename, the_callable, *args, **kwargs):
 
1031
    from breezy.lsprof import profile
 
1032
    ret, stats = profile(exception_to_return_code, the_callable,
 
1033
                         *args, **kwargs)
 
1034
    stats.sort()
 
1035
    if filename is None:
 
1036
        stats.pprint()
 
1037
    else:
 
1038
        stats.save(filename)
 
1039
        trace.note(gettext('Profile data written to "%s".'), filename)
 
1040
    return ret
 
1041
 
 
1042
 
 
1043
def get_alias(cmd, config=None):
 
1044
    """Return an expanded alias, or None if no alias exists.
 
1045
 
 
1046
    cmd
 
1047
        Command to be checked for an alias.
 
1048
    config
 
1049
        Used to specify an alternative config to use,
 
1050
        which is especially useful for testing.
 
1051
        If it is unspecified, the global config will be used.
 
1052
    """
 
1053
    if config is None:
 
1054
        import breezy.config
 
1055
        config = breezy.config.GlobalConfig()
 
1056
    alias = config.get_alias(cmd)
 
1057
    if (alias):
 
1058
        return cmdline.split(alias)
 
1059
    return None
 
1060
 
 
1061
 
 
1062
def run_bzr(argv, load_plugins=load_plugins, disable_plugins=disable_plugins):
 
1063
    """Execute a command.
 
1064
 
 
1065
    :param argv: The command-line arguments, without the program name from
 
1066
        argv[0] These should already be decoded. All library/test code calling
 
1067
        run_bzr should be passing valid strings (don't need decoding).
 
1068
    :param load_plugins: What function to call when triggering plugin loading.
 
1069
        This function should take no arguments and cause all plugins to be
 
1070
        loaded.
 
1071
    :param disable_plugins: What function to call when disabling plugin
 
1072
        loading. This function should take no arguments and cause all plugin
 
1073
        loading to be prohibited (so that code paths in your application that
 
1074
        know about some plugins possibly being present will fail to import
 
1075
        those plugins even if they are installed.)
 
1076
    :return: Returns a command exit code or raises an exception.
 
1077
 
 
1078
    Special master options: these must come before the command because
 
1079
    they control how the command is interpreted.
 
1080
 
 
1081
    --no-plugins
 
1082
        Do not load plugin modules at all
 
1083
 
 
1084
    --no-aliases
 
1085
        Do not allow aliases
 
1086
 
 
1087
    --builtin
 
1088
        Only use builtin commands.  (Plugins are still allowed to change
 
1089
        other behaviour.)
 
1090
 
 
1091
    --profile
 
1092
        Run under the Python hotshot profiler.
 
1093
 
 
1094
    --lsprof
 
1095
        Run under the Python lsprof profiler.
 
1096
 
 
1097
    --coverage
 
1098
        Generate code coverage report
 
1099
 
 
1100
    --concurrency
 
1101
        Specify the number of processes that can be run concurrently
 
1102
        (selftest).
 
1103
    """
 
1104
    trace.mutter("breezy version: " + breezy.__version__)
 
1105
    argv = _specified_or_unicode_argv(argv)
 
1106
    trace.mutter("brz arguments: %r", argv)
 
1107
 
 
1108
    opt_lsprof = opt_profile = opt_no_plugins = opt_builtin = \
 
1109
        opt_coverage = opt_no_l10n = opt_no_aliases = False
 
1110
    opt_lsprof_file = None
 
1111
 
 
1112
    # --no-plugins is handled specially at a very early stage. We need
 
1113
    # to load plugins before doing other command parsing so that they
 
1114
    # can override commands, but this needs to happen first.
 
1115
 
 
1116
    argv_copy = []
 
1117
    i = 0
 
1118
    override_config = []
 
1119
    while i < len(argv):
 
1120
        a = argv[i]
 
1121
        if a == '--profile':
 
1122
            opt_profile = True
 
1123
        elif a == '--lsprof':
 
1124
            opt_lsprof = True
 
1125
        elif a == '--lsprof-file':
 
1126
            opt_lsprof = True
 
1127
            opt_lsprof_file = argv[i + 1]
 
1128
            i += 1
 
1129
        elif a == '--no-plugins':
 
1130
            opt_no_plugins = True
 
1131
        elif a == '--no-aliases':
 
1132
            opt_no_aliases = True
 
1133
        elif a == '--no-l10n':
 
1134
            opt_no_l10n = True
 
1135
        elif a == '--builtin':
 
1136
            opt_builtin = True
 
1137
        elif a == '--concurrency':
 
1138
            os.environ['BRZ_CONCURRENCY'] = argv[i + 1]
 
1139
            i += 1
 
1140
        elif a == '--coverage':
 
1141
            opt_coverage = True
 
1142
        elif a == '--profile-imports':
 
1143
            pass  # already handled in startup script Bug #588277
 
1144
        elif a.startswith('-D'):
 
1145
            debug.debug_flags.add(a[2:])
 
1146
        elif a.startswith('-O'):
 
1147
            override_config.append(a[2:])
 
1148
        else:
 
1149
            argv_copy.append(a)
 
1150
        i += 1
 
1151
 
 
1152
    cmdline_overrides = breezy.get_global_state().cmdline_overrides
 
1153
    cmdline_overrides._from_cmdline(override_config)
 
1154
 
 
1155
    debug.set_debug_flags_from_config()
 
1156
 
 
1157
    if not opt_no_plugins:
 
1158
        load_plugins()
 
1159
    else:
 
1160
        disable_plugins()
 
1161
 
 
1162
    argv = argv_copy
 
1163
    if (not argv):
 
1164
        get_cmd_object('help').run_argv_aliases([])
 
1165
        return 0
 
1166
 
 
1167
    if argv[0] == '--version':
 
1168
        get_cmd_object('version').run_argv_aliases([])
 
1169
        return 0
 
1170
 
 
1171
    alias_argv = None
 
1172
 
 
1173
    if not opt_no_aliases:
 
1174
        alias_argv = get_alias(argv[0])
 
1175
        if alias_argv:
 
1176
            argv[0] = alias_argv.pop(0)
 
1177
 
 
1178
    cmd = argv.pop(0)
 
1179
    cmd_obj = get_cmd_object(cmd, plugins_override=not opt_builtin)
 
1180
    if opt_no_l10n:
 
1181
        cmd_obj.l10n = False
 
1182
    run = cmd_obj.run_argv_aliases
 
1183
    run_argv = [argv, alias_argv]
 
1184
 
 
1185
    try:
 
1186
        # We can be called recursively (tests for example), but we don't want
 
1187
        # the verbosity level to propagate.
 
1188
        saved_verbosity_level = option._verbosity_level
 
1189
        option._verbosity_level = 0
 
1190
        if opt_lsprof:
 
1191
            if opt_coverage:
 
1192
                trace.warning(
 
1193
                    '--coverage ignored, because --lsprof is in use.')
 
1194
            ret = apply_lsprofiled(opt_lsprof_file, run, *run_argv)
 
1195
        elif opt_profile:
 
1196
            if opt_coverage:
 
1197
                trace.warning(
 
1198
                    '--coverage ignored, because --profile is in use.')
 
1199
            ret = apply_profiled(run, *run_argv)
 
1200
        elif opt_coverage:
 
1201
            ret = apply_coveraged(run, *run_argv)
 
1202
        else:
 
1203
            ret = run(*run_argv)
 
1204
        return ret or 0
 
1205
    finally:
 
1206
        # reset, in case we may do other commands later within the same
 
1207
        # process. Commands that want to execute sub-commands must propagate
 
1208
        # --verbose in their own way.
 
1209
        if 'memory' in debug.debug_flags:
 
1210
            trace.debug_memory('Process status after command:', short=False)
 
1211
        option._verbosity_level = saved_verbosity_level
 
1212
        # Reset the overrides
 
1213
        cmdline_overrides._reset()
 
1214
 
 
1215
 
 
1216
def display_command(func):
 
1217
    """Decorator that suppresses pipe/interrupt errors."""
 
1218
    def ignore_pipe(*args, **kwargs):
 
1219
        try:
 
1220
            result = func(*args, **kwargs)
 
1221
            sys.stdout.flush()
 
1222
            return result
 
1223
        except IOError as e:
 
1224
            if getattr(e, 'errno', None) is None:
 
1225
                raise
 
1226
            if e.errno != errno.EPIPE:
 
1227
                # Win32 raises IOError with errno=0 on a broken pipe
 
1228
                if sys.platform != 'win32' or (e.errno not in (0, errno.EINVAL)):
 
1229
                    raise
 
1230
            pass
 
1231
        except KeyboardInterrupt:
 
1232
            pass
 
1233
    return ignore_pipe
 
1234
 
 
1235
 
 
1236
def install_bzr_command_hooks():
 
1237
    """Install the hooks to supply bzr's own commands."""
 
1238
    if _list_bzr_commands in Command.hooks["list_commands"]:
 
1239
        return
 
1240
    Command.hooks.install_named_hook("list_commands", _list_bzr_commands,
 
1241
                                     "bzr commands")
 
1242
    Command.hooks.install_named_hook("get_command", _get_bzr_command,
 
1243
                                     "bzr commands")
 
1244
    Command.hooks.install_named_hook("get_command", _get_plugin_command,
 
1245
                                     "bzr plugin commands")
 
1246
    Command.hooks.install_named_hook("get_command", _get_external_command,
 
1247
                                     "bzr external command lookup")
 
1248
    Command.hooks.install_named_hook("get_missing_command",
 
1249
                                     _try_plugin_provider,
 
1250
                                     "bzr plugin-provider-db check")
 
1251
 
 
1252
 
 
1253
def _specified_or_unicode_argv(argv):
 
1254
    # For internal or testing use, argv can be passed.  Otherwise, get it from
 
1255
    # the process arguments in a unicode-safe way.
 
1256
    if argv is None:
 
1257
        return osutils.get_unicode_argv()
 
1258
    new_argv = []
 
1259
    try:
 
1260
        # ensure all arguments are unicode strings
 
1261
        for a in argv:
 
1262
            if not isinstance(a, str):
 
1263
                raise ValueError('not native str or unicode: %r' % (a,))
 
1264
            new_argv.append(a)
 
1265
    except (ValueError, UnicodeDecodeError):
 
1266
        raise errors.BzrError("argv should be list of unicode strings.")
 
1267
    return new_argv
 
1268
 
 
1269
 
 
1270
def main(argv=None):
 
1271
    """Main entry point of command-line interface.
 
1272
 
 
1273
    Typically `breezy.initialize` should be called first.
 
1274
 
 
1275
    :param argv: list of unicode command-line arguments similar to sys.argv.
 
1276
        argv[0] is script name usually, it will be ignored.
 
1277
        Don't pass here sys.argv because this list contains plain strings
 
1278
        and not unicode; pass None instead.
 
1279
 
 
1280
    :return: exit code of brz command.
 
1281
    """
 
1282
    if argv is not None:
 
1283
        argv = argv[1:]
 
1284
    _register_builtin_commands()
 
1285
    ret = run_bzr_catch_errors(argv)
 
1286
    trace.mutter("return code %d", ret)
 
1287
    return ret
 
1288
 
 
1289
 
 
1290
def run_bzr_catch_errors(argv):
 
1291
    """Run a bzr command with parameters as described by argv.
 
1292
 
 
1293
    This function assumed that that UI layer is setup, that symbol deprecations
 
1294
    are already applied, and that unicode decoding has already been performed
 
1295
    on argv.
 
1296
    """
 
1297
    # done here so that they're covered for every test run
 
1298
    install_bzr_command_hooks()
 
1299
    return exception_to_return_code(run_bzr, argv)
 
1300
 
 
1301
 
 
1302
def run_bzr_catch_user_errors(argv):
 
1303
    """Run brz and report user errors, but let internal errors propagate.
 
1304
 
 
1305
    This is used for the test suite, and might be useful for other programs
 
1306
    that want to wrap the commandline interface.
 
1307
    """
 
1308
    # done here so that they're covered for every test run
 
1309
    install_bzr_command_hooks()
 
1310
    try:
 
1311
        return run_bzr(argv)
 
1312
    except Exception as e:
 
1313
        if (isinstance(e, (OSError, IOError))
 
1314
                or not getattr(e, 'internal_error', True)):
 
1315
            trace.report_exception(sys.exc_info(), sys.stderr)
 
1316
            return 3
 
1317
        else:
 
1318
            raise
 
1319
 
 
1320
 
 
1321
class HelpCommandIndex(object):
 
1322
    """A index for bzr help that returns commands."""
 
1323
 
 
1324
    def __init__(self):
 
1325
        self.prefix = 'commands/'
 
1326
 
 
1327
    def get_topics(self, topic):
 
1328
        """Search for topic amongst commands.
 
1329
 
 
1330
        :param topic: A topic to search for.
 
1331
        :return: A list which is either empty or contains a single
 
1332
            Command entry.
 
1333
        """
 
1334
        if topic and topic.startswith(self.prefix):
 
1335
            topic = topic[len(self.prefix):]
 
1336
        try:
 
1337
            cmd = _get_cmd_object(topic, check_missing=False)
 
1338
        except KeyError:
 
1339
            return []
 
1340
        else:
 
1341
            return [cmd]
 
1342
 
 
1343
 
 
1344
class Provider(object):
 
1345
    """Generic class to be overriden by plugins"""
 
1346
 
 
1347
    def plugin_for_command(self, cmd_name):
 
1348
        """Takes a command and returns the information for that plugin
 
1349
 
 
1350
        :return: A dictionary with all the available information
 
1351
            for the requested plugin
 
1352
        """
 
1353
        raise NotImplementedError
 
1354
 
 
1355
 
 
1356
class ProvidersRegistry(registry.Registry):
 
1357
    """This registry exists to allow other providers to exist"""
 
1358
 
 
1359
    def __iter__(self):
 
1360
        for key, provider in self.items():
 
1361
            yield provider
 
1362
 
 
1363
 
 
1364
command_providers_registry = ProvidersRegistry()