/brz/remove-bazaar

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

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2009-11-18 06:18:14 UTC
  • mfrom: (4634.97.5 doc-2.0)
  • Revision ID: pqm@pqm.ubuntu.com-20091118061814-695imx80olc79o7l
(mbp, trivial) additional doc building fix

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2006, 2008 Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
 
 
18
# TODO: probably should say which arguments are candidates for glob
 
19
# expansion on windows and do that at the command level.
 
20
 
 
21
# TODO: Define arguments by objects, rather than just using names.
 
22
# Those objects can specify the expected type of the argument, which
 
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
 
32
import sys
 
33
 
 
34
from bzrlib.lazy_import import lazy_import
 
35
lazy_import(globals(), """
 
36
import codecs
 
37
import errno
 
38
import threading
 
39
from warnings import warn
 
40
 
 
41
import bzrlib
 
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):
 
128
    global plugin_cmds
 
129
    return plugin_cmds.register(cmd, decorate)
 
130
 
 
131
 
 
132
def _squish_command_name(cmd):
 
133
    return 'cmd_' + cmd.replace('-', '_')
 
134
 
 
135
 
 
136
def _unsquish_command_name(cmd):
 
137
    return cmd[4:].replace('_','-')
 
138
 
 
139
 
 
140
def _builtin_commands():
 
141
    import bzrlib.builtins
 
142
    return _scan_module_for_commands(bzrlib.builtins)
 
143
 
 
144
 
 
145
def _scan_module_for_commands(module):
 
146
    r = {}
 
147
    for name, obj in module.__dict__.iteritems():
 
148
        if name.startswith("cmd_"):
 
149
            real_name = _unsquish_command_name(name)
 
150
            r[real_name] = obj
 
151
    return r
 
152
 
 
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
 
 
172
 
 
173
def 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
    """
 
179
    return _builtin_commands().keys()
 
180
 
 
181
 
 
182
def plugin_command_names():
 
183
    """Returns command names from commands registered by plugins."""
 
184
    return plugin_cmds.keys()
 
185
 
 
186
 
 
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
    """
 
197
    d = _builtin_commands()
 
198
    if plugins_override:
 
199
        d.update(plugin_cmds.iteritems())
 
200
    for k, v in d.iteritems():
 
201
        yield k,v
 
202
 
 
203
 
 
204
def get_cmd_object(cmd_name, plugins_override=True):
 
205
    """Return the command object for a command.
 
206
 
 
207
    plugins_override
 
208
        If true, plugin commands can override builtins.
 
209
    """
 
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()
 
279
    try:
 
280
        return cmds[cmd_name]()
 
281
    except KeyError:
 
282
        pass
 
283
    # look for any command which claims this as an alias
 
284
    for real_cmd_name, cmd_class in cmds.iteritems():
 
285
        if cmd_name in cmd_class.aliases:
 
286
            return cmd_class()
 
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
 
296
    cmd_obj = ExternalCommand.find_command(cmd_name)
 
297
    if cmd_obj:
 
298
        return cmd_obj
 
299
 
 
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
 
312
 
 
313
 
 
314
class Command(object):
 
315
    """Base class for commands.
 
316
 
 
317
    Commands are the heart of the command-line bzr interface.
 
318
 
 
319
    The command object mostly handles the mapping of command-line
 
320
    parameters into one or more bzrlib operations, and of the results
 
321
    into textual output.
 
322
 
 
323
    Commands normally don't have any state.  All their arguments are
 
324
    passed in to the run method.  (Subclasses may take a different
 
325
    policy if the behaviour of the instance needs to depend on e.g. a
 
326
    shell plugin and not just its Python class.)
 
327
 
 
328
    The docstring for an actual command should give a single-line
 
329
    summary, then a complete description of the command.  A grammar
 
330
    description will be inserted.
 
331
 
 
332
    aliases
 
333
        Other accepted names for this command.
 
334
 
 
335
    takes_args
 
336
        List of argument forms, marked with whether they are optional,
 
337
        repeated, etc.
 
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
 
 
347
    takes_options
 
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().
 
351
 
 
352
    hidden
 
353
        If true, this command isn't advertised.  This is typically
 
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.
 
372
    """
 
373
    aliases = []
 
374
    takes_args = []
 
375
    takes_options = []
 
376
    encoding_type = 'strict'
 
377
 
 
378
    hidden = False
 
379
 
 
380
    def __init__(self):
 
381
        """Construct an instance of this command."""
 
382
        if self.__doc__ == Command.__doc__:
 
383
            warn("No help message set for %r" % self)
 
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
 
628
        if 'help' in opts:  # e.g. bzr add --help
 
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']
 
643
 
 
644
        # mix arguments and options into one dictionary
 
645
        cmdargs = _match_argform(self.name(), self.takes_args, args)
 
646
        cmdopts = {}
 
647
        for k, v in opts.items():
 
648
            cmdopts[k.replace('-', '_')] = v
 
649
 
 
650
        all_cmd_args = cmdargs.copy()
 
651
        all_cmd_args.update(cmdopts)
 
652
 
 
653
        self._setup_outf()
 
654
 
 
655
        return self.run(**all_cmd_args)
 
656
 
 
657
    def run(self):
 
658
        """Actually run the command.
 
659
 
 
660
        This is invoked with the options and arguments bound to
 
661
        keyword parameters.
 
662
 
 
663
        Return 0 or None if the command was successful, or a non-zero
 
664
        shell error code if not.  It's OK for this method to allow
 
665
        an exception to raise up.
 
666
        """
 
667
        raise NotImplementedError('no implementation of command %r'
 
668
                                  % self.name())
 
669
 
 
670
    def help(self):
 
671
        """Return help message for this class."""
 
672
        from inspect import getdoc
 
673
        if self.__doc__ is Command.__doc__:
 
674
            return None
 
675
        return getdoc(self)
 
676
 
 
677
    def name(self):
 
678
        return _unsquish_command_name(self.__class__.__name__)
 
679
 
 
680
    def plugin_name(self):
 
681
        """Get the name of the plugin that provides this command.
 
682
 
 
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]
 
