/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: Gustav Hartvigsson
  • Date: 2021-01-09 21:36:27 UTC
  • Revision ID: gustav.hartvigsson@gmail.com-20210109213627-h1xwcutzy9m7a99b
Added 'Case Preserving Working Tree Use Cases' from Canonical Wiki

* Addod a page from the Canonical Bazaar wiki
  with information on the scmeatics of case
  perserving filesystems an a case insensitive
  filesystem works.
  
  * Needs re-work, but this will do as it is the
    same inforamoton as what was on the linked
    page in the currint documentation.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2004, 2005 by Canonical Ltd
2
 
 
 
1
# Copyright (C) 2005-2011 Canonical Ltd
 
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
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
7
 
 
 
7
#
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
11
# GNU General Public License for more details.
12
 
 
 
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
 
 
17
 
 
18
 
# TODO: probably should say which arguments are candidates for glob
19
 
# expansion on windows and do that at the command level.
20
 
 
21
 
# TODO: Help messages for options.
22
 
 
23
 
# TODO: Define arguments by objects, rather than just using names.
24
 
# Those objects can specify the expected type of the argument, which
25
 
# would help with validation and shell completion.
26
 
 
27
 
 
28
 
# TODO: Help messages for options.
29
 
 
30
 
# TODO: Define arguments by objects, rather than just using names.
31
 
# Those objects can specify the expected type of the argument, which
32
 
# would help with validation and shell completion.
33
 
 
34
 
 
35
 
 
 
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
36
26
import sys
37
 
import os
38
 
from warnings import warn
39
 
from inspect import getdoc
40
 
 
41
 
import bzrlib
42
 
import bzrlib.trace
43
 
from bzrlib.trace import mutter, note, log_error, warning
44
 
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError, NotBranchError
45
 
from bzrlib.revisionspec import RevisionSpec
46
 
from bzrlib import BZRDIR
47
 
 
48
 
plugin_cmds = {}
49
 
 
50
 
 
51
 
def register_command(cmd):
52
 
    "Utility function to help register a command"
 
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.CommandError):
 
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
    """
53
176
    global plugin_cmds
54
 
    k = cmd.__name__
55
 
    if k.startswith("cmd_"):
56
 
        k_unsquished = _unsquish_command_name(k)
57
 
    else:
58
 
        k_unsquished = k
59
 
    if not plugin_cmds.has_key(k_unsquished):
60
 
        plugin_cmds[k_unsquished] = cmd
61
 
        mutter('registered plugin command %s', k_unsquished)      
62
 
    else:
63
 
        log_error('Two plugins defined the same command: %r' % k)
64
 
        log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
 
177
    return plugin_cmds.register(cmd, decorate)
65
178
 
66
179
 
67
180
def _squish_command_name(cmd):
69
182
 
70
183
 
71
184
def _unsquish_command_name(cmd):
72
 
    assert cmd.startswith("cmd_")
73
 
    return cmd[4:].replace('_','-')
74
 
 
75
 
 
76
 
def _parse_revision_str(revstr):
77
 
    """This handles a revision string -> revno.
78
 
 
79
 
    This always returns a list.  The list will have one element for
80
 
    each revision.
81
 
 
82
 
    >>> _parse_revision_str('234')
83
 
    [<RevisionSpec_int 234>]
84
 
    >>> _parse_revision_str('234..567')
85
 
    [<RevisionSpec_int 234>, <RevisionSpec_int 567>]
86
 
    >>> _parse_revision_str('..')
87
 
    [<RevisionSpec None>, <RevisionSpec None>]
88
 
    >>> _parse_revision_str('..234')
89
 
    [<RevisionSpec None>, <RevisionSpec_int 234>]
90
 
    >>> _parse_revision_str('234..')
91
 
    [<RevisionSpec_int 234>, <RevisionSpec None>]
92
 
    >>> _parse_revision_str('234..456..789') # Maybe this should be an error
93
 
    [<RevisionSpec_int 234>, <RevisionSpec_int 456>, <RevisionSpec_int 789>]
94
 
    >>> _parse_revision_str('234....789') # Error?
95
 
    [<RevisionSpec_int 234>, <RevisionSpec None>, <RevisionSpec_int 789>]
96
 
    >>> _parse_revision_str('revid:test@other.com-234234')
97
 
    [<RevisionSpec_revid revid:test@other.com-234234>]
98
 
    >>> _parse_revision_str('revid:test@other.com-234234..revid:test@other.com-234235')
99
 
    [<RevisionSpec_revid revid:test@other.com-234234>, <RevisionSpec_revid revid:test@other.com-234235>]
100
 
    >>> _parse_revision_str('revid:test@other.com-234234..23')
