~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Vincent Ladeuil
  • Date: 2009-12-14 15:51:36 UTC
  • mto: (4894.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 4895.
  • Revision ID: v.ladeuil+lp@free.fr-20091214155136-rf4nkqvxda9oiw4u
Cleanup tests and tweak the text displayed.

* bzrlib/tests/blackbox/test_update.py:
Fix imports and replace the assertContainsRe with assertEqualDiff
to make the test clearer, more robust and easier to debug.

* bzrlib/tests/commands/test_update.py: 
Fix imports.

* bzrlib/tests/blackbox/test_filtered_view_ops.py: 
Fix imports and strange accesses to base class methods.
(TestViewTreeOperations.test_view_on_update): Avoid os.chdir()
call, simplify string matching assertions.

* bzrlib/builtins.py:
(cmd_update.run): Fix spurious space, get rid of the final '/' for
the base path, don't add a final period (it's a legal char in a
path and would be annoying for people that like to copy/paste).

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2004, 2005 by Canonical Ltd
2
 
 
 
1
# Copyright (C) 2006, 2008 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
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
 
18
18
# TODO: probably should say which arguments are candidates for glob
19
19
# expansion on windows and do that at the command level.
20
20
 
21
 
# TODO: Help messages for options.
22
 
 
23
21
# TODO: Define arguments by objects, rather than just using names.
24
22
# Those objects can specify the expected type of the argument, which
25
 
# would help with validation and shell completion.
26
 
 
27
 
 
28
 
 
 
23
# would help with validation and shell completion.  They could also provide
 
24
# help/explanation for that argument in a structured way.
 
25
 
 
26
# TODO: Specific "examples" property on commands for consistent formatting.
 
27
 
 
28
# TODO: "--profile=cum", to change sort order.  Is there any value in leaving
 
29
# the profile output behind so it can be interactively examined?
 
30
 
 
31
import os
29
32
import sys
30
 
import os
 
33
 
 
34
from bzrlib.lazy_import import lazy_import
 
35
lazy_import(globals(), """
 
36
import codecs
 
37
import errno
 
38
import threading
31
39
from warnings import warn
32
 
from inspect import getdoc
33
40
 
34
41
import bzrlib
35
 
import bzrlib.trace
36
 
from bzrlib.trace import mutter, note, log_error, warning
37
 
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError
38
 
from bzrlib.branch import find_branch
39
 
from bzrlib import BZRDIR
40
 
 
41
 
plugin_cmds = {}
42
 
 
43
 
 
44
 
def register_command(cmd):
45
 
    "Utility function to help register a command"
 
42
from bzrlib import (
 
43
    debug,
 
44
    errors,
 
45
    option,
 
46
    osutils,
 
47
    trace,
 
48
    win32utils,
 
49
    )
 
50
""")
 
51
 
 
52
from bzrlib.hooks import HookPoint, Hooks
 
53
# Compatibility - Option used to be in commands.
 
54
from bzrlib.option import Option
 
55
from bzrlib import registry
 
56
from bzrlib.symbol_versioning import (
 
57
    deprecated_function,
 
58
    deprecated_in,
 
59
    deprecated_method,
 
60
    suppress_deprecation_warnings,
 
61
    )
 
62
 
 
63
 
 
64
class CommandInfo(object):
 
65
    """Information about a command."""
 
66
 
 
67
    def __init__(self, aliases):
 
68
        """The list of aliases for the command."""
 
69
        self.aliases = aliases
 
70
 
 
71
    @classmethod
 
72
    def from_command(klass, command):
 
73
        """Factory to construct a CommandInfo from a command."""
 
74
        return klass(command.aliases)
 
75
 
 
76
 
 
77
class CommandRegistry(registry.Registry):
 
78
 
 
79
    @staticmethod
 
80
    def _get_name(command_name):
 
81
        if command_name.startswith("cmd_"):
 
82
            return _unsquish_command_name(command_name)
 
83
        else:
 
84
            return command_name
 
85
 
 
86
    def register(self, cmd, decorate=False):
 
87
        """Utility function to help register a command
 
88
 
 
89
        :param cmd: Command subclass to register
 
90
        :param decorate: If true, allow overriding an existing command
 
91
            of the same name; the old command is returned by this function.
 
92
            Otherwise it is an error to try to override an existing command.
 
93
        """
 
94
        k = cmd.__name__
 
95
        k_unsquished = self._get_name(k)
 
96
        try:
 
97
            previous = self.get(k_unsquished)
 
98
        except KeyError:
 
99
            previous = _builtin_commands().get(k_unsquished)
 
100
        info = CommandInfo.from_command(cmd)
 
101
        try:
 
102
            registry.Registry.register(self, k_unsquished, cmd,
 
103
                                       override_existing=decorate, info=info)
 
104
        except KeyError:
 
105
            trace.warning('Two plugins defined the same command: %r' % k)
 
106
            trace.warning('Not loading the one in %r' %
 
107
                sys.modules[cmd.__module__])
 
108
            trace.warning('Previously this command was registered from %r' %
 
109
                sys.modules[previous.__module__])
 
110
        return previous
 
111
 
 
112
    def register_lazy(self, command_name, aliases, module_name):
 
113
        """Register a command without loading its module.
 
114
 
 
115
        :param command_name: The primary name of the command.
 
116
        :param aliases: A list of aliases for the command.
 
117
        :module_name: The module that the command lives in.
 
118
        """
 
119
        key = self._get_name(command_name)
 
120
        registry.Registry.register_lazy(self, key, module_name, command_name,
 
121
                                        info=CommandInfo(aliases))
 
122
 
 
123
 
 
124
plugin_cmds = CommandRegistry()
 
125
 
 
126
 
 
127
def register_command(cmd, decorate=False):
46
128
    global plugin_cmds
47
 
    k = cmd.__name__
48
 
    if k.startswith("cmd_"):
49
 
        k_unsquished = _unsquish_command_name(k)
50
 
    else:
51
 
        k_unsquished = k
52
 
    if not plugin_cmds.has_key(k_unsquished):
53
 
        plugin_cmds[k_unsquished] = cmd
54
 
        mutter('registered plugin command %s', k_unsquished)      
55
 
    else:
56
 
        log_error('Two plugins defined the same command: %r' % k)
57
 
        log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
 
129
    return plugin_cmds.register(cmd, decorate)
58
130
 
59
131
 
60
132
def _squish_command_name(cmd):
62
134
 
63
135
 
64
136
def _unsquish_command_name(cmd):
65
 
    assert cmd.startswith("cmd_")
66
137
    return cmd[4:].replace('_','-')
67
138
 
68
139
 
69
 
def _parse_revision_str(revstr):
70
 
    """This handles a revision string -> revno.
71
 
 
72
 
    This always returns a list.  The list will have one element for 
73
 
 
74
 
    It supports integers directly, but everything else it
75
 
    defers for passing to Branch.get_revision_info()
76
 
 
77
 
    >>> _parse_revision_str('234')
78
 
    [234]
79
 
    >>> _parse_revision_str('234..567')
80
 
    [234, 567]
81
 
    >>> _parse_revision_str('..')
82
 
    [None, None]
83
 
    >>> _parse_revision_str('..234')
84
 
    [None, 234]
85
 
    >>> _parse_revision_str('234..')
86
 
    [234, None]
87
 
    >>> _parse_revision_str('234..456..789') # Maybe this should be an error
88
 
    [234, 456, 789]
89
 
    >>> _parse_revision_str('234....789') # Error?
90
 
    [234, None, 789]
91
 
    >>> _parse_revision_str('revid:test@other.com-234234')
92
 
    ['revid:test@other.com-234234']
93
 
    >>> _parse_revision_str('revid:test@other.com-234234..revid:test@other.com-234235')
94
 
    ['revid:test@other.com-234234', 'revid:test@other.com-234235']
95
 
    >>> _parse_revision_str('revid:test@other.com-234234..23')
96
 
    ['revid:test@other.com-234234', 23]
97
 
    >>> _parse_revision_str('date:2005-04-12')
98
 
    ['date:2005-04-12']
99
 
    >>> _parse_revision_str('date:2005-04-12 12:24:33')
100
 
    ['date:2005-04-12 12:24:33']
101
 
    >>> _parse_revision_str('date:2005-04-12T12:24:33')
102
 
    ['date:2005-04-12T12:24:33']
103
 
    >>> _parse_revision_str('date:2005-04-12,12:24:33')
104
 
    ['date:2005-04-12,12:24:33']
105
 
    >>> _parse_revision_str('-5..23')
106
 
    [-5, 23]
107
 
    >>> _parse_revision_str('-5')
108
 
    [-5]
109
 
    >>> _parse_revision_str('123a')
110
 
    ['123a']
111
 
    >>> _parse_revision_str('abc')
112
 
    ['abc']
113
 
    """