688
        else:
 
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):
 
730
    """Parse command line.
 
731
 
 
732
    Arguments and options are parsed at this level before being passed
 
733
    down to specific command handlers.  This routine knows, from a
 
734
    lookup table, something about the available options, what optargs
 
735
    they take, and which commands will accept them.
 
736
    """
 
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])
 
747
    return args, opts
 
748
 
 
749
 
 
750
def _match_argform(cmd, takes_args, args):
 
751
    argdict = {}
 
752
 
 
753
    # step through args and takes_args, allowing appropriate 0-many matches
 
754
    for ap in takes_args:
 
755
        argname = ap[:-1]
 
756
        if ap[-1] == '?':
 
757
            if args:
 
758
                argdict[argname] = args.pop(0)
 
759
        elif ap[-1] == '*': # all remaining arguments
 
760
            if args:
 
761
                argdict[argname + '_list'] = args[:]
 
762
                args = []
 
763
            else:
 
764
                argdict[argname + '_list'] = None
 
765
        elif ap[-1] == '+':
 
766
            if not args:
 
767
                raise errors.BzrCommandError("command %r needs one or more %s"
 
768
                                             % (cmd, argname.upper()))
 
769
            else:
 
770
                argdict[argname + '_list'] = args[:]
 
771
                args = []
 
772
        elif ap[-1] == '$': # all but one
 
773
            if len(args) < 2:
 
774
                raise errors.BzrCommandError("command %r needs one or more %s"
 
775
                                             % (cmd, argname.upper()))
 
776
            argdict[argname + '_list'] = args[:-1]
 
777
            args[:-1] = []
 
778
        else:
 
779
            # just a plain arg
 
780
            argname = ap
 
781
            if not args:
 
782
                raise errors.BzrCommandError("command %r requires argument %s"
 
783
                               % (cmd, argname.upper()))
 
784
            else:
 
785
                argdict[argname] = args.pop(0)
 
786
 
 
787
    if args:
 
788
        raise errors.BzrCommandError("extra argument to command %s: %s"
 
789
                                     % (cmd, args[0]))
 
790
 
 
791
    return argdict
 
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)
 
809
 
 
810
 
 
811
def apply_profiled(the_callable, *args, **kwargs):
 
812
    import hotshot
 
813
    import tempfile
 