101
 
    [<RevisionSpec_revid revid:test@other.com-234234>, <RevisionSpec_int 23>]
102
 
    >>> _parse_revision_str('date:2005-04-12')
103
 
    [<RevisionSpec_date date:2005-04-12>]
104
 
    >>> _parse_revision_str('date:2005-04-12 12:24:33')
105
 
    [<RevisionSpec_date date:2005-04-12 12:24:33>]
106
 
    >>> _parse_revision_str('date:2005-04-12T12:24:33')
107
 
    [<RevisionSpec_date date:2005-04-12T12:24:33>]
108
 
    >>> _parse_revision_str('date:2005-04-12,12:24:33')
109
 
    [<RevisionSpec_date date:2005-04-12,12:24:33>]
110
 
    >>> _parse_revision_str('-5..23')
111
 
    [<RevisionSpec_int -5>, <RevisionSpec_int 23>]
112
 
    >>> _parse_revision_str('-5')
113
 
    [<RevisionSpec_int -5>]
114
 
    >>> _parse_revision_str('123a')
115
 
    Traceback (most recent call last):
116
 
      ...
117
 
    BzrError: No namespace registered for string: '123a'
118
 
    >>> _parse_revision_str('abc')
119
 
    Traceback (most recent call last):
120
 
      ...
121
 
    BzrError: No namespace registered for string: 'abc'
122
 
    """
123
 
    import re
124
 
    old_format_re = re.compile('\d*:\d*')
125
 
    m = old_format_re.match(revstr)
126
 
    revs = []
127
 
    if m:
128
 
        warning('Colon separator for revision numbers is deprecated.'
129
 
                ' Use .. instead')
130
 
        for rev in revstr.split(':'):
131
 
            if rev:
132
 
                revs.append(RevisionSpec(int(rev)))
133
 
            else:
134
 
                revs.append(RevisionSpec(None))
135
 
    else:
136
 
        for x in revstr.split('..'):
137
 
            if not x:
138
 
                revs.append(RevisionSpec(None))
139
 
            else:
140
 
                revs.append(RevisionSpec(x))
141
 
    return revs
142
 
 
143
 
 
144
 
def get_merge_type(typestring):
145
 
    """Attempt to find the merge class/factory associated with a string."""
146
 
    from merge import merge_types
147
 
    try:
148
 
        return merge_types[typestring][0]
149
 
    except KeyError:
150
 
        templ = '%s%%7s: %%s' % (' '*12)
151
 
        lines = [templ % (f[0], f[1][1]) for f in merge_types.iteritems()]
152
 
        type_list = '\n'.join(lines)
153
 
        msg = "No known merge type %s. Supported types are:\n%s" %\
154
 
            (typestring, type_list)
155
 
        raise BzrCommandError(msg)
156
 
 
157
 
 
158
 
def _builtin_commands():
159
 
    import bzrlib.builtins
160
 
    r = {}
161
 
    builtins = bzrlib.builtins.__dict__
162
 
    for name in builtins:
 
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:
163
201
        if name.startswith("cmd_"):
164
 
            real_name = _unsquish_command_name(name)        
165
 
            r[real_name] = builtins[name]
166
 
    return r
167
 
 
168
 
            
 
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
 
169
227
 
170
228
def builtin_command_names():
171
 
    """Return list of builtin command names."""
172
 
    return _builtin_commands().keys()
173
 
    
 
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
 
174
237
 
175
238
def plugin_command_names():
 
239
    """Returns command names from commands registered by plugins."""
176
240
    return plugin_cmds.keys()
177
241
 
178
242
 
179
 
def _get_cmd_dict(plugins_override=True):
180
 
    """Return name->class mapping for all commands."""
181
 
    d = _builtin_commands()
182
 
    if plugins_override:
183
 
        d.update(plugin_cmds)
184
 
    return d
185
 
 
186
 
    
187
 
def get_all_cmds(plugins_override=True):
188
 
    """Return canonical name and class for all registered commands."""
189
 
    for k, v in _get_cmd_dict(plugins_override=plugins_override).iteritems():
190
 
        yield k,v
 
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
191
287
 
192
288
 
193
289
def get_cmd_object(cmd_name, plugins_override=True):
194
 
    """Return the canonical name and command class for a command.
 
290
    """Return the command object for a command.
195
291
 
196
292
    plugins_override
197
293
        If true, plugin commands can override builtins.
198
294
    """
199
 
    from bzrlib.externalcommand import ExternalCommand
200
 
 
201
 
    cmd_name = str(cmd_name)            # not unicode
202
 
 
203
 
    # first look up this command under the specified name
204
 
    cmds = _get_cmd_dict(plugins_override=plugins_override)