114
 
    import re
115
 
    old_format_re = re.compile('\d*:\d*')
116
 
    m = old_format_re.match(revstr)
117
 
    if m:
118
 
        warning('Colon separator for revision numbers is deprecated.'
119
 
                ' Use .. instead')
120
 
        revs = []
121
 
        for rev in revstr.split(':'):
122
 
            if rev:
123
 
                revs.append(int(rev))
124
 
            else:
125
 
                revs.append(None)
126
 
        return revs
127
 
    revs = []
128
 
    for x in revstr.split('..'):
129
 
        if not x:
130
 
            revs.append(None)
131
 
        else:
132
 
            try:
133
 
                revs.append(int(x))
134
 
            except ValueError:
135
 
                revs.append(x)
136
 
    return revs
137
 
 
138
 
 
139
 
def get_merge_type(typestring):
140
 
    """Attempt to find the merge class/factory associated with a string."""
141
 
    from merge import merge_types
142
 
    try:
143
 
        return merge_types[typestring][0]
144
 
    except KeyError:
145
 
        templ = '%s%%7s: %%s' % (' '*12)
146
 
        lines = [templ % (f[0], f[1][1]) for f in merge_types.iteritems()]
147
 
        type_list = '\n'.join(lines)
148
 
        msg = "No known merge type %s. Supported types are:\n%s" %\
149
 
            (typestring, type_list)
150
 
        raise BzrCommandError(msg)
151
 
    
152
 
 
153
140
def _builtin_commands():
154
141
    import bzrlib.builtins
 
142
    return _scan_module_for_commands(bzrlib.builtins)
 
143
 
 
144
 
 
145
def _scan_module_for_commands(module):
155
146
    r = {}
156
 
    builtins = bzrlib.builtins.__dict__
157
 
    for name in builtins:
 
147
    for name, obj in module.__dict__.iteritems():
158
148
        if name.startswith("cmd_"):
159
 
            real_name = _unsquish_command_name(name)        
160
 
            r[real_name] = builtins[name]
 
149
            real_name = _unsquish_command_name(name)
 
150
            r[real_name] = obj
161
151
    return r
162
152
 
163
 
            
 
153
 
 
154
def _list_bzr_commands(names):
 
155
    """Find commands from bzr's core and plugins."""
 
156
    # to eliminate duplicates
 
157
    names.update(builtin_command_names())
 
158
    names.update(plugin_command_names())
 
159
    return names
 
160
 
 
161
 
 
162
def all_command_names():
 
163
    """Return a set of all command names."""
 
164
    names = set()
 
165
    for hook in Command.hooks['list_commands']:
 
166
        names = hook(names)
 
167
        if names is None:
 
168
            raise AssertionError(
 
169
                'hook %s returned None' % Command.hooks.get_hook_name(hook))
 
170
    return names
 
171
 
164
172
 
165
173
def builtin_command_names():
166
 
    """Return list of builtin command names."""
 
174
    """Return list of builtin command names.
 
175
    
 
176
    Use of all_command_names() is encouraged rather than builtin_command_names
 
177
    and/or plugin_command_names.
 
178
    """
167
179
    return _builtin_commands().keys()
168
 
    
 
180
 
169
181
 
170
182
def plugin_command_names():
 
183
    """Returns command names from commands registered by plugins."""
171
184
    return plugin_cmds.keys()
172
185
 
173
186
 
174
 
def _get_cmd_dict(plugins_override=True):
175
 
    """Return name->class mapping for all commands."""
 
187
@deprecated_function(deprecated_in((1, 17, 0)))
 
188
def get_all_cmds(plugins_override=False):
 
189
    """Return canonical name and class for most commands.
 
190
    
 
191
    NB: This does not return all commands since the introduction of
 
192
    command hooks, and returning the class is not sufficient to 
 
193
    get correctly setup commands, which is why it is deprecated.
 
194
 
 
195
    Use 'all_command_names' + 'get_cmd_object' instead.
 
196
    """
176
197
    d = _builtin_commands()
177
198
    if plugins_override:
178
 
        d.update(plugin_cmds)
179
 
    return d
180
 
 
181
 
    
182
 
def get_all_cmds(plugins_override=True):
183
 
    """Return canonical name and class for all registered commands."""
184
 
    for k, v in _get_cmd_dict(plugins_override=plugins_override).iteritems():
 
199
        d.update(plugin_cmds.iteritems())
 
200
    for k, v in d.iteritems():
185
201
        yield k,v
186
202
 
187
203
 
188
204
def get_cmd_object(cmd_name, plugins_override=True):
189
 
    """Return the canonical name and command class for a command.
 
205
    """Return the command object for a command.
190
206
 
191
207
    plugins_override
192
208
        If true, plugin commands can override builtins.
193
209
    """
194
 
    from bzrlib.externalcommand import ExternalCommand
195
 
 
196
 
    cmd_name = str(cmd_name)            # not unicode
197
 
 
198
 
    # first look up this command under the specified name
199
 
    cmds = _get_cmd_dict(plugins_override=plugins_override)
 
210
    try:
 
211
        return _get_cmd_object(cmd_name, plugins_override)
 
212
    except KeyError:
 
213
        raise errors.BzrCommandError('unknown command "%s"' % cmd_name)
 
214
 
 
215
 
 
216
def _get_cmd_object(cmd_name, plugins_override=True):
 
217
    """Get a command object.
 
218
 
 
219
    :param cmd_name: The name of the command.
 
220
    :param plugins_override: Allow plugins to override builtins.
 
221
    :return: A Command object instance
 
222
    :raises KeyError: If no command is found.
 
223
    """
 
224
    # We want only 'ascii' command names, but the user may have typed
 
225
    # in a Unicode name. In that case, they should just get a
 
226
    # 'command not found' error later.
 
227
    # In the future, we may actually support Unicode command names.
 
228
    cmd = None
 
229
    # Get a command
 
230
    for hook in Command.hooks['get_command']:
 
231
        cmd = hook(cmd, cmd_name)
 
232
        if cmd is not None and not plugins_override and not cmd.plugin_name():
 
233
            # We've found a non-plugin command, don't permit it to be
 
234
            # overridden.
 
235
            break
 
236
    if cmd is None:
 
237
        for hook in Command.hooks['get_missing_command']:
 
238
            cmd = hook(cmd_name)
 
239
            if cmd is not None:
 
240
                break
 
241
    if cmd is None:
 
242
        # No command found.
 
243
        raise KeyError
 
244
    # Allow plugins to extend commands
 
245
    for hook in Command.hooks['extend_command']:
 
246
        hook(cmd)
 
247
    return cmd
 
248
 
 
249
 
 
250
def _try_plugin_provider(cmd_name):
 
251
    """Probe for a plugin provider having cmd_name."""
 
252
    try:
 
253
        plugin_metadata, provider = probe_for_provider(cmd_name)
 
254
        raise errors.CommandAvailableInPlugin(cmd_name,
 
255
            plugin_metadata, provider)
 
256
    except errors.NoPluginAvailable:
 
257
        pass
 
258
 
 
259
 
 
260
def probe_for_provider(cmd_name):
 
261
    """Look for a provider for cmd_name.
 
