~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Arnaud Jeansen
  • Date: 2010-03-19 23:58:06 UTC
  • mto: This revision was merged to the branch mainline in revision 5126.
  • Revision ID: arnaud.jeansen@gmail.com-20100319235806-n0owdq874qsrb12u
Go back to unified report_delta method (i.e. former TreeDelta.show())

Show diffs side-by-side

added added

removed removed

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