205
 
    try:
206
 
        return cmds[cmd_name]()
207
 
    except KeyError:
208
 
        pass
209
 
 
210
 
    # look for any command which claims this as an alias
211
 
    for real_cmd_name, cmd_class in cmds.iteritems():
212
 
        if cmd_name in cmd_class.aliases:
213
 
            return cmd_class()
214
 
 
 
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.CommandError(
 
302
                gettext('unknown command "%s". Perhaps you meant "%s"')
 
303
                % (cmd_name, candidate))
 
304
        raise errors.CommandError(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
215
392
    cmd_obj = ExternalCommand.find_command(cmd_name)
216
393
    if cmd_obj:
217
394
        return cmd_obj
218
395
 
219
 
    raise BzrCommandError("unknown command %r" % cmd_name)
 
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
220
408
 
221
409
 
222
410
class Command(object):
223
411
    """Base class for commands.
224
412
 
225
 
    Commands are the heart of the command-line bzr interface.
 
413
    Commands are the heart of the command-line brz interface.
226
414
 
227
415
    The command object mostly handles the mapping of command-line
228
 
    parameters into one or more bzrlib operations, and of the results
 
416
    parameters into one or more breezy operations, and of the results
229
417
    into textual output.
230
418
 
231
419
    Commands normally don't have any state.  All their arguments are
237
425
    summary, then a complete description of the command.  A grammar
238
426
    description will be inserted.
239
427
 
240
 
    aliases
241
 
        Other accepted names for this command.
242
 
 
243
 
    takes_args
244
 
        List of argument forms, marked with whether they are optional,
245
 
        repeated, etc.
246
 
 
247
 
    takes_options
248
 
        List of options that may be given for this command.
249
 
 
250
 
    hidden
251
 
        If true, this command isn't advertised.  This is typically
 
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
252
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"
253
473
    """
254
474
    aliases = []
255
 
    
256
475
    takes_args = []
257
476
    takes_options = []
 
477
    encoding_type = 'strict'
 
478
    invoked_as = None
 
479
    l10n = True
258
480
 
259
481
    hidden = False
260
 
    
 
482
 
261
483
    def __init__(self):
262
484
        """Construct an instance of this command."""
263
 
        if self.__doc__ == Command.__doc__:
264
 
            warn("No help message set for %r" % self)
265
 
 
266
 
 
267
 
    def run_argv(self, argv):
268
 
        """Parse command line and run."""
269
 
        args, opts = parse_args(argv)
270
 
 
271
 
        if 'help' in opts:  # e.g. bzr add --help
272
 
            from bzrlib.help import help_on_command
273
 
            help_on_command(self.name())
274
 
            return 0
275
 
 
276
 
        # check options are reasonable
277
 
        allowed = self.takes_options
278
 
        for oname in opts:
279
 
            if oname not in allowed:
280
 
                raise BzrCommandError("option '--%s' is not allowed for command %r"
281
 
                                      % (oname, self.name()))
282
 
 
 
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']
283
747
        # mix arguments and options into one dictionary
284
748
        cmdargs = _match_argform(self.name(), self.takes_args, args)
285
749
        cmdopts = {}
289
753
        all_cmd_args = cmdargs.copy()
290
754
        all_cmd_args.update(cmdopts)
291
755
 
292
 
        return self.run(**all_cmd_args)
293
 
 
294
 
    
 
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
 
295
788
    def run(self):
296
789
        """Actually run the command.
297
790
 
301
794
        Return 0 or None if the command was successful, or a non-zero
302
795
        shell error code if not.  It's OK for this method to allow
303
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
304
808
        """
305
 
        raise NotImplementedError()
306
 
 
 
809
        raise NotImplementedError('no implementation of command %r'
 
810
                                  % self.name())
307
811
 
308
812
    def help(self):
309
813
        """Return help message for this class."""
 
814
        from inspect import getdoc
310
815
        if self.__doc__ is Command.__doc__:
311
816
            return None
312
817
        return getdoc(self)
313
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
 
314
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
        """
315
832
        return _unsquish_command_name(self.__class__.__name__)
316
833
 
317
 
 
318
 
def parse_spec(spec):
319
 
    """
320
 
    >>> parse_spec(None)
321
 
    [None, None]
322
 
    >>> parse_spec("./")
323
 
    ['./', None]
324
 
    >>> parse_spec("../@")
325
 
    ['..', -1]
326
 
    >>> parse_spec("../f/@35")
327
 
    ['../f', 35]
328
 
    >>> parse_spec('./@revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67')
329
 
    ['.', 'revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67']
330
 
    """
331
 
    if spec is None:
332
 
        return [None, None]
333
 
    if '/@' in spec:
334
 
        parsed = spec.split('/@')
335
 
        assert len(parsed) == 2
336
 
        if parsed[1] == "":
337
 
            parsed[1] = -1
338
 
        else:
339
 
            try:
340
 
                parsed[1] = int(parsed[1])
341
 
            except ValueError:
342
 
                pass # We can allow stuff like ./@revid:blahblahblah
343
 
            else:
344
 
                assert parsed[1] >=0
345
 
    else:
346
 
        parsed = [spec, None]
347
 
    return parsed
348
 
 
349
 
 
350
 
# list of all available options; the rhs can be either None for an
351
 
# option that takes no argument, or a constructor function that checks
352
 
# the type.
353
 
OPTIONS = {
354
 
    'all':                    None,
355
 
    'diff-options':           str,
356
 
    'help':                   None,
357
 
    'file':                   unicode,
358
 
    'force':                  None,
359
 
    'format':                 unicode,
360
 
    'forward':                None,
361
 
    'message':                unicode,
362
 
    'no-recurse':             None,
363
 
    'profile':                None,
364
 
    'revision':               _parse_revision_str,
365
 
    'short':                  None,
366
 
    'show-ids':               None,
367
 
    'timezone':               str,
368
 
    'verbose':                None,
369
 
    'version':                None,
370
 
    'email':                  None,
371
 
    'unchanged':              None,
372
 
    'update':                 None,
373
 
    'long':                   None,
374
 
    'root':                   str,
375
 
    'no-backup':              None,
376
 
    'merge-type':             get_merge_type,
377
 
    'pattern':                str,
378
 
    }
379
 
 
380
 
SHORT_OPTIONS = {
381
 
    'F':                      'file', 
382
 
    'h':                      'help',
383
 
    'm':                      'message',
384
 
    'r':                      'revision',
385
 
    'v':                      'verbose',
386
 
    'l':                      'long',
387
 
}
388
 
 
389
 
 
390
 
def parse_args(argv):
 
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):
391
893
    """Parse command line.
392
 
    
 
894
 
393
895
    Arguments and options are parsed at this level before being passed
394
896
    down to specific command handlers.  This routine knows, from a
395
897
    lookup table, something about the available options, what optargs
396
898
    they take, and which commands will accept them.
397
 
 
398
 
    >>> parse_args('--help'.split())
399
 
    ([], {'help': True})
400
 
    >>> parse_args('help -- --invalidcmd'.split())
401
 
    (['help', '--invalidcmd'], {})
402
 
    >>> parse_args('--version'.split())
403
 
    ([], {'version': True})
404
 
    >>> parse_args('status --all'.split())
405
 
    (['status'], {'all': True})
406
 
    >>> parse_args('commit --message=biter'.split())
407
 
    (['commit'], {'message': u'biter'})
408
 
    >>> parse_args('log -r 500'.split())
409
 
    (['log'], {'revision': [<RevisionSpec_int 500>]})
410
 
    >>> parse_args('log -r500..600'.split())
411
 
    (['log'], {'revision': [<RevisionSpec_int 500>, <RevisionSpec_int 600>]})
412
 
    >>> parse_args('log -vr500..600'.split())
413
 
    (['log'], {'verbose': True, 'revision': [<RevisionSpec_int 500>, <RevisionSpec_int 600>]})
414
 
    >>> parse_args('log -rrevno:500..600'.split()) #the r takes an argument
415
 
    (['log'], {'revision': [<RevisionSpec_revno revno:500>, <RevisionSpec_int 600>]})
416
899
    """
417
 
    args = []
418
 
    opts = {}
419
 
 
420
 
    argsover = False
421
 
    while argv:
422
 
        a = argv.pop(0)
423
 
        if not argsover and a[0] == '-':
424
 
            # option names must not be unicode
425
 
            a = str(a)
426
 
            optarg = None
427
 
            if a[1] == '-':
428
 
                if a == '--':
429
 
                    # We've received a standalone -- No more flags
430
 
                    argsover = True
431
 
                    continue
432
 
                mutter("  got option %r" % a)
433
 
                if '=' in a:
434
 
                    optname, optarg = a[2:].split('=', 1)
435
 
                else:
436
 
                    optname = a[2:]
437
 
                if optname not in OPTIONS:
438
 
                    raise BzrError('unknown long option %r' % a)
439
 
            else:
440
 
                shortopt = a[1:]
441
 
                if shortopt in SHORT_OPTIONS:
442
 
                    # Multi-character options must have a space to delimit
443
 
                    # their value
444
 
                    optname = SHORT_OPTIONS[shortopt]
445
 
                else:
446
 
                    # Single character short options, can be chained,
447
 
                    # and have their value appended to their name
448
 
                    shortopt = a[1:2]
449
 
                    if shortopt not in SHORT_OPTIONS:
450
 
                        # We didn't find the multi-character name, and we
451
 
                        # didn't find the single char name
452
 
                        raise BzrError('unknown short option %r' % a)
453
 
                    optname = SHORT_OPTIONS[shortopt]
454
 
 
455
 
                    if a[2:]:
456
 
                        # There are extra things on this option
457
 
                        # see if it is the value, or if it is another
458
 
                        # short option
459
 
                        optargfn = OPTIONS[optname]
460
 
                        if optargfn is None:
461
 
                            # This option does not take an argument, so the
462
 
                            # next entry is another short option, pack it back
463
 
                            # into the list
464
 
                            argv.insert(0, '-' + a[2:])
465
 
                        else:
466
 
                            # This option takes an argument, so pack it
467
 
                            # into the array
468
 
                            optarg = a[2:]
469
 
            
470
 
            if optname in opts:
471
 
                # XXX: Do we ever want to support this, e.g. for -r?
472
 
                raise BzrError('repeated option %r' % a)
473
 
                
474
 
            optargfn = OPTIONS[optname]
475
 
            if optargfn:
476
 
                if optarg == None:
477
 
                    if not argv:
478
 
                        raise BzrError('option %r needs an argument' % a)
479
 
                    else:
480
 
                        optarg = argv.pop(0)
481
 
                opts[optname] = optargfn(optarg)
482
 
            else:
483
 
                if optarg != None:
484
 
                    raise BzrError('option %r takes no argument' % optname)
485
 
                opts[optname] = True
486
 
        else:
487
 
            args.append(a)
488
 
 
 
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.CommandError(
 
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)
489
918
    return args, opts
490
919
 
491
920
 
492
 
 
493
 
 
494
921
def _match_argform(cmd, takes_args, args):
495
922
    argdict = {}
496
923
 
500
927
        if ap[-1] == '?':
501
928
            if args:
502
929
                argdict[argname] = args.pop(0)
503
 
        elif ap[-1] == '*': # all remaining arguments
 
930
        elif ap[-1] == '*':  # all remaining arguments
504
931
            if args:
505
932
                argdict[argname + '_list'] = args[:]
506
933
                args = []
508
935
                argdict[argname + '_list'] = None
509
936
        elif ap[-1] == '+':
510
937
            if not args:
511
 
                raise BzrCommandError("command %r needs one or more %s"
512
 
                        % (cmd, argname.upper()))
 
938
                raise errors.CommandError(gettext(
 
939
                    "command {0!r} needs one or more {1}").format(
 
940
                    cmd, argname.upper()))
513
941
            else:
514
942
                argdict[argname + '_list'] = args[:]
515
943
                args = []
516
 
        elif ap[-1] == '$': # all but one
 
944
        elif ap[-1] == '$':  # all but one
517
945
            if len(args) < 2:
518
 
                raise BzrCommandError("command %r needs one or more %s"
519
 
                        % (cmd, argname.upper()))
 
946
                raise errors.CommandError(
 
947
                    gettext("command {0!r} needs one or more {1}").format(
 
948
                        cmd, argname.upper()))
520
949
            argdict[argname + '_list'] = args[:-1]
521
 
            args[:-1] = []                
 
950
            args[:-1] = []
522
951
        else:
523
952
            # just a plain arg
524
953
            argname = ap
525
954
            if not args:
526
 
                raise BzrCommandError("command %r requires argument %s"
527
 
                        % (cmd, argname.upper()))
 
955
                raise errors.CommandError(
 
956
                    gettext("command {0!r} requires argument {1}").format(
 
957
                        cmd, argname.upper()))
528
958
            else:
529
959
                argdict[argname] = args.pop(0)
530
 
            
 
960
 
531
961
    if args:
532
 
        raise BzrCommandError("extra argument to command %s: %s"
533
 
                              % (cmd, args[0]))
 
962
        raise errors.CommandError(gettext(
 
963
            "extra argument to command {0}: {1}").format(
 
964
            cmd, args[0]))
534
965
 
535
966
    return argdict
536
967
 
537
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
 
538
984
 
539
985
def apply_profiled(the_callable, *args, **kwargs):
540
986
    import hotshot
541
987
    import tempfile
 
988
    import hotshot.stats
542
989
    pffileno, pfname = tempfile.mkstemp()
543
990
    try:
544
991
        prof = hotshot.Profile(pfname)
545
992
        try:
546
 
            ret = prof.runcall(the_callable, *args, **kwargs) or 0
 
993
            ret = prof.runcall(exception_to_return_code, the_callable, *args,
 
994
                               **kwargs) or 0
547
995
        finally:
548
996
            prof.close()
549
 
 
550
 
        import hotshot.stats
551
997
        stats = hotshot.stats.load(pfname)
552
 
        #stats.strip_dirs()
553
 
        stats.sort_stats('time')
554
 
        ## XXX: Might like to write to stderr or the trace file instead but
555
 
        ## print_stats seems hardcoded to stdout
 
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
556
1002
        stats.print_stats(20)
557
 
 
558
1003
        return ret
559
1004
    finally:
560
1005
        os.close(pffileno)
561
1006
        os.remove(pfname)
562
1007
 
563
1008
 
564
 
def run_bzr(argv):
 
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):
565
1063
    """Execute a command.
566
1064
 
567
 
    This is similar to main(), but without all the trappings for
568
 
    logging and error handling.  
569
 
    
570
 
    argv
571
 
       The command-line arguments, without the program name from argv[0]
572
 
    
573
 
    Returns a command status or raises an exception.
 
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.
574
1077
 
575
1078
    Special master options: these must come before the command because
576
1079
    they control how the command is interpreted.
578
1081
    --no-plugins
579
1082
        Do not load plugin modules at all
580
1083
 
 
1084
    --no-aliases
 
1085
        Do not allow aliases
 
1086
 
581
1087
    --builtin
582
1088
        Only use builtin commands.  (Plugins are still allowed to change
583
1089
        other behaviour.)
584
1090
 
585
1091
    --profile
586
 
        Run under the Python profiler.
 
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).
587
1103
    """
588
 
    
589
 
    argv = [a.decode(bzrlib.user_encoding) for a in argv]
 
1104
    trace.mutter("breezy version: " + breezy.__version__)
 
1105
    argv = _specified_or_unicode_argv(argv)
 
1106
    trace.mutter("brz arguments: %r", argv)
590
1107
 
591
 
    opt_profile = opt_no_plugins = opt_builtin = False
 
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
592
1111
 
593
1112
    # --no-plugins is handled specially at a very early stage. We need
594
1113
    # to load plugins before doing other command parsing so that they
595
1114
    # can override commands, but this needs to happen first.
596
1115
 
597
 
    for a in argv:
 
1116
    argv_copy = []
 
1117
    i = 0
 
1118
    override_config = []
 
1119
    while i < len(argv):
 
1120
        a = argv[i]
598
1121
        if a == '--profile':
599
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
600
1129
        elif a == '--no-plugins':
601
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
602
1135
        elif a == '--builtin':
603
1136
            opt_builtin = True
604
 
        else:
605
 
            break
606
 
        argv.remove(a)
607
 
 
608
 
    if (not argv) or (argv[0] == '--help'):
609
 
        from bzrlib.help import help
610
 
        if len(argv) > 1:
611
 
            help(argv[1])
612
 
        else:
613
 
            help()
 
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
        from breezy import config
 
1159
        c = config.GlobalConfig()
 
1160
        warn_load_problems = not c.suppress_warning('plugin_load_failure')
 
1161
        load_plugins(warn_load_problems=warn_load_problems)
 
1162
    else:
 
1163
        disable_plugins()
 
1164
 
 
1165
    argv = argv_copy
 
1166
    if (not argv):
 
1167
        get_cmd_object('help').run_argv_aliases([])
614
1168
        return 0
615
1169
 
616
1170
    if argv[0] == '--version':
617
 
        from bzrlib.builtins import show_version
618
 
        show_version()
 
1171
        get_cmd_object('version').run_argv_aliases([])
619
1172
        return 0
620
 
        
621
 
    if not opt_no_plugins:
622
 
        from bzrlib.plugin import load_plugins
623
 
        load_plugins()
624
 
 
625
 
    cmd = str(argv.pop(0))
626
 
 
 
1173
 
 
1174
    alias_argv = None
 
1175
 
 
1176
    if not opt_no_aliases:
 
1177
        alias_argv = get_alias(argv[0])
 
1178
        if alias_argv:
 
1179
            argv[0] = alias_argv.pop(0)
 
1180
 
 
1181
    cmd = argv.pop(0)
627
1182
    cmd_obj = get_cmd_object(cmd, plugins_override=not opt_builtin)
628
 
 
629
 
    if opt_profile:
630
 
        ret = apply_profiled(cmd_obj.run_argv, argv)
631
 
    else:
632
 
        ret = cmd_obj.run_argv(argv)
633
 
    return ret or 0
634
 
 
635
 
 
636
 
def main(argv):
637
 
    import bzrlib.ui
638
 
    bzrlib.trace.log_startup(argv)
639
 
    bzrlib.ui.ui_factory = bzrlib.ui.TextUIFactory()
640
 
 
641
 
    return run_bzr_catch_errors(argv[1:])
 
1183
    if opt_no_l10n:
 
1184
        cmd_obj.l10n = False
 
1185
    run = cmd_obj.run_argv_aliases
 
1186
    run_argv = [argv, alias_argv]
 
1187
 
 
1188
    try:
 
1189
        # We can be called recursively (tests for example), but we don't want
 
1190
        # the verbosity level to propagate.
 
1191
        saved_verbosity_level = option._verbosity_level
 
1192
        option._verbosity_level = 0
 
1193
        if opt_lsprof:
 
1194
            if opt_coverage:
 
1195
                trace.warning(
 
1196
                    '--coverage ignored, because --lsprof is in use.')
 
1197
            ret = apply_lsprofiled(opt_lsprof_file, run, *run_argv)
 
1198
        elif opt_profile:
 
1199
            if opt_coverage:
 
1200
                trace.warning(
 
1201
                    '--coverage ignored, because --profile is in use.')
 
1202
            ret = apply_profiled(run, *run_argv)
 
1203
        elif opt_coverage:
 
1204
            ret = apply_coveraged(run, *run_argv)
 
1205
        else:
 
1206
            ret = run(*run_argv)
 
1207
        return ret or 0
 
1208
    finally:
 
1209
        # reset, in case we may do other commands later within the same
 
1210
        # process. Commands that want to execute sub-commands must propagate
 
1211
        # --verbose in their own way.
 
1212
        if 'memory' in debug.debug_flags:
 
1213
            trace.debug_memory('Process status after command:', short=False)
 
1214
        option._verbosity_level = saved_verbosity_level
 
1215
        # Reset the overrides
 
1216
        cmdline_overrides._reset()
 
1217
 
 
1218
 
 
1219
def display_command(func):
 
1220
    """Decorator that suppresses pipe/interrupt errors."""
 
1221
    def ignore_pipe(*args, **kwargs):
 
1222
        try:
 
1223
            result = func(*args, **kwargs)
 
1224
            sys.stdout.flush()
 
1225
            return result
 
1226
        except IOError as e:
 
1227
            if getattr(e, 'errno', None) is None:
 
1228
                raise
 
1229
            if e.errno != errno.EPIPE:
 
1230
                # Win32 raises IOError with errno=0 on a broken pipe
 
1231
                if sys.platform != 'win32' or (e.errno not in (0, errno.EINVAL)):
 
1232
                    raise
 
1233
            pass
 
1234
        except KeyboardInterrupt:
 
1235
            pass
 
1236
    return ignore_pipe
 
1237
 
 
1238
 
 
1239
def install_bzr_command_hooks():
 
1240
    """Install the hooks to supply bzr's own commands."""
 