262
 
 
263
    :param cmd_name: The command name.
 
264
    :return: plugin_metadata, provider for getting cmd_name.
 
265
    :raises NoPluginAvailable: When no provider can supply the plugin.
 
266
    """
 
267
    # look for providers that provide this command but aren't installed
 
268
    for provider in command_providers_registry:
 
269
        try:
 
270
            return provider.plugin_for_command(cmd_name), provider
 
271
        except errors.NoPluginAvailable:
 
272
            pass
 
273
    raise errors.NoPluginAvailable(cmd_name)
 
274
 
 
275
 
 
276
def _get_bzr_command(cmd_or_None, cmd_name):
 
277
    """Get a command from bzr's core."""
 
278
    cmds = _builtin_commands()
200
279
    try:
201
280
        return cmds[cmd_name]()
202
281
    except KeyError:
203
282
        pass
204
 
 
205
283
    # look for any command which claims this as an alias
206
284
    for real_cmd_name, cmd_class in cmds.iteritems():
207
285
        if cmd_name in cmd_class.aliases:
208
286
            return cmd_class()
209
 
 
 
287
    return cmd_or_None
 
288
 
 
289
 
 
290
def _get_external_command(cmd_or_None, cmd_name):
 
291
    """Lookup a command that is a shell script."""
 
292
    # Only do external command lookups when no command is found so far.
 
293
    if cmd_or_None is not None:
 
294
        return cmd_or_None
 
295
    from bzrlib.externalcommand import ExternalCommand
210
296
    cmd_obj = ExternalCommand.find_command(cmd_name)
211
297
    if cmd_obj:
212
298
        return cmd_obj
213
299
 
214
 
    raise BzrCommandError("unknown command %r" % cmd_name)
 
300
 
 
301
def _get_plugin_command(cmd_or_None, cmd_name):
 
302
    """Get a command from bzr's plugins."""
 
303
    try:
 
304
        return plugin_cmds.get(cmd_name)()
 
305
    except KeyError:
 
306
        pass
 
307
    for key in plugin_cmds.keys():
 
308
        info = plugin_cmds.get_info(key)
 
309
        if cmd_name in info.aliases:
 
310
            return plugin_cmds.get(key)()
 
311
    return cmd_or_None
215
312
 
216
313
 
217
314
class Command(object):
239
336
        List of argument forms, marked with whether they are optional,
240
337
        repeated, etc.
241
338
 
 
339
                Examples:
 
340
 
 
341
                ['to_location', 'from_branch?', 'file*']
 
342
 
 
343
                'to_location' is required
 
344
                'from_branch' is optional
 
345
                'file' can be specified 0 or more times
 
346
 
242
347
    takes_options
243
 
        List of options that may be given for this command.
 
348
        List of options that may be given for this command.  These can
 
349
        be either strings, referring to globally-defined options,
 
350
        or option objects.  Retrieve through options().
244
351
 
245
352
    hidden
246
353
        If true, this command isn't advertised.  This is typically
247
354
        for commands intended for expert users.
 
355
 
 
356
    encoding_type
 
357
        Command objects will get a 'outf' attribute, which has been
 
358
        setup to properly handle encoding of unicode strings.
 
359
        encoding_type determines what will happen when characters cannot
 
360
        be encoded
 
361
            strict - abort if we cannot decode
 
362
            replace - put in a bogus character (typically '?')
 
363
            exact - do not encode sys.stdout
 
364
 
 
365
            NOTE: by default on Windows, sys.stdout is opened as a text
 
366
            stream, therefore LF line-endings are converted to CRLF.
 
367
            When a command uses encoding_type = 'exact', then
 
368
            sys.stdout is forced to be a binary stream, and line-endings
 
369
            will not mangled.
 
370
 
 
371
    :cvar hooks: An instance of CommandHooks.
248
372
    """
249
373
    aliases = []
250
 
    
251
374
    takes_args = []
252
375
    takes_options = []
 
376
    encoding_type = 'strict'
253
377
 
254
378
    hidden = False
255
 
    
 
379
 
256
380
    def __init__(self):
257
381
        """Construct an instance of this command."""
258
382
        if self.__doc__ == Command.__doc__:
259
383
            warn("No help message set for %r" % self)
260
 
 
261
 
 
262
 
    def run_argv(self, argv):
263
 
        """Parse command line and run."""
264
 
        args, opts = parse_args(argv)
265
 
 
 
384
        # List of standard options directly supported
 
385
        self.supported_std_options = []
 
386
 
 
387
    @deprecated_method(deprecated_in((2, 1, 0)))
 
388
    def _maybe_expand_globs(self, file_list):
 
389
        """Glob expand file_list if the platform does not do that itself.
 
390
 
 
391
        Not used anymore, now that the bzr command-line parser globs on
 
392
        Windows.
 
393
 
 
394
        :return: A possibly empty list of unicode paths.
 
395
 
 
396
        Introduced in bzrlib 0.18.
 
397
        """
 
398
        return file_list
 
399
 
 
400
    def _usage(self):
 
401
        """Return single-line grammar for this command.
 
402
 
 
403
        Only describes arguments, not options.
 
404
        """
 
405
        s = 'bzr ' + self.name() + ' '
 
406
        for aname in self.takes_args:
 
407
            aname = aname.upper()
 
408
            if aname[-1] in ['$', '+']:
 
409
                aname = aname[:-1] + '...'
 
410
            elif aname[-1] == '?':
 
411
                aname = '[' + aname[:-1] + ']'
 
412
            elif aname[-1] == '*':
 
413
                aname = '[' + aname[:-1] + '...]'
 
414
            s += aname + ' '
 
415
        s = s[:-1]      # remove last space
 
416
        return s
 
417
 
 
418
    def get_help_text(self, additional_see_also=None, plain=True,
 
419
                      see_also_as_links=False, verbose=True):
 
420
        """Return a text string with help for this command.
 
421
 
 
422
        :param additional_see_also: Additional help topics to be
 
423
            cross-referenced.
 
424
        :param plain: if False, raw help (reStructuredText) is
 
425
            returned instead of plain text.
 
426
        :param see_also_as_links: if True, convert items in 'See also'
 
427
            list to internal links (used by bzr_man rstx generator)
 
428
        :param verbose: if True, display the full help, otherwise
 
429
            leave out the descriptive sections and just display
 
430
            usage help (e.g. Purpose, Usage, Options) with a
 
431
            message explaining how to obtain full help.
 