814
    import hotshot.stats
 
815
    pffileno, pfname = tempfile.mkstemp()
 
816
    try:
 
817
        prof = hotshot.Profile(pfname)
 
818
        try:
 
819
            ret = prof.runcall(exception_to_return_code, the_callable, *args,
 
820
                **kwargs) or 0
 
821
        finally:
 
822
            prof.close()
 
823
        stats = hotshot.stats.load(pfname)
 
824
        stats.strip_dirs()
 
825
        stats.sort_stats('cum')   # 'time'
 
826
        ## XXX: Might like to write to stderr or the trace file instead but
 
827
        ## print_stats seems hardcoded to stdout
 
828
        stats.print_stats(20)
 
829
        return ret
 
830
    finally:
 
831
        os.close(pffileno)
 
832
        os.remove(pfname)
 
833
 
 
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
 
 
912
def run_bzr(argv):
 
913
    """Execute a command.
 
914
 
 
915
    argv
 
916
       The command-line arguments, without the program name from argv[0]
 
917
       These should already be decoded. All library/test code calling
 
918
       run_bzr should be passing valid strings (don't need decoding).
 
919
 
 
920
    Returns a command status or raises an exception.
 
921
 
 
922
    Special master options: these must come before the command because
 
923
    they control how the command is interpreted.
 
924
 
 
925
    --no-plugins
 
926
        Do not load plugin modules at all
 
927
 
 
928
    --no-aliases
 
929
        Do not allow aliases
 
930
 
 
931
    --builtin
 
932
        Only use builtin commands.  (Plugins are still allowed to change
 
933
        other behaviour.)
 
934
 
 
935
    --profile
 
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
    argv = list(argv)
 
945
    trace.mutter("bzr arguments: %r", argv)
 
946
 
 
947
    opt_lsprof = opt_profile = opt_no_plugins = opt_builtin =  \
 
948
                opt_no_aliases = False
 
949
    opt_lsprof_file = opt_coverage_dir = None
 
950
 
 
951
    # --no-plugins is handled specially at a very early stage. We need
 
952
    # to load plugins before doing other command parsing so that they
 
953
    # can override commands, but this needs to happen first.
 
954
 
 
955
    argv_copy = []
 
956
    i = 0
 
957
    while i < len(argv):
 
958
        a = argv[i]
 
959
        if a == '--profile':
 
960
            opt_profile = True
 
961
        elif a == '--lsprof':
 
962
            opt_lsprof = True
 
963
        elif a == '--lsprof-file':
 
964
            opt_lsprof = True
 
965
            opt_lsprof_file = argv[i + 1]
 
966
            i += 1
 
967
        elif a == '--no-plugins':
 
968
            opt_no_plugins = True
 
969
        elif a == '--no-aliases':
 
970
            opt_no_aliases = True
 
971
        elif a == '--builtin':
 
972
            opt_builtin = True
 
973
        elif a == '--coverage':
 
974
            opt_coverage_dir = argv[i + 1]
 
975
            i += 1
 
976
        elif a.startswith('-D'):
 
977
            debug.debug_flags.add(a[2:])
 
978
        else:
 
979
            argv_copy.append(a)
 
980
        i += 1
 
981
 
 
982
    debug.set_debug_flags_from_config()
 
983
 
 
984
    argv = argv_copy
 
985
    if (not argv):
 
986
        from bzrlib.builtins import cmd_help
 
987
        cmd_help().run_argv_aliases([])
 
988
        return 0
 
989
 
 
990
    if argv[0] == '--version':
 
991
        from bzrlib.builtins import cmd_version
 
992
        cmd_version().run_argv_aliases([])
 
993
        return 0
 
994
 
 
995
    if not opt_no_plugins:
 
996
        from bzrlib.plugin import load_plugins
 
997
        load_plugins()
 
998
    else:
 
999
        from bzrlib.plugin import disable_plugins
 
1000
        disable_plugins()
 
1001
 
 
1002
    alias_argv = None
 
1003
 
 
1004
    if not opt_no_aliases:
 
1005
        alias_argv = get_alias(argv[0])
 
1006
        if alias_argv:
 
1007
            user_encoding = osutils.get_user_encoding()
 
1008
            alias_argv = [a.decode(user_encoding) for a in alias_argv]
 
1009
            argv[0] = alias_argv.pop(0)
 
1010
 
 
1011
    cmd = argv.pop(0)
 
1012
    # We want only 'ascii' command names, but the user may have typed
 
1013
    # in a Unicode name. In that case, they should just get a
 
1014
    # 'command not found' error later.
 
1015
 
 
1016
    cmd_obj = get_cmd_object(cmd, plugins_override=not opt_builtin)
 
1017
    run = cmd_obj.run_argv_aliases
 
1018
    run_argv = [argv, alias_argv]
 
1019
 
 
1020
    try:
 
1021
        # We can be called recursively (tests for example), but we don't want
 
1022
        # the verbosity level to propagate.
 
1023
        saved_verbosity_level = option._verbosity_level
 
1024
        option._verbosity_level = 0
 
1025
        if opt_lsprof:
 
1026
            if opt_coverage_dir:
 
1027
                trace.warning(
 
1028
                    '--coverage ignored, because --lsprof is in use.')
 
1029
            ret = apply_lsprofiled(opt_lsprof_file, run, *run_argv)
 
1030
        elif opt_profile:
 
1031
            if opt_coverage_dir:
 
1032
                trace.warning(
 
1033
                    '--coverage ignored, because --profile is in use.')
 
1034
            ret = apply_profiled(run, *run_argv)
 
1035
        elif opt_coverage_dir:
 
1036
            ret = apply_coveraged(opt_coverage_dir, run, *run_argv)
 
1037
        else:
 
1038
            ret = run(*run_argv)
 
1039
        return ret or 0
 
1040
    finally:
 
1041
        # reset, in case we may do other commands later within the same
 
1042
        # process. Commands that want to execute sub-commands must propagate
 
1043
        # --verbose in their own way.
 
1044
        if 'memory' in debug.debug_flags:
 
1045
            trace.debug_memory('Process status after command:', short=False)
 
1046
        option._verbosity_level = saved_verbosity_level
 
1047
 
 
1048
 
 
1049
def display_command(func):
 
1050
    """Decorator that suppresses pipe/interrupt errors."""
 
1051
    def ignore_pipe(*args, **kwargs):
 
1052
        try:
 
1053
            result = func(*args, **kwargs)
 
1054
            sys.stdout.flush()
 
1055
            return result
 
1056
        except IOError, e:
 
1057
            if getattr(e, 'errno', None) is None:
 
1058
                raise
 
1059
            if e.errno != errno.EPIPE:
 
1060
                # Win32 raises IOError with errno=0 on a broken pipe
 
1061
                if sys.platform != 'win32' or (e.errno not in (0, errno.EINVAL)):
 
1062
                    raise
 
1063
            pass
 
1064
        except KeyboardInterrupt:
 
1065
            pass
 
1066
    return ignore_pipe
 
1067
 
 
1068
 
 
1069
def install_bzr_command_hooks():
 
1070
    """Install the hooks to supply bzr's own commands."""
 