1241
    if _list_bzr_commands in Command.hooks["list_commands"]:
 
1242
        return
 
1243
    Command.hooks.install_named_hook("list_commands", _list_bzr_commands,
 
1244
                                     "bzr commands")
 
1245
    Command.hooks.install_named_hook("get_command", _get_bzr_command,
 
1246
                                     "bzr commands")
 
1247
    Command.hooks.install_named_hook("get_command", _get_plugin_command,
 
1248
                                     "bzr plugin commands")
 
1249
    Command.hooks.install_named_hook("get_command", _get_external_command,
 
1250
                                     "bzr external command lookup")
 
1251
    Command.hooks.install_named_hook("get_missing_command",
 
1252
                                     _try_plugin_provider,
 
1253
                                     "bzr plugin-provider-db check")
 
1254
 
 
1255
 
 
1256
def _specified_or_unicode_argv(argv):
 
1257
    # For internal or testing use, argv can be passed.  Otherwise, get it from
 
1258
    # the process arguments.
 
1259
    if argv is None:
 
1260
        return sys.argv[1:]
 
1261
    new_argv = []
 
1262
    try:
 
1263
        # ensure all arguments are unicode strings
 
1264
        for a in argv:
 
1265
            if not isinstance(a, str):
 
1266
                raise ValueError('not native str or unicode: %r' % (a,))
 