432
        """
 
433
        doc = self.help()
 
434
        if doc is None:
 
435
            raise NotImplementedError("sorry, no detailed help yet for %r" % self.name())
 
436
 
 
437
        # Extract the summary (purpose) and sections out from the text
 
438
        purpose,sections,order = self._get_help_parts(doc)
 
439
 
 
440
        # If a custom usage section was provided, use it
 
441
        if sections.has_key('Usage'):
 
442
            usage = sections.pop('Usage')
 
443
        else:
 
444
            usage = self._usage()
 
445
 
 
446
        # The header is the purpose and usage
 
447
        result = ""
 
448
        result += ':Purpose: %s\n' % purpose
 
449
        if usage.find('\n') >= 0:
 
450
            result += ':Usage:\n%s\n' % usage
 
451
        else:
 
452
            result += ':Usage:   %s\n' % usage
 
453
        result += '\n'
 
454
 
 
455
        # Add the options
 
456
        #
 
457
        # XXX: optparse implicitly rewraps the help, and not always perfectly,
 
458
        # so we get <https://bugs.launchpad.net/bzr/+bug/249908>.  -- mbp
 
459
        # 20090319
 
460
        options = option.get_optparser(self.options()).format_option_help()
 
461
        # XXX: According to the spec, ReST option lists actually don't support 
 
462
        # options like --1.9 so that causes syntax errors (in Sphinx at least).
 
463
        # As that pattern always appears in the commands that break, we trap
 
464
        # on that and then format that block of 'format' options as a literal
 
465
        # block.
 
466
        if not plain and options.find('  --1.9  ') != -1:
 
467
            options = options.replace(' format:\n', ' format::\n\n', 1)
 
468
        if options.startswith('Options:'):
 
469
            result += ':' + options
 
470
        elif options.startswith('options:'):
 
471
            # Python 2.4 version of optparse
 
472
            result += ':Options:' + options[len('options:'):]
 
473
        else:
 
474
            result += options
 
475
        result += '\n'
 
476
 
 
477
        if verbose:
 
478
            # Add the description, indenting it 2 spaces
 
479
            # to match the indentation of the options
 
480
            if sections.has_key(None):
 
481
                text = sections.pop(None)
 
482
                text = '\n  '.join(text.splitlines())
 
483
                result += ':%s:\n  %s\n\n' % ('Description',text)
 
484
 
 
485
            # Add the custom sections (e.g. Examples). Note that there's no need
 
486
            # to indent these as they must be indented already in the source.
 
487
            if sections:
 
488
                for label in order:
 
489
                    if sections.has_key(label):
 
490
                        result += ':%s:\n%s\n' % (label,sections[label])
 
491
                result += '\n'
 
492
        else:
 
493
            result += ("See bzr help %s for more details and examples.\n\n"
 
494
                % self.name())
 
495
 
 
496
        # Add the aliases, source (plug-in) and see also links, if any
 
497
        if self.aliases:
 
498
            result += ':Aliases:  '
 
499
            result += ', '.join(self.aliases) + '\n'
 
500
        plugin_name = self.plugin_name()
 
501
        if plugin_name is not None:
 
502
            result += ':From:     plugin "%s"\n' % plugin_name
 
503
        see_also = self.get_see_also(additional_see_also)
 
504
        if see_also:
 
505
            if not plain and see_also_as_links:
 
506
                see_also_links = []
 
507
                for item in see_also:
 
508
                    if item == 'topics':
 
509
                        # topics doesn't have an independent section
 
510
                        # so don't create a real link
 
511
                        see_also_links.append(item)
 
512
                    else:
 
513
                        # Use a reST link for this entry
 
514
                        see_also_links.append("`%s`_" % (item,))
 
515
                see_also = see_also_links
 
516
            result += ':See also: '
 
517
            result += ', '.join(see_also) + '\n'
 
518
 
 
519
        # If this will be rendered as plain text, convert it
 
520
        if plain:
 
521
            import bzrlib.help_topics
 
522
            result = bzrlib.help_topics.help_as_plain_text(result)
 
523
        return result
 
524
 
 
525
    @staticmethod
 
526
    def _get_help_parts(text):
 
527
        """Split help text into a summary and named sections.
 
528
 
 
529
        :return: (summary,sections,order) where summary is the top line and
 
530
            sections is a dictionary of the rest indexed by section name.
 
531
            order is the order the section appear in the text.
 
532
            A section starts with a heading line of the form ":xxx:".
 
533
            Indented text on following lines is the section value.
 
534
            All text found outside a named section is assigned to the
 
535
            default section which is given the key of None.
 
536
        """
 
537
        def save_section(sections, order, label, section):
 
538
            if len(section) > 0:
 
539
                if sections.has_key(label):
 
540
                    sections[label] += '\n' + section
 
541
                else:
 
542
                    order.append(label)
 
543
                    sections[label] = section
 
544
 
 
545
        lines = text.rstrip().splitlines()
 
546
        summary = lines.pop(0)
 
547
        sections = {}
 
548
        order = []
 
549
        label,section = None,''
 
550
        for line in lines:
 
551
            if line.startswith(':') and line.endswith(':') and len(line) > 2:
 
552
                save_section(sections, order, label, section)
 
553
                label,section = line[1:-1],''
 
554
            elif (label is not None) and len(line) > 1 and not line[0].isspace():
 
555
                save_section(sections, order, label, section)
 
556
                label,section = None,line
 
557
            else:
 
558
                if len(section) > 0:
 
559
                    section += '\n' + line
 
560
                else:
 
561
                    section = line
 
562
        save_section(sections, order, label, section)
 
563
        return summary, sections, order
 
564
 
 
565
    def get_help_topic(self):
 
566
        """Return the commands help topic - its name."""
 
567
        return self.name()
 
568
 
 
569
    def get_see_also(self, additional_terms=None):
 
570
        """Return a list of help topics that are related to this command.
 
571
 
 
572
        The list is derived from the content of the _see_also attribute. Any
 
573
        duplicates are removed and the result is in lexical order.
 
574
        :param additional_terms: Additional help topics to cross-reference.
 
575
        :return: A list of help topics.
 
576
        """
 
577
        see_also = set(getattr(self, '_see_also', []))
 
578
        if additional_terms:
 
579
            see_also.update(additional_terms)
 
580
        return sorted(see_also)
 
581
 
 
582
    def options(self):
 
583
        """Return dict of valid options for this command.
 
584
 
 
585
        Maps from long option name to option object."""
 
586
        r = Option.STD_OPTIONS.copy()
 
587
        std_names = r.keys()
 
588
        for o in self.takes_options:
 
589
            if isinstance(o, basestring):
 
590
                o = option.Option.OPTIONS[o]
 
591
            r[o.name] = o
 
592
            if o.name in std_names:
 
593
                self.supported_std_options.append(o.name)
 
594
        return r
 
595
 
 
596
    def _setup_outf(self):
 
597
        """Return a file linked to stdout, which has proper encoding."""
 
598
        # Originally I was using self.stdout, but that looks
 
599
        # *way* too much like sys.stdout
 
600
        if self.encoding_type == 'exact':
 
601
            # force sys.stdout to be binary stream on win32
 
602
            if sys.platform == 'win32':
 
603
                fileno = getattr(sys.stdout, 'fileno', None)
 
604
                if fileno:
 
605
                    import msvcrt
 
606
                    msvcrt.setmode(fileno(), os.O_BINARY)
 
607
            self.outf = sys.stdout
 
608
            return
 
609
 
 
610
        output_encoding = osutils.get_terminal_encoding()
 
611
 
 
612
        self.outf = codecs.getwriter(output_encoding)(sys.stdout,
 
613
                        errors=self.encoding_type)
 
614
        # For whatever reason codecs.getwriter() does not advertise its encoding
 
615
        # it just returns the encoding of the wrapped file, which is completely
 
616
        # bogus. So set the attribute, so we can find the correct encoding later.
 
617
        self.outf.encoding = output_encoding
 
618
 
 
619
    def run_argv_aliases(self, argv, alias_argv=None):
 
620
        """Parse the command line and run with extra aliases in alias_argv."""
 
621
        if argv is None:
 
622
            warn("Passing None for [] is deprecated from bzrlib 0.10",
 
623
                 DeprecationWarning, stacklevel=2)
 
624
            argv = []
 
625
        args, opts = parse_args(self, argv, alias_argv)
 
626
 
 
627
        # Process the standard options
266
628
        if 'help' in opts:  # e.g. bzr add --help
267
 
            from bzrlib.help import help_on_command
268
 
            help_on_command(self.name())
269
 
            return 0
270
 
 
271
 
        # check options are reasonable
272
 
        allowed = self.takes_options
273
 
        for oname in opts:
274
 
            if oname not in allowed:
275
 
                raise BzrCommandError("option '--%s' is not allowed for command %r"
276
 
                                      % (oname, self.name()))
 
629
            sys.stdout.write(self.get_help_text())
 
630
            return 0
 
631
        if 'usage' in opts:  # e.g. bzr add --usage
 
632
            sys.stdout.write(self.get_help_text(verbose=False))
 
633
            return 0
 
634
        trace.set_verbosity_level(option._verbosity_level)
 
635
        if 'verbose' in self.supported_std_options:
 
636
            opts['verbose'] = trace.is_verbose()
 
637
        elif opts.has_key('verbose'):
 
638
            del opts['verbose']
 
639
        if 'quiet' in self.supported_std_options:
 
640
            opts['quiet'] = trace.is_quiet()
 
641
        elif opts.has_key('quiet'):
 
642
            del opts['quiet']
277
643
 
278
644
        # mix arguments and options into one dictionary
279
645
        cmdargs = _match_argform(self.name(), self.takes_args, args)
284
650
        all_cmd_args = cmdargs.copy()
285
651
        all_cmd_args.update(cmdopts)
286
652
 
 
653
        self._setup_outf()
 
654
 
287
655
        return self.run(**all_cmd_args)
288
656
 
289
 
    
290
657
    def run(self):
291
658
        """Actually run the command.