1071
    if _list_bzr_commands in Command.hooks["list_commands"]:
 
1072
        return
 
1073
    Command.hooks.install_named_hook("list_commands", _list_bzr_commands,
 
1074
        "bzr commands")
 
1075
    Command.hooks.install_named_hook("get_command", _get_bzr_command,
 
1076
        "bzr commands")
 
1077
    Command.hooks.install_named_hook("get_command", _get_plugin_command,
 
1078
        "bzr plugin commands")
 
1079
    Command.hooks.install_named_hook("get_command", _get_external_command,
 
1080
        "bzr external command lookup")
 
1081
    Command.hooks.install_named_hook("get_missing_command", _try_plugin_provider,
 
1082
        "bzr plugin-provider-db check")
 
1083
 
 
1084
 
 
1085
def main(argv=None):
 
1086
    """Main entry point of command-line interface.
 
1087
 
 
1088
    :param argv: list of unicode command-line arguments similar to sys.argv.
 
1089
        argv[0] is script name usually, it will be ignored.
 
1090
        Don't pass here sys.argv because this list contains plain strings
 
1091
        and not unicode; pass None instead.
 
1092
 
 
1093
    :return: exit code of bzr command.
 
1094
    """
 
1095
    import bzrlib.ui
 
1096
    bzrlib.ui.ui_factory = bzrlib.ui.make_ui_for_terminal(
 
1097
        sys.stdin, sys.stdout, sys.stderr)
 