1267
            new_argv.append(a)
 
1268
    except (ValueError, UnicodeDecodeError):
 
1269
        raise errors.BzrError("argv should be list of unicode strings.")
 
1270
    return new_argv
 
1271
 
 
1272
 
 
1273
def main(argv=None):
 
1274
    """Main entry point of command-line interface.
 
1275
 
 
1276
    Typically `breezy.initialize` should be called first.
 
1277
 
 
1278
    :param argv: list of unicode command-line arguments similar to sys.argv.
 
1279
        argv[0] is script name usually, it will be ignored.
 
1280
        Don't pass here sys.argv because this list contains plain strings
 
1281
        and not unicode; pass None instead.
 
1282
 
 
1283
    :return: exit code of brz command.
 
1284
    """
 
1285
    if argv is not None:
 
1286
        argv = argv[1:]
 
1287
    _register_builtin_commands()
 
1288
    ret = run_bzr_catch_errors(argv)
 
1289
    trace.mutter("return code %d", ret)
 
1290
    return ret
642
1291
 
643
1292
 
644
1293
def run_bzr_catch_errors(argv):
 
1294
    """Run a bzr command with parameters as described by argv.
 
1295
 
 
1296
    This function assumed that that UI layer is setup, that symbol deprecations
 
1297
    are already applied, and that unicode decoding has already been performed
 
1298
    on argv.
 
1299
    """
 