292
659
 
297
664
        shell error code if not.  It's OK for this method to allow
298
665
        an exception to raise up.
299
666
        """
300
 
        raise NotImplementedError()
301
 
 
 
667
        raise NotImplementedError('no implementation of command %r'
 
668
                                  % self.name())
302
669
 
303
670
    def help(self):
304
671
        """Return help message for this class."""
 
672
        from inspect import getdoc
305
673
        if self.__doc__ is Command.__doc__:
306
674
            return None
307
675
        return getdoc(self)
309
677
    def name(self):
310
678
        return _unsquish_command_name(self.__class__.__name__)
311
679
 
 
680
    def plugin_name(self):
 
681
        """Get the name of the plugin that provides this command.
312
682
 
313
 
def parse_spec(spec):
314
 
    """
315
 
    >>> parse_spec(None)
316
 
    [None, None]
317
 
    >>> parse_spec("./")
318
 
    ['./', None]
319
 
    >>> parse_spec("../@")
320
 
    ['..', -1]
321
 
    >>> parse_spec("../f/@35")
322
 
    ['../f', 35]
323
 
    >>> parse_spec('./@revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67')
324
 
    ['.', 'revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67']
325
 
    """
326
 
    if spec is None:
327
 
        return [None, None]
328
 
    if '/@' in spec:
329
 
        parsed = spec.split('/@')
330
 
        assert len(parsed) == 2
331
 
        if parsed[1] == "":
332
 
            parsed[1] = -1
 
683
        :return: The name of the plugin or None if the command is builtin.
 
684
        """
 
685
        mod_parts = self.__module__.split('.')
 
686
        if len(mod_parts) >= 3 and mod_parts[1] == 'plugins':
 
687
            return mod_parts[2]
333
688
        else:
334
 
            try:
335
 
                parsed[1] = int(parsed[1])
336
 
            except ValueError:
337
 
                pass # We can allow stuff like ./@revid:blahblahblah
338
 
            else:
339
 
                assert parsed[1] >=0
340
 
    else:
341
 
        parsed = [spec, None]
342
 
    return parsed
343
 
 
344
 
 
345
 
 
346
 
 
347
 
# list of all available options; the rhs can be either None for an
348
 
# option that takes no argument, or a constructor function that checks
349
 
# the type.
350
 
OPTIONS = {
351
 
    'all':                    None,
352
 
    'diff-options':           str,
353
 
    'help':                   None,
354
 
    'file':                   unicode,
355
 
    'force':                  None,
356
 
    'format':                 unicode,
357
 
    'forward':                None,
358
 
    'message':                unicode,
359
 
    'no-recurse':             None,
360
 
    'profile':                None,
361
 
    'revision':               _parse_revision_str,
362
 
    'short':                  None,
363
 
    'show-ids':               None,
364
 
    'timezone':               str,
365
 
    'verbose':                None,
366
 
    'version':                None,
367
 
    'email':                  None,
368
 
    'unchanged':              None,
369
 
    'update':                 None,
370
 
    'long':                   None,
371
 
    'root':                   str,
372
 
    'no-backup':              None,
373
 
    'merge-type':             get_merge_type,
374
 
    'pattern':                str,
375
 
    }
376
 
 
377
 
SHORT_OPTIONS = {
378
 
    'F':                      'file', 
379
 
    'h':                      'help',
380
 
    'm':                      'message',
381
 
    'r':                      'revision',
382
 
    'v':                      'verbose',
383
 
    'l':                      'long',
384
 
}
385
 
 
386
 
 
387
 
def parse_args(argv):
 
689
            return None
 
690
 
 
691
 
 
692
class CommandHooks(Hooks):
 
693
    """Hooks related to Command object creation/enumeration."""
 
694
 
 
695
    def __init__(self):
 
696
        """Create the default hooks.
 
697
 
 
698
        These are all empty initially, because by default nothing should get
 
699
        notified.
 
700
        """
 
701
        Hooks.__init__(self)
 
702
        self.create_hook(HookPoint('extend_command',
 
703
            "Called after creating a command object to allow modifications "
 
704
            "such as adding or removing options, docs etc. Called with the "
 
705
            "new bzrlib.commands.Command object.", (1, 13), None))
 
706
        self.create_hook(HookPoint('get_command',
 
707
            "Called when creating a single command. Called with "
 
708
            "(cmd_or_None, command_name). get_command should either return "
 
709
            "the cmd_or_None parameter, or a replacement Command object that "
 
710
            "should be used for the command. Note that the Command.hooks "
 
711
            "hooks are core infrastructure. Many users will prefer to use "
 
712
            "bzrlib.commands.register_command or plugin_cmds.register_lazy.",
 
713
            (1, 17), None))
 
714
        self.create_hook(HookPoint('get_missing_command',
 
715
            "Called when creating a single command if no command could be "
 
716
            "found. Called with (command_name). get_missing_command should "
 
717
            "either return None, or a Command object to be used for the "
 
718
            "command.", (1, 17), None))
 
719
        self.create_hook(HookPoint('list_commands',
 
720
            "Called when enumerating commands. Called with a set of "
 
721
            "cmd_name strings for all the commands found so far. This set "
 
722
            " is safe to mutate - e.g. to remove a command. "
 
723
            "list_commands should return the updated set of command names.",
 
724
            (1, 17), None))
 
725
 
 
726
Command.hooks = CommandHooks()
 
727
 
 
728
 
 
729
def parse_args(command, argv, alias_argv=None):
388
730
    """Parse command line.
389
 
    
 
731
 
390
732
    Arguments and options are parsed at this level before being passed
391
733
    down to specific command handlers.  This routine knows, from a
392
734
    lookup table, something about the available options, what optargs
393
735
    they take, and which commands will accept them.
394
 
 
395
 
    >>> parse_args('--help'.split())
396
 
    ([], {'help': True})
397
 
    >>> parse_args('help -- --invalidcmd'.split())
398
 
    (['help', '--invalidcmd'], {})
399
 
    >>> parse_args('--version'.split())
400
 
    ([], {'version': True})
401
 
    >>> parse_args('status --all'.split())
402
 
    (['status'], {'all': True})
403
 
    >>> parse_args('commit --message=biter'.split())
404
 
    (['commit'], {'message': u'biter'})
405
 
    >>> parse_args('log -r 500'.split())
406
 
    (['log'], {'revision': [500]})
407
 
    >>> parse_args('log -r500..600'.split())
408
 
    (['log'], {'revision': [500, 600]})
409
 
    >>> parse_args('log -vr500..600'.split())
410
 
    (['log'], {'verbose': True, 'revision': [500, 600]})
411
 
    >>> parse_args('log -rv500..600'.split()) #the r takes an argument
412
 
    (['log'], {'revision': ['v500', 600]})