1098
 
 
1099
    # Is this a final release version? If so, we should suppress warnings
 
1100
    if bzrlib.version_info[3] == 'final':
 
1101
        suppress_deprecation_warnings(override=True)
 
1102
    if argv is None:
 
1103
        argv = osutils.get_unicode_argv()
 
1104
    else:
 
1105
        new_argv = []
 
1106
        try:
 
1107
            # ensure all arguments are unicode strings
 
1108
            for a in argv[1:]:
 
1109
                if isinstance(a, unicode):
 
1110
                    new_argv.append(a)
 
1111
                else:
 
1112
                    new_argv.append(a.decode('ascii'))
 
1113
        except UnicodeDecodeError:
 
1114
            raise errors.BzrError("argv should be list of unicode strings.")
 
1115
        argv = new_argv
 
1116
    ret = run_bzr_catch_errors(argv)
 
1117
    trace.mutter("return code %d", ret)
 
1118
    osutils.report_extension_load_failures()
 
1119
    return ret
 
1120
 
 
1121
 
 
1122
def run_bzr_catch_errors(argv):
 
1123
    """Run a bzr command with parameters as described by argv.
 
1124
 
 
1125
    This function assumed that that UI layer is setup, that symbol deprecations
 
1126
    are already applied, and that unicode decoding has already been performed on argv.
 
1127
    """
 
1128
    install_bzr_command_hooks()
 
1129
    return exception_to_return_code(run_bzr, argv)
 
1130
 
 
1131
 
 
1132
def run_bzr_catch_user_errors(argv):
 
1133
    """Run bzr and report user errors, but let internal errors propagate.
 
1134
 
 
1135
    This is used for the test suite, and might be useful for other programs
 
1136
    that want to wrap the commandline interface.
 
1137
    """
 
1138
    install_bzr_command_hooks()
 
1139
    try:
 
1140
        return run_bzr(argv)
 
1141
    except Exception, e:
 
1142
        if (isinstance(e, (OSError, IOError))
 
1143
            or not getattr(e, 'internal_error', True)):
 
1144
            trace.report_exception(sys.exc_info(), sys.stderr)
 
1145
            return 3
 
1146
        else:
 
1147
            raise
 
1148
 
 
1149
 
 
1150
class HelpCommandIndex(object):
 
1151
    """A index for bzr help that returns commands."""
 
1152
 
 
1153
    def __init__(self):
 
1154
        self.prefix = 'commands/'
 
1155
 
 
1156
    def get_topics(self, topic):
 
1157
        """Search for topic amongst commands.
 
1158
 
 
1159
        :param topic: A topic to search for.
 
1160
        :return: A list which is either empty or contains a single
 
1161
            Command entry.
 
1162
        """
 
1163
        if topic and topic.startswith(self.prefix):
 
1164
            topic = topic[len(self.prefix):]
 
1165
        try:
 
1166
            cmd = _get_cmd_object(topic)
 
1167
        except KeyError:
 
1168
            return []
 
1169
        else:
 
1170
            return [cmd]
 
1171
 
 
1172
 
 
1173
class Provider(object):
 
1174
    '''Generic class to be overriden by plugins'''
 
1175
 
 
1176
    def plugin_for_command(self, cmd_name):
 
1177
        '''Takes a command and returns the information for that plugin
 
1178
 
 
1179
        :return: A dictionary with all the available information
 
1180
        for the requested plugin
 
1181
        '''
 
1182
        raise NotImplementedError
 
1183
 
 
1184
 
 
1185
class ProvidersRegistry(registry.Registry):
 
1186
    '''This registry exists to allow other providers to exist'''
 
1187
 
 
1188
    def __iter__(self):
 
1189
        for key, provider in self.iteritems():
 
1190
            yield provider
 
1191
 
 
1192
command_providers_registry = ProvidersRegistry()
 
1193
 
 
1194
 
 
1195
if __name__ == '__main__':
 
1196
    sys.exit(main(sys.argv))