1300
    # done here so that they're covered for every test run
 
1301
    install_bzr_command_hooks()
 
1302
    return exception_to_return_code(run_bzr, argv)
 
1303
 
 
1304
 
 
1305
def run_bzr_catch_user_errors(argv):
 
1306
    """Run brz and report user errors, but let internal errors propagate.
 
1307
 
 
1308
    This is used for the test suite, and might be useful for other programs
 
1309
    that want to wrap the commandline interface.
 
1310
    """
 
1311
    # done here so that they're covered for every test run
 
1312
    install_bzr_command_hooks()
645
1313
    try:
 
1314
        return run_bzr(argv)
 
1315
    except Exception as e:
 
1316
        if (isinstance(e, (OSError, IOError))
 
1317
                or not getattr(e, 'internal_error', True)):
 
1318
            trace.report_exception(sys.exc_info(), sys.stderr)
 
1319
            return 3
 
1320
        else:
 
1321
            raise
 
1322
 
 
1323
 
 
1324
class HelpCommandIndex(object):
 
1325
    """A index for bzr help that returns commands."""
 
1326
 
 
1327
    def __init__(self):
 
1328
        self.prefix = 'commands/'
 
1329
 
 
1330
    def get_topics(self, topic):
 
1331
        """Search for topic amongst commands.
 
1332
 
 
1333
        :param topic: A topic to search for.
 
1334
        :return: A list which is either empty or contains a single
 
1335
            Command entry.
 
1336
        """
 