413
736
    """
414
 
    args = []
415
 
    opts = {}
416
 
 
417
 
    argsover = False
418
 
    while argv:
419
 
        a = argv.pop(0)
420
 
        if not argsover and a[0] == '-':
421
 
            # option names must not be unicode
422
 
            a = str(a)
423
 
            optarg = None
424
 
            if a[1] == '-':
425
 
                if a == '--':
426
 
                    # We've received a standalone -- No more flags
427
 
                    argsover = True
428
 
                    continue
429
 
                mutter("  got option %r" % a)
430
 
                if '=' in a:
431
 
                    optname, optarg = a[2:].split('=', 1)
432
 
                else:
433
 
                    optname = a[2:]
434
 
                if optname not in OPTIONS:
435
 
                    raise BzrError('unknown long option %r' % a)
436
 
            else:
437
 
                shortopt = a[1:]
438
 
                if shortopt in SHORT_OPTIONS:
439
 
                    # Multi-character options must have a space to delimit
440
 
                    # their value
441
 
                    optname = SHORT_OPTIONS[shortopt]
442
 
                else:
443
 
                    # Single character short options, can be chained,
444
 
                    # and have their value appended to their name
445
 
                    shortopt = a[1:2]
446
 
                    if shortopt not in SHORT_OPTIONS:
447
 
                        # We didn't find the multi-character name, and we
448
 
                        # didn't find the single char name
449
 
                        raise BzrError('unknown short option %r' % a)
450
 
                    optname = SHORT_OPTIONS[shortopt]
451
 
 
452
 
                    if a[2:]:
453
 
                        # There are extra things on this option
454
 
                        # see if it is the value, or if it is another
455
 
                        # short option
456
 
                        optargfn = OPTIONS[optname]
457
 
                        if optargfn is None:
458
 
                            # This option does not take an argument, so the
459
 
                            # next entry is another short option, pack it back
460
 
                            # into the list
461
 
                            argv.insert(0, '-' + a[2:])
462
 
                        else:
463
 
                            # This option takes an argument, so pack it
464
 
                            # into the array
465
 
                            optarg = a[2:]
466
 
            
467
 
            if optname in opts:
468
 
                # XXX: Do we ever want to support this, e.g. for -r?
469
 
                raise BzrError('repeated option %r' % a)
470
 
                
471
 
            optargfn = OPTIONS[optname]
472
 
            if optargfn:
473
 
                if optarg == None:
474
 
                    if not argv:
475
 
                        raise BzrError('option %r needs an argument' % a)
476
 
                    else:
477
 
                        optarg = argv.pop(0)
478
 
                opts[optname] = optargfn(optarg)
479
 
            else:
480
 
                if optarg != None:
481
 
                    raise BzrError('option %r takes no argument' % optname)
482
 
                opts[optname] = True
483
 
        else:
484
 
            args.append(a)
485
 
 
 
737
    # TODO: make it a method of the Command?
 
738
    parser = option.get_optparser(command.options())
 
739
    if alias_argv is not None:
 
740
        args = alias_argv + argv
 
741
    else:
 
742
        args = argv
 
743
 
 
744
    options, args = parser.parse_args(args)
 
745
    opts = dict([(k, v) for k, v in options.__dict__.iteritems() if
 
746
                 v is not option.OptionParser.DEFAULT_VALUE])
486
747
    return args, opts
487
748
 
488
749
 
489
 
 
490
 
 
491
750
def _match_argform(cmd, takes_args, args):
492
751
    argdict = {}
493
752
 
505
764
                argdict[argname + '_list'] = None
506
765
        elif ap[-1] == '+':
507
766
            if not args:
508
 
                raise BzrCommandError("command %r needs one or more %s"
509
 
                        % (cmd, argname.upper()))
 
767
                raise errors.BzrCommandError("command %r needs one or more %s"
 
768
                                             % (cmd, argname.upper()))
510
769
            else:
511
770
                argdict[argname + '_list'] = args[:]
512
771
                args = []
513
772
        elif ap[-1] == '$': # all but one
514
773
            if len(args) < 2:
515
 
                raise BzrCommandError("command %r needs one or more %s"
516
 
                        % (cmd, argname.upper()))
 
774
                raise errors.BzrCommandError("command %r needs one or more %s"
 
775
                                             % (cmd, argname.upper()))
517
776
            argdict[argname + '_list'] = args[:-1]
518
 
            args[:-1] = []                
 
777
            args[:-1] = []
519
778
        else:
520
779
            # just a plain arg
521
780
            argname = ap
522
781
            if not args:
523
 
                raise BzrCommandError("command %r requires argument %s"
524
 
                        % (cmd, argname.upper()))
 
782
                raise errors.BzrCommandError("command %r requires argument %s"
 
783
                               % (cmd, argname.upper()))
525
784
            else:
526
785
                argdict[argname] = args.pop(0)
527
 
            
 
786
 
528
787
    if args:
529
 
        raise BzrCommandError("extra argument to command %s: %s"
530
 
                              % (cmd, args[0]))
 
788
        raise errors.BzrCommandError("extra argument to command %s: %s"
 
789
                                     % (cmd, args[0]))
531
790
 
532
791
    return argdict
533
792
 
 
793
def apply_coveraged(dirname, the_callable, *args, **kwargs):
 
794
    # Cannot use "import trace", as that would import bzrlib.trace instead of
 
795
    # the standard library's trace.
 
796
    trace = __import__('trace')
 
797
 
 
798
    tracer = trace.Trace(count=1, trace=0)
 
799
    sys.settrace(tracer.globaltrace)
 
800
    threading.settrace(tracer.globaltrace)
 
801
 
 
802
    try:
 
803
        return exception_to_return_code(the_callable, *args, **kwargs)
 
804
    finally:
 
805
        sys.settrace(None)
 
806
        results = tracer.results()
 
807
        results.write_results(show_missing=1, summary=False,
 
808
                              coverdir=dirname)
534
809
 
535
810
 
536
811
def apply_profiled(the_callable, *args, **kwargs):
537
812
    import hotshot
538
813
    import tempfile
 
814
    import hotshot.stats
539
815
    pffileno, pfname = tempfile.mkstemp()
540
816
    try:
541
817
        prof = hotshot.Profile(pfname)
542
818
        try:
543
 
            ret = prof.runcall(the_callable, *args, **kwargs) or 0
 
819
            ret = prof.runcall(exception_to_return_code, the_callable, *args,
 
820
                **kwargs) or 0
544
821
        finally:
545
822
            prof.close()
546
 
 
547
 
        import hotshot.stats
548
823
        stats = hotshot.stats.load(pfname)
549
 
        #stats.strip_dirs()
550
 
        stats.sort_stats('time')
 
824
        stats.strip_dirs()
 
825
        stats.sort_stats('cum')   # 'time'
551
826
        ## XXX: Might like to write to stderr or the trace file instead but
552
827
        ## print_stats seems hardcoded to stdout
553
828
        stats.print_stats(20)
554
 
 
555
829
        return ret
556
830
    finally:
557
831
        os.close(pffileno)
558
832
        os.remove(pfname)
559
833
 
560
834
 
 
835
def exception_to_return_code(the_callable, *args, **kwargs):
 
836
    """UI level helper for profiling and coverage.
 
837
 
 
838
    This transforms exceptions into a return value of 3. As such its only
 
839
    relevant to the UI layer, and should never be called where catching
 
840
    exceptions may be desirable.
 
841
    """
 
842
    try:
 
843
        return the_callable(*args, **kwargs)
 
844
    except (KeyboardInterrupt, Exception), e:
 
845
        # used to handle AssertionError and KeyboardInterrupt
 
846
        # specially here, but hopefully they're handled ok by the logger now
 
847
        exc_info = sys.exc_info()
 
848
        exitcode = trace.report_exception(exc_info, sys.stderr)
 
849
        if os.environ.get('BZR_PDB'):
 
850
            print '**** entering debugger'
 
851
            tb = exc_info[2]
 
852
            import pdb
 
853
            if sys.version_info[:2] < (2, 6):
 
854
                # XXX: we want to do
 
855
                #    pdb.post_mortem(tb)
 
856
                # but because pdb.post_mortem gives bad results for tracebacks
 
857
                # from inside generators, we do it manually.
 
858
                # (http://bugs.python.org/issue4150, fixed in Python 2.6)
 
859
 
 
860
                # Setup pdb on the traceback
 
861
                p = pdb.Pdb()
 
862
                p.reset()
 
863
                p.setup(tb.tb_frame, tb)
 
864
                # Point the debugger at the deepest frame of the stack
 
865
                p.curindex = len(p.stack) - 1
 
866
                p.curframe = p.stack[p.curindex][0]
 
867
                # Start the pdb prompt.
 
868
                p.print_stack_entry(p.stack[p.curindex])
 
869
                p.execRcLines()
 
870
                p.cmdloop()
 
871
            else:
 
872
                pdb.post_mortem(tb)
 
873
        return exitcode
 
874
 
 
875
 
 
876
def apply_lsprofiled(filename, the_callable, *args, **kwargs):
 
877
    from bzrlib.lsprof import profile
 
878
    ret, stats = profile(exception_to_return_code, the_callable, *args, **kwargs)
 
879
    stats.sort()
 
880
    if filename is None:
 
881
        stats.pprint()
 
882
    else:
 
883
        stats.save(filename)
 
884
        trace.note('Profile data written to "%s".', filename)
 
885
    return ret
 
886
 
 
887
 
 
888
def shlex_split_unicode(unsplit):
 
889
    import shlex
 
890
    return [u.decode('utf-8') for u in shlex.split(unsplit.encode('utf-8'))]
 
891
 
 
892
 
 
893
def get_alias(cmd, config=None):
 
894
    """Return an expanded alias, or None if no alias exists.
 
895
 
 
896
    cmd
 
897
        Command to be checked for an alias.
 
898
    config
 
899
        Used to specify an alternative config to use,
 
900
        which is especially useful for testing.
 
901
        If it is unspecified, the global config will be used.
 
902
    """
 
903
    if config is None:
 
904
        import bzrlib.config
 
905
        config = bzrlib.config.GlobalConfig()
 
906
    alias = config.get_alias(cmd)
 
907
    if (alias):
 
908
        return shlex_split_unicode(alias)
 
909
    return None
 
910
 
 
911
 
561
912
def run_bzr(argv):
562
913
    """Execute a command.
563
914
 
564
 
    This is similar to main(), but without all the trappings for
565
 
    logging and error handling.  
566
 
    
567
915
    argv
568
916
       The command-line arguments, without the program name from argv[0]
569
 
    
 
917
       These should already be decoded. All library/test code calling
 
918
       run_bzr should be passing valid strings (don't need decoding).
 
919
 
570
920
    Returns a command status or raises an exception.
571
921
 
572
922
    Special master options: these must come before the command because
575
925
    --no-plugins
576
926
        Do not load plugin modules at all
577
927
 
 
928
    --no-aliases
 
929
        Do not allow aliases
 
930
 
578
931
    --builtin
579
932
        Only use builtin commands.  (Plugins are still allowed to change
580
933
        other behaviour.)
581
934
 
582
935
    --profile
583
 
        Run under the Python profiler.
 
936
        Run under the Python hotshot profiler.
 
937
 
 
938
    --lsprof
 
939
        Run under the Python lsprof profiler.
 
940
 
 
941
    --coverage
 
942
        Generate line coverage report in the specified directory.
 
943
 
 
944
    --concurrency
 
945
        Specify the number of processes that can be run concurrently (selftest).
584
946
    """
585
 
    
586
 
    argv = [a.decode(bzrlib.user_encoding) for a in argv]
 
947
    argv = list(argv)
 
948
    trace.mutter("bzr arguments: %r", argv)
587
949
 
588
 
    opt_profile = opt_no_plugins = opt_builtin = False
 
950
    opt_lsprof = opt_profile = opt_no_plugins = opt_builtin =  \
 
951
                opt_no_aliases = False
 
952
    opt_lsprof_file = opt_coverage_dir = None
589
953
 
590
954
    # --no-plugins is handled specially at a very early stage. We need
591
955
    # to load plugins before doing other command parsing so that they
592
956
    # can override commands, but this needs to happen first.
593
957
 
594
 
    for a in argv:
 
958
    argv_copy = []
 
959
    i = 0
 
960
    while i < len(argv):
 
961
        a = argv[i]
595
962
        if a == '--profile':
596
963
            opt_profile = True
 
964
        elif a == '--lsprof':
 
965
            opt_lsprof = True
 
966
        elif a == '--lsprof-file':
 
967
            opt_lsprof = True
 
968
            opt_lsprof_file = argv[i + 1]
 
969
            i += 1
597
970
        elif a == '--no-plugins':
598
971
            opt_no_plugins = True
 
972
        elif a == '--no-aliases':
 
973
            opt_no_aliases = True
599
974
        elif a == '--builtin':
600
975
            opt_builtin = True
601
 
        else:
602
 
            break
603
 
        argv.remove(a)
604
 
 
605
 
    if (not argv) or (argv[0] == '--help'):
606
 
        from bzrlib.help import help
607
 
        if len(argv) > 1:
608
 
            help(argv[1])
609
 
        else:
610
 
            help()
 
976
        elif a == '--concurrency':
 
977
            os.environ['BZR_CONCURRENCY'] = argv[i + 1]
 
978
            i += 1
 
979
        elif a == '--coverage':
 
980
            opt_coverage_dir = argv[i + 1]
 
981
            i += 1
 
982
        elif a.startswith('-D'):
 
983
            debug.debug_flags.add(a[2:])
 
984
        else:
 
985
            argv_copy.append(a)
 
986
        i += 1
 
987
 
 
988
    debug.set_debug_flags_from_config()
 
989
 
 
990
    argv = argv_copy
 
991
    if (not argv):
 
992
        from bzrlib.builtins import cmd_help
 
993
        cmd_help().run_argv_aliases([])
611
994
        return 0
612
995
 
613
996
    if argv[0] == '--version':
614
 
        from bzrlib.builtins import show_version
615
 
        show_version()
 
997
        from bzrlib.builtins import cmd_version
 
998
        cmd_version().run_argv_aliases([])
616
999
        return 0
617
 
        
 
1000
 
618
1001
    if not opt_no_plugins:
619
1002
        from bzrlib.plugin import load_plugins
620
1003
        load_plugins()
621
 
 
622
 
    cmd = str(argv.pop(0))
 
1004
    else:
 
1005
        from bzrlib.plugin import disable_plugins
 
1006
        disable_plugins()
 
1007
 
 
1008
    alias_argv = None
 
1009
 
 
1010
    if not opt_no_aliases:
 
1011
        alias_argv = get_alias(argv[0])
 
1012
        if alias_argv:
 
1013
            user_encoding = osutils.get_user_encoding()
 
1014
            alias_argv = [a.decode(user_encoding) for a in alias_argv]
 
1015
            argv[0] = alias_argv.pop(0)
 
1016
 
 
1017
    cmd = argv.pop(0)
 
1018
    # We want only 'ascii' command names, but the user may have typed
 
1019
    # in a Unicode name. In that case, they should just get a
 
1020
    # 'command not found' error later.
623
1021
 
624
1022
    cmd_obj = get_cmd_object(cmd, plugins_override=not opt_builtin)
625
 
 
626
 
    if opt_profile:
627
 
        ret = apply_profiled(cmd_obj.run_argv, argv)
628
 
    else:
629
 
        ret = cmd_obj.run_argv(argv)
630
 
    return ret or 0
631
 
 
632
 
 
633
 
def main(argv):
 
1023
    run = cmd_obj.run_argv_aliases
 
1024
    run_argv = [argv, alias_argv]
 
1025
 
 
1026
    try:
 
1027
        # We can be called recursively (tests for example), but we don't want
 
1028
        # the verbosity level to propagate.
 
1029
        saved_verbosity_level = option._verbosity_level
 
1030
        option._verbosity_level = 0
 
1031
        if opt_lsprof:
 
1032
            if opt_coverage_dir:
 
1033
                trace.warning(
 
1034
                    '--coverage ignored, because --lsprof is in use.')
 
1035
            ret = apply_lsprofiled(opt_lsprof_file, run, *run_argv)
 
1036
        elif opt_profile:
 
1037
            if opt_coverage_dir:
 
1038
                trace.warning(
 
1039
                    '--coverage ignored, because --profile is in use.')
 
1040
            ret = apply_profiled(run, *run_argv)
 
1041
        elif opt_coverage_dir:
 
1042
            ret = apply_coveraged(opt_coverage_dir, run, *run_argv)
 
1043
        else:
 
1044
            ret = run(*run_argv)
 
1045
        return ret or 0
 
1046
    finally:
 
1047
        # reset, in case we may do other commands later within the same
 
1048
        # process. Commands that want to execute sub-commands must propagate
 
1049
        # --verbose in their own way.
 
1050
        if 'memory' in debug.debug_flags:
 
1051
            trace.debug_memory('Process status after command:', short=False)
 
1052
        option._verbosity_level = saved_verbosity_level
 
1053
 
 
1054
 
 
1055
def display_command(func):
 
1056
    """Decorator that suppresses pipe/interrupt errors."""
 
1057
    def ignore_pipe(*args, **kwargs):
 
1058
        try:
 
1059
            result = func(*args, **kwargs)
 
1060
            sys.stdout.flush()
 
1061
            return result
 
1062
        except IOError, e:
 
1063
            if getattr(e, 'errno', None) is None:
 
1064
                raise
 
1065
            if e.errno != errno.EPIPE:
 
1066
                # Win32 raises IOError with errno=0 on a broken pipe
 
1067
                if sys.platform != 'win32' or (e.errno not in (0, errno.EINVAL)):
 
1068
                    raise
 
1069
            pass
 
1070
        except KeyboardInterrupt:
 
1071
            pass
 
1072
    return ignore_pipe
 
1073
 
 
1074
 
 
1075
def install_bzr_command_hooks():
 
1076
    """Install the hooks to supply bzr's own commands."""
 
1077
    if _list_bzr_commands in Command.hooks["list_commands"]:
 
1078
        return
 
1079
    Command.hooks.install_named_hook("list_commands", _list_bzr_commands,
 
1080
        "bzr commands")
 
1081
    Command.hooks.install_named_hook("get_command", _get_bzr_command,
 
1082
        "bzr commands")
 
1083
    Command.hooks.install_named_hook("get_command", _get_plugin_command,
 
1084
        "bzr plugin commands")
 
1085
    Command.hooks.install_named_hook("get_command", _get_external_command,
 
1086
        "bzr external command lookup")
 
1087
    Command.hooks.install_named_hook("get_missing_command", _try_plugin_provider,
 
1088
        "bzr plugin-provider-db check")
 
1089
 
 
1090
 
 
1091
def main(argv=None):
 
1092
    """Main entry point of command-line interface.
 
1093
 
 
1094
    :param argv: list of unicode command-line arguments similar to sys.argv.
 
1095
        argv[0] is script name usually, it will be ignored.
 
1096
        Don't pass here sys.argv because this list contains plain strings
 
1097
        and not unicode; pass None instead.
 
1098
 
 
1099
    :return: exit code of bzr command.
 
1100
    """
634
1101
    import bzrlib.ui
635
 
    bzrlib.trace.log_startup(argv)
636
 
    bzrlib.ui.ui_factory = bzrlib.ui.TextUIFactory()
637
 
 
 
1102
    bzrlib.ui.ui_factory = bzrlib.ui.make_ui_for_terminal(
 
1103
        sys.stdin, sys.stdout, sys.stderr)
 
1104
 
 
1105
    # Is this a final release version? If so, we should suppress warnings
 
1106
    if bzrlib.version_info[3] == 'final':
 
1107
        suppress_deprecation_warnings(override=True)
 
1108
    if argv is None:
 
1109
        argv = osutils.get_unicode_argv()
 
1110
    else:
 
1111
        new_argv = []
 
1112
        try:
 
1113
            # ensure all arguments are unicode strings
 
1114
            for a in argv[1:]:
 
1115
                if isinstance(a, unicode):
 
1116
                    new_argv.append(a)
 
1117
                else:
 
1118
                    new_argv.append(a.decode('ascii'))
 
1119
        except UnicodeDecodeError:
 
1120
            raise errors.BzrError("argv should be list of unicode strings.")
 
1121
        argv = new_argv
 
1122
    ret = run_bzr_catch_errors(argv)
 
1123
    trace.mutter("return code %d", ret)
 
1124
    osutils.report_extension_load_failures()
 
1125
    return ret
 
1126
 
 
1127
 
 
1128
def run_bzr_catch_errors(argv):
 
1129
    """Run a bzr command with parameters as described by argv.
 
1130
 
 
1131
    This function assumed that that UI layer is setup, that symbol deprecations
 
1132
    are already applied, and that unicode decoding has already been performed on argv.
 
1133
    """
 
1134
    install_bzr_command_hooks()
 
1135
    return exception_to_return_code(run_bzr, argv)
 
1136
 
 
1137
 
 
1138
def run_bzr_catch_user_errors(argv):
 
1139
    """Run bzr and report user errors, but let internal errors propagate.
 
1140
 
 
1141
    This is used for the test suite, and might be useful for other programs
 
1142
    that want to wrap the commandline interface.
 
1143
    """
 
1144
    install_bzr_command_hooks()
638
1145
    try:
639
 
        try:
640
 
            return run_bzr(argv[1:])
641
 
        finally:
642
 
            # do this here inside the exception wrappers to catch EPIPE
643
 
            sys.stdout.flush()
644
 
    except BzrCommandError, e:
645
 
        # command line syntax error, etc
646
 
        log_error(str(e))
647
 
        return 1
648
 
    except BzrError, e:
649
 
        bzrlib.trace.log_exception()
650
 
        return 1
651
 
    except AssertionError, e:
652
 
        bzrlib.trace.log_exception('assertion failed: ' + str(e))
653
 
        return 3
654
 
    except KeyboardInterrupt, e:
655
 
        bzrlib.trace.note('interrupted')
656
 
        return 2
 
1146
        return run_bzr(argv)
657
1147
    except Exception, e:
658
 
        import errno
659
 
        if (isinstance(e, IOError) 
660
 
            and hasattr(e, 'errno')
661
 
            and e.errno == errno.EPIPE):
662
 
            bzrlib.trace.note('broken pipe')
663
 
            return 2
664
 
        else:
665
 
            bzrlib.trace.log_exception()
666
 
            return 2
 
1148
        if (isinstance(e, (OSError, IOError))
 
1149
            or not getattr(e, 'internal_error', True)):
 
1150
            trace.report_exception(sys.exc_info(), sys.stderr)
 
1151
            return 3
 
1152
        else:
 
1153
            raise
 
1154
 
 
1155
 
 
1156
class HelpCommandIndex(object):
 
1157
    """A index for bzr help that returns commands."""
 
1158
 
 
1159
    def __init__(self):
 
1160
        self.prefix = 'commands/'
 
1161
 
 
1162
    def get_topics(self, topic):
 
1163
        """Search for topic amongst commands.
 
1164
 
 
1165
        :param topic: A topic to search for.
 
1166
        :return: A list which is either empty or contains a single
 
1167
            Command entry.
 
1168
        """
 
1169
        if topic and topic.startswith(self.prefix):
 
1170
            topic = topic[len(self.prefix):]
 
1171
        try:
 
1172
            cmd = _get_cmd_object(topic)
 
1173
        except KeyError:
 
1174
            return []
 
1175
        else:
 
1176
            return [cmd]
 
1177
 
 
1178
 
 
1179
class Provider(object):
 
1180
    '''Generic class to be overriden by plugins'''
 
1181
 
 
1182
    def plugin_for_command(self, cmd_name):
 
1183
        '''Takes a command and returns the information for that plugin
 
1184
 
 
1185
        :return: A dictionary with all the available information
 
1186
        for the requested plugin
 
1187
        '''
 
1188
        raise NotImplementedError
 
1189
 
 
1190
 
 
1191
class ProvidersRegistry(registry.Registry):
 
1192
    '''This registry exists to allow other providers to exist'''
 
1193
 
 
1194
    def __iter__(self):
 
1195
        for key, provider in self.iteritems():
 
1196
            yield provider
 
1197
 
 
1198
command_providers_registry = ProvidersRegistry()
667
1199
 
668
1200
 
669
1201
if __name__ == '__main__':