1337
        if topic and topic.startswith(self.prefix):
 
1338
            topic = topic[len(self.prefix):]
646
1339
        try:
647
 
            try:
648
 
                return run_bzr(argv)
649
 
            finally:
650
 
                # do this here inside the exception wrappers to catch EPIPE
651
 
                sys.stdout.flush()
652
 
        #wrap common errors as CommandErrors.
653
 
        except (NotBranchError,), e:
654
 
            raise BzrCommandError(str(e))
655
 
    except BzrCommandError, e:
656
 
        # command line syntax error, etc
657
 
        log_error(str(e))
658
 
        return 1
659
 
    except BzrError, e:
660
 
        bzrlib.trace.log_exception()
661
 
        return 1
662
 
    except AssertionError, e:
663
 
        bzrlib.trace.log_exception('assertion failed: ' + str(e))
664
 
        return 3
665
 
    except KeyboardInterrupt, e:
666
 
        bzrlib.trace.note('interrupted')
667
 
        return 2
668
 
    except Exception, e:
669
 
        import errno
670
 
        if (isinstance(e, IOError) 
671
 
            and hasattr(e, 'errno')
672
 
            and e.errno == errno.EPIPE):
673
 
            bzrlib.trace.note('broken pipe')
674
 
            return 2
 
1340
            cmd = _get_cmd_object(topic, check_missing=False)
 
1341
        except KeyError:
 
1342
            return []
675
1343
        else:
676
 
            bzrlib.trace.log_exception()
677
 
            return 2
678
 
 
679
 
 
680
 
if __name__ == '__main__':
681
 
    sys.exit(main(sys.argv))
 
1344
            return [cmd]
 
1345
 
 
1346
 
 
1347
class Provider(object):
 
1348
    """Generic class to be overriden by plugins"""
 
1349
 
 
1350
    def plugin_for_command(self, cmd_name):
 
1351
        """Takes a command and returns the information for that plugin
 
1352
 
 
1353
        :return: A dictionary with all the available information
 
1354
            for the requested plugin
 
1355
        """
 
1356
        raise NotImplementedError
 
1357
 
 
1358
 
 
1359
class ProvidersRegistry(registry.Registry):
 
1360
    """This registry exists to allow other providers to exist"""
 
1361
 
 
1362
    def __iter__(self):
 
1363
        for key, provider in self.items():
 
1364
            yield provider
 
1365
 
 
1366
 
 
1367
command_providers_registry = ProvidersRegistry()