/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: Vincent Ladeuil
  • Date: 2009-04-27 16:10:10 UTC
  • mto: (4310.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 4311.
  • Revision ID: v.ladeuil+lp@free.fr-20090427161010-7swfzeagf63cpixd
Fix bug #367726 by reverting some default user handling introduced
while fixing bug #256612.

* bzrlib/transport/ssh.py:
(_paramiko_auth): Explicitly use getpass.getuser() as default
user.

* bzrlib/transport/ftp/_gssapi.py:
(GSSAPIFtpTransport._create_connection): Explicitly use
getpass.getuser() as default user.

* bzrlib/transport/ftp/__init__.py:
(FtpTransport._create_connection): Explicitly use
getpass.getuser() as default user.

* bzrlib/tests/test_sftp_transport.py:
(TestUsesAuthConfig.test_sftp_is_none_if_no_config)
(TestUsesAuthConfig.test_sftp_doesnt_prompt_username): Revert to
None as the default user.

* bzrlib/tests/test_remote.py:
(TestRemoteSSHTransportAuthentication): The really offending one:
revert to None as the default user.

* bzrlib/tests/test_config.py:
(TestAuthenticationConfig.test_username_default_no_prompt): Update
test (and some PEP8).

* bzrlib/smtp_connection.py:
(SMTPConnection._authenticate): Revert to None as the default
user.

* bzrlib/plugins/launchpad/account.py:
(_get_auth_user): Revert default value handling.

* bzrlib/config.py:
(AuthenticationConfig.get_user): Fix doc-string. Leave default
value handling to callers.

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
from warnings import warn
 
39
 
 
40
import bzrlib
 
41
from bzrlib import (
 
42
    debug,
 
43
    errors,
 
44
    option,
 
45
    osutils,
 
46
    trace,
 
47
    win32utils,
 
48
    )
 
49
""")
 
50
 
 
51
from bzrlib import registry
 
52
# Compatibility
 
53
from bzrlib.hooks import HookPoint, Hooks
 
54
from bzrlib.option import Option
 
55
 
 
56
 
 
57
class CommandInfo(object):
 
58
    """Information about a command."""
 
59
 
 
60
    def __init__(self, aliases):
 
61
        """The list of aliases for the command."""
 
62
        self.aliases = aliases
 
63
 
 
64
    @classmethod
 
65
    def from_command(klass, command):
 
66
        """Factory to construct a CommandInfo from a command."""
 
67
        return klass(command.aliases)
 
68
 
 
69
 
 
70
class CommandRegistry(registry.Registry):
 
71
 
 
72
    @staticmethod
 
73
    def _get_name(command_name):
 
74
        if command_name.startswith("cmd_"):
 
75
            return _unsquish_command_name(command_name)
 
76
        else:
 
77
            return command_name
 
78
 
 
79
    def register(self, cmd, decorate=False):
 
80
        """Utility function to help register a command
 
81
 
 
82
        :param cmd: Command subclass to register
 
83
        :param decorate: If true, allow overriding an existing command
 
84
            of the same name; the old command is returned by this function.
 
85
            Otherwise it is an error to try to override an existing command.
 
86
        """
 
87
        k = cmd.__name__
 
88
        k_unsquished = self._get_name(k)
 
89
        try:
 
90
            previous = self.get(k_unsquished)
 
91
        except KeyError:
 
92
            previous = _builtin_commands().get(k_unsquished)
 
93
        info = CommandInfo.from_command(cmd)
 
94
        try:
 
95
            registry.Registry.register(self, k_unsquished, cmd,
 
96
                                       override_existing=decorate, info=info)
 
97
        except KeyError:
 
98
            trace.log_error('Two plugins defined the same command: %r' % k)
 
99
            trace.log_error('Not loading the one in %r' %
 
100
                            sys.modules[cmd.__module__])
 
101
            trace.log_error('Previously this command was registered from %r' %
 
102
                            sys.modules[previous.__module__])
 
103
        return previous
 
104
 
 
105
    def register_lazy(self, command_name, aliases, module_name):
 
106
        """Register a command without loading its module.
 
107
 
 
108
        :param command_name: The primary name of the command.
 
109
        :param aliases: A list of aliases for the command.
 
110
        :module_name: The module that the command lives in.
 
111
        """
 
112
        key = self._get_name(command_name)
 
113
        registry.Registry.register_lazy(self, key, module_name, command_name,
 
114
                                        info=CommandInfo(aliases))
 
115
 
 
116
 
 
117
plugin_cmds = CommandRegistry()
 
118
 
 
119
 
 
120
def register_command(cmd, decorate=False):
 
121
    global plugin_cmds
 
122
    return plugin_cmds.register(cmd, decorate)
 
123
 
 
124
 
 
125
def _squish_command_name(cmd):
 
126
    return 'cmd_' + cmd.replace('-', '_')
 
127
 
 
128
 
 
129
def _unsquish_command_name(cmd):
 
130
    return cmd[4:].replace('_','-')
 
131
 
 
132
 
 
133
def _builtin_commands():
 
134
    import bzrlib.builtins
 
135
    r = {}
 
136
    builtins = bzrlib.builtins.__dict__
 
137
    for name in builtins:
 
138
        if name.startswith("cmd_"):
 
139
            real_name = _unsquish_command_name(name)
 
140
            r[real_name] = builtins[name]
 
141
    return r
 
142
 
 
143
 
 
144
def builtin_command_names():
 
145
    """Return list of builtin command names."""
 
146
    return _builtin_commands().keys()
 
147
 
 
148
 
 
149
def plugin_command_names():
 
150
    return plugin_cmds.keys()
 
151
 
 
152
 
 
153
def _get_cmd_dict(plugins_override=True):
 
154
    """Return name->class mapping for all commands."""
 
155
    d = _builtin_commands()
 
156
    if plugins_override:
 
157
        d.update(plugin_cmds.iteritems())
 
158
    return d
 
159
 
 
160
 
 
161
def get_all_cmds(plugins_override=True):
 
162
    """Return canonical name and class for all registered commands."""
 
163
    for k, v in _get_cmd_dict(plugins_override=plugins_override).iteritems():
 
164
        yield k,v
 
165
 
 
166
 
 
167
def get_cmd_object(cmd_name, plugins_override=True):
 
168
    """Return the canonical name and command class for a command.
 
169
 
 
170
    plugins_override
 
171
        If true, plugin commands can override builtins.
 
172
    """
 
173
    try:
 
174
        cmd = _get_cmd_object(cmd_name, plugins_override)
 
175
        # Allow plugins to extend commands
 
176
        for hook in Command.hooks['extend_command']:
 
177
            hook(cmd)
 
178
        return cmd
 
179
    except KeyError:
 
180
        raise errors.BzrCommandError('unknown command "%s"' % cmd_name)
 
181
 
 
182
 
 
183
def _get_cmd_object(cmd_name, plugins_override=True):
 
184
    """Worker for get_cmd_object which raises KeyError rather than BzrCommandError."""
 
185
    from bzrlib.externalcommand import ExternalCommand
 
186
 
 
187
    # We want only 'ascii' command names, but the user may have typed
 
188
    # in a Unicode name. In that case, they should just get a
 
189
    # 'command not found' error later.
 
190
    # In the future, we may actually support Unicode command names.
 
191
 
 
192
    # first look up this command under the specified name
 
193
    if plugins_override:
 
194
        try:
 
195
            return plugin_cmds.get(cmd_name)()
 
196
        except KeyError:
 
197
            pass
 
198
    cmds = _get_cmd_dict(plugins_override=False)
 
199
    try:
 
200
        return cmds[cmd_name]()
 
201
    except KeyError:
 
202
        pass
 
203
    if plugins_override:
 
204
        for key in plugin_cmds.keys():
 
205
            info = plugin_cmds.get_info(key)
 
206
            if cmd_name in info.aliases:
 
207
                return plugin_cmds.get(key)()
 
208
    # look for any command which claims this as an alias
 
209
    for real_cmd_name, cmd_class in cmds.iteritems():
 
210
        if cmd_name in cmd_class.aliases:
 
211
            return cmd_class()
 
212
 
 
213
    cmd_obj = ExternalCommand.find_command(cmd_name)
 
214
    if cmd_obj:
 
215
        return cmd_obj
 
216
 
 
217
    # look for plugins that provide this command but aren't installed
 
218
    for provider in command_providers_registry:
 
219
        try:
 
220
            plugin_metadata = provider.plugin_for_command(cmd_name)
 
221
        except errors.NoPluginAvailable:
 
222
            pass
 
223
        else:
 
224
            raise errors.CommandAvailableInPlugin(cmd_name,
 
225
                                                  plugin_metadata, provider)
 
226
    raise KeyError
 
227
 
 
228
 
 
229
class Command(object):
 
230
    """Base class for commands.
 
231
 
 
232
    Commands are the heart of the command-line bzr interface.
 
233
 
 
234
    The command object mostly handles the mapping of command-line
 
235
    parameters into one or more bzrlib operations, and of the results
 
236
    into textual output.
 
237
 
 
238
    Commands normally don't have any state.  All their arguments are
 
239
    passed in to the run method.  (Subclasses may take a different
 
240
    policy if the behaviour of the instance needs to depend on e.g. a
 
241
    shell plugin and not just its Python class.)
 
242
 
 
243
    The docstring for an actual command should give a single-line
 
244
    summary, then a complete description of the command.  A grammar
 
245
    description will be inserted.
 
246
 
 
247
    aliases
 
248
        Other accepted names for this command.
 
249
 
 
250
    takes_args
 
251
        List of argument forms, marked with whether they are optional,
 
252
        repeated, etc.
 
253
 
 
254
                Examples:
 
255
 
 
256
                ['to_location', 'from_branch?', 'file*']
 
257
 
 
258
                'to_location' is required
 
259
                'from_branch' is optional
 
260
                'file' can be specified 0 or more times
 
261
 
 
262
    takes_options
 
263
        List of options that may be given for this command.  These can
 
264
        be either strings, referring to globally-defined options,
 
265
        or option objects.  Retrieve through options().
 
266
 
 
267
    hidden
 
268
        If true, this command isn't advertised.  This is typically
 
269
        for commands intended for expert users.
 
270
 
 
271
    encoding_type
 
272
        Command objects will get a 'outf' attribute, which has been
 
273
        setup to properly handle encoding of unicode strings.
 
274
        encoding_type determines what will happen when characters cannot
 
275
        be encoded
 
276
            strict - abort if we cannot decode
 
277
            replace - put in a bogus character (typically '?')
 
278
            exact - do not encode sys.stdout
 
279
 
 
280
            NOTE: by default on Windows, sys.stdout is opened as a text
 
281
            stream, therefore LF line-endings are converted to CRLF.
 
282
            When a command uses encoding_type = 'exact', then
 
283
            sys.stdout is forced to be a binary stream, and line-endings
 
284
            will not mangled.
 
285
 
 
286
    :cvar hooks: An instance of CommandHooks.
 
287
    """
 
288
    aliases = []
 
289
    takes_args = []
 
290
    takes_options = []
 
291
    encoding_type = 'strict'
 
292
 
 
293
    hidden = False
 
294
 
 
295
    def __init__(self):
 
296
        """Construct an instance of this command."""
 
297
        if self.__doc__ == Command.__doc__:
 
298
            warn("No help message set for %r" % self)
 
299
        # List of standard options directly supported
 
300
        self.supported_std_options = []
 
301
 
 
302
    def _maybe_expand_globs(self, file_list):
 
303
        """Glob expand file_list if the platform does not do that itself.
 
304
 
 
305
        :return: A possibly empty list of unicode paths.
 
306
 
 
307
        Introduced in bzrlib 0.18.
 
308
        """
 
309
        if not file_list:
 
310
            file_list = []
 
311
        if sys.platform == 'win32':
 
312
            file_list = win32utils.glob_expand(file_list)
 
313
        return list(file_list)
 
314
 
 
315
    def _usage(self):
 
316
        """Return single-line grammar for this command.
 
317
 
 
318
        Only describes arguments, not options.
 
319
        """
 
320
        s = 'bzr ' + self.name() + ' '
 
321
        for aname in self.takes_args:
 
322
            aname = aname.upper()
 
323
            if aname[-1] in ['$', '+']:
 
324
                aname = aname[:-1] + '...'
 
325
            elif aname[-1] == '?':
 
326
                aname = '[' + aname[:-1] + ']'
 
327
            elif aname[-1] == '*':
 
328
                aname = '[' + aname[:-1] + '...]'
 
329
            s += aname + ' '
 
330
        s = s[:-1]      # remove last space
 
331
        return s
 
332
 
 
333
    def get_help_text(self, additional_see_also=None, plain=True,
 
334
                      see_also_as_links=False, verbose=True):
 
335
        """Return a text string with help for this command.
 
336
 
 
337
        :param additional_see_also: Additional help topics to be
 
338
            cross-referenced.
 
339
        :param plain: if False, raw help (reStructuredText) is
 
340
            returned instead of plain text.
 
341
        :param see_also_as_links: if True, convert items in 'See also'
 
342
            list to internal links (used by bzr_man rstx generator)
 
343
        :param verbose: if True, display the full help, otherwise
 
344
            leave out the descriptive sections and just display
 
345
            usage help (e.g. Purpose, Usage, Options) with a
 
346
            message explaining how to obtain full help.
 
347
        """
 
348
        doc = self.help()
 
349
        if doc is None:
 
350
            raise NotImplementedError("sorry, no detailed help yet for %r" % self.name())
 
351
 
 
352
        # Extract the summary (purpose) and sections out from the text
 
353
        purpose,sections,order = self._get_help_parts(doc)
 
354
 
 
355
        # If a custom usage section was provided, use it
 
356
        if sections.has_key('Usage'):
 
357
            usage = sections.pop('Usage')
 
358
        else:
 
359
            usage = self._usage()
 
360
 
 
361
        # The header is the purpose and usage
 
362
        result = ""
 
363
        result += ':Purpose: %s\n' % purpose
 
364
        if usage.find('\n') >= 0:
 
365
            result += ':Usage:\n%s\n' % usage
 
366
        else:
 
367
            result += ':Usage:   %s\n' % usage
 
368
        result += '\n'
 
369
 
 
370
        # Add the options
 
371
        options = option.get_optparser(self.options()).format_option_help()
 
372
        if options.startswith('Options:'):
 
373
            result += ':' + options
 
374
        elif options.startswith('options:'):
 
375
            # Python 2.4 version of optparse
 
376
            result += ':Options:' + options[len('options:'):]
 
377
        else:
 
378
            result += options
 
379
        result += '\n'
 
380
 
 
381
        if verbose:
 
382
            # Add the description, indenting it 2 spaces
 
383
            # to match the indentation of the options
 
384
            if sections.has_key(None):
 
385
                text = sections.pop(None)
 
386
                text = '\n  '.join(text.splitlines())
 
387
                result += ':%s:\n  %s\n\n' % ('Description',text)
 
388
 
 
389
            # Add the custom sections (e.g. Examples). Note that there's no need
 
390
            # to indent these as they must be indented already in the source.
 
391
            if sections:
 
392
                for label in order:
 
393
                    if sections.has_key(label):
 
394
                        result += ':%s:\n%s\n' % (label,sections[label])
 
395
                result += '\n'
 
396
        else:
 
397
            result += ("See bzr help %s for more details and examples.\n\n"
 
398
                % self.name())
 
399
 
 
400
        # Add the aliases, source (plug-in) and see also links, if any
 
401
        if self.aliases:
 
402
            result += ':Aliases:  '
 
403
            result += ', '.join(self.aliases) + '\n'
 
404
        plugin_name = self.plugin_name()
 
405
        if plugin_name is not None:
 
406
            result += ':From:     plugin "%s"\n' % plugin_name
 
407
        see_also = self.get_see_also(additional_see_also)
 
408
        if see_also:
 
409
            if not plain and see_also_as_links:
 
410
                see_also_links = []
 
411
                for item in see_also:
 
412
                    if item == 'topics':
 
413
                        # topics doesn't have an independent section
 
414
                        # so don't create a real link
 
415
                        see_also_links.append(item)
 
416
                    else:
 
417
                        # Use a reST link for this entry
 
418
                        see_also_links.append("`%s`_" % (item,))
 
419
                see_also = see_also_links
 
420
            result += ':See also: '
 
421
            result += ', '.join(see_also) + '\n'
 
422
 
 
423
        # If this will be rendered as plain text, convert it
 
424
        if plain:
 
425
            import bzrlib.help_topics
 
426
            result = bzrlib.help_topics.help_as_plain_text(result)
 
427
        return result
 
428
 
 
429
    @staticmethod
 
430
    def _get_help_parts(text):
 
431
        """Split help text into a summary and named sections.
 
432
 
 
433
        :return: (summary,sections,order) where summary is the top line and
 
434
            sections is a dictionary of the rest indexed by section name.
 
435
            order is the order the section appear in the text.
 
436
            A section starts with a heading line of the form ":xxx:".
 
437
            Indented text on following lines is the section value.
 
438
            All text found outside a named section is assigned to the
 
439
            default section which is given the key of None.
 
440
        """
 
441
        def save_section(sections, order, label, section):
 
442
            if len(section) > 0:
 
443
                if sections.has_key(label):
 
444
                    sections[label] += '\n' + section
 
445
                else:
 
446
                    order.append(label)
 
447
                    sections[label] = section
 
448
 
 
449
        lines = text.rstrip().splitlines()
 
450
        summary = lines.pop(0)
 
451
        sections = {}
 
452
        order = []
 
453
        label,section = None,''
 
454
        for line in lines:
 
455
            if line.startswith(':') and line.endswith(':') and len(line) > 2:
 
456
                save_section(sections, order, label, section)
 
457
                label,section = line[1:-1],''
 
458
            elif (label is not None) and len(line) > 1 and not line[0].isspace():
 
459
                save_section(sections, order, label, section)
 
460
                label,section = None,line
 
461
            else:
 
462
                if len(section) > 0:
 
463
                    section += '\n' + line
 
464
                else:
 
465
                    section = line
 
466
        save_section(sections, order, label, section)
 
467
        return summary, sections, order
 
468
 
 
469
    def get_help_topic(self):
 
470
        """Return the commands help topic - its name."""
 
471
        return self.name()
 
472
 
 
473
    def get_see_also(self, additional_terms=None):
 
474
        """Return a list of help topics that are related to this command.
 
475
 
 
476
        The list is derived from the content of the _see_also attribute. Any
 
477
        duplicates are removed and the result is in lexical order.
 
478
        :param additional_terms: Additional help topics to cross-reference.
 
479
        :return: A list of help topics.
 
480
        """
 
481
        see_also = set(getattr(self, '_see_also', []))
 
482
        if additional_terms:
 
483
            see_also.update(additional_terms)
 
484
        return sorted(see_also)
 
485
 
 
486
    def options(self):
 
487
        """Return dict of valid options for this command.
 
488
 
 
489
        Maps from long option name to option object."""
 
490
        r = Option.STD_OPTIONS.copy()
 
491
        std_names = r.keys()
 
492
        for o in self.takes_options:
 
493
            if isinstance(o, basestring):
 
494
                o = option.Option.OPTIONS[o]
 
495
            r[o.name] = o
 
496
            if o.name in std_names:
 
497
                self.supported_std_options.append(o.name)
 
498
        return r
 
499
 
 
500
    def _setup_outf(self):
 
501
        """Return a file linked to stdout, which has proper encoding."""
 
502
        # Originally I was using self.stdout, but that looks
 
503
        # *way* too much like sys.stdout
 
504
        if self.encoding_type == 'exact':
 
505
            # force sys.stdout to be binary stream on win32
 
506
            if sys.platform == 'win32':
 
507
                fileno = getattr(sys.stdout, 'fileno', None)
 
508
                if fileno:
 
509
                    import msvcrt
 
510
                    msvcrt.setmode(fileno(), os.O_BINARY)
 
511
            self.outf = sys.stdout
 
512
            return
 
513
 
 
514
        output_encoding = osutils.get_terminal_encoding()
 
515
 
 
516
        self.outf = codecs.getwriter(output_encoding)(sys.stdout,
 
517
                        errors=self.encoding_type)
 
518
        # For whatever reason codecs.getwriter() does not advertise its encoding
 
519
        # it just returns the encoding of the wrapped file, which is completely
 
520
        # bogus. So set the attribute, so we can find the correct encoding later.
 
521
        self.outf.encoding = output_encoding
 
522
 
 
523
    def run_argv_aliases(self, argv, alias_argv=None):
 
524
        """Parse the command line and run with extra aliases in alias_argv."""
 
525
        if argv is None:
 
526
            warn("Passing None for [] is deprecated from bzrlib 0.10",
 
527
                 DeprecationWarning, stacklevel=2)
 
528
            argv = []
 
529
        args, opts = parse_args(self, argv, alias_argv)
 
530
 
 
531
        # Process the standard options
 
532
        if 'help' in opts:  # e.g. bzr add --help
 
533
            sys.stdout.write(self.get_help_text())
 
534
            return 0
 
535
        if 'usage' in opts:  # e.g. bzr add --usage
 
536
            sys.stdout.write(self.get_help_text(verbose=False))
 
537
            return 0
 
538
        trace.set_verbosity_level(option._verbosity_level)
 
539
        if 'verbose' in self.supported_std_options:
 
540
            opts['verbose'] = trace.is_verbose()
 
541
        elif opts.has_key('verbose'):
 
542
            del opts['verbose']
 
543
        if 'quiet' in self.supported_std_options:
 
544
            opts['quiet'] = trace.is_quiet()
 
545
        elif opts.has_key('quiet'):
 
546
            del opts['quiet']
 
547
 
 
548
        # mix arguments and options into one dictionary
 
549
        cmdargs = _match_argform(self.name(), self.takes_args, args)
 
550
        cmdopts = {}
 
551
        for k, v in opts.items():
 
552
            cmdopts[k.replace('-', '_')] = v
 
553
 
 
554
        all_cmd_args = cmdargs.copy()
 
555
        all_cmd_args.update(cmdopts)
 
556
 
 
557
        self._setup_outf()
 
558
 
 
559
        return self.run(**all_cmd_args)
 
560
 
 
561
    def run(self):
 
562
        """Actually run the command.
 
563
 
 
564
        This is invoked with the options and arguments bound to
 
565
        keyword parameters.
 
566
 
 
567
        Return 0 or None if the command was successful, or a non-zero
 
568
        shell error code if not.  It's OK for this method to allow
 
569
        an exception to raise up.
 
570
        """
 
571
        raise NotImplementedError('no implementation of command %r'
 
572
                                  % self.name())
 
573
 
 
574
    def help(self):
 
575
        """Return help message for this class."""
 
576
        from inspect import getdoc
 
577
        if self.__doc__ is Command.__doc__:
 
578
            return None
 
579
        return getdoc(self)
 
580
 
 
581
    def name(self):
 
582
        return _unsquish_command_name(self.__class__.__name__)
 
583
 
 
584
    def plugin_name(self):
 
585
        """Get the name of the plugin that provides this command.
 
586
 
 
587
        :return: The name of the plugin or None if the command is builtin.
 
588
        """
 
589
        mod_parts = self.__module__.split('.')
 
590
        if len(mod_parts) >= 3 and mod_parts[1] == 'plugins':
 
591
            return mod_parts[2]
 
592
        else:
 
593
            return None
 
594
 
 
595
 
 
596
class CommandHooks(Hooks):
 
597
    """Hooks related to Command object creation/enumeration."""
 
598
 
 
599
    def __init__(self):
 
600
        """Create the default hooks.
 
601
 
 
602
        These are all empty initially, because by default nothing should get
 
603
        notified.
 
604
        """
 
605
        Hooks.__init__(self)
 
606
        self.create_hook(HookPoint('extend_command',
 
607
            "Called after creating a command object to allow modifications "
 
608
            "such as adding or removing options, docs etc. Called with the "
 
609
            "new bzrlib.commands.Command object.", (1, 13), None))
 
610
 
 
611
Command.hooks = CommandHooks()
 
612
 
 
613
 
 
614
def parse_args(command, argv, alias_argv=None):
 
615
    """Parse command line.
 
616
 
 
617
    Arguments and options are parsed at this level before being passed
 
618
    down to specific command handlers.  This routine knows, from a
 
619
    lookup table, something about the available options, what optargs
 
620
    they take, and which commands will accept them.
 
621
    """
 
622
    # TODO: make it a method of the Command?
 
623
    parser = option.get_optparser(command.options())
 
624
    if alias_argv is not None:
 
625
        args = alias_argv + argv
 
626
    else:
 
627
        args = argv
 
628
 
 
629
    options, args = parser.parse_args(args)
 
630
    opts = dict([(k, v) for k, v in options.__dict__.iteritems() if
 
631
                 v is not option.OptionParser.DEFAULT_VALUE])
 
632
    return args, opts
 
633
 
 
634
 
 
635
def _match_argform(cmd, takes_args, args):
 
636
    argdict = {}
 
637
 
 
638
    # step through args and takes_args, allowing appropriate 0-many matches
 
639
    for ap in takes_args:
 
640
        argname = ap[:-1]
 
641
        if ap[-1] == '?':
 
642
            if args:
 
643
                argdict[argname] = args.pop(0)
 
644
        elif ap[-1] == '*': # all remaining arguments
 
645
            if args:
 
646
                argdict[argname + '_list'] = args[:]
 
647
                args = []
 
648
            else:
 
649
                argdict[argname + '_list'] = None
 
650
        elif ap[-1] == '+':
 
651
            if not args:
 
652
                raise errors.BzrCommandError("command %r needs one or more %s"
 
653
                                             % (cmd, argname.upper()))
 
654
            else:
 
655
                argdict[argname + '_list'] = args[:]
 
656
                args = []
 
657
        elif ap[-1] == '$': # all but one
 
658
            if len(args) < 2:
 
659
                raise errors.BzrCommandError("command %r needs one or more %s"
 
660
                                             % (cmd, argname.upper()))
 
661
            argdict[argname + '_list'] = args[:-1]
 
662
            args[:-1] = []
 
663
        else:
 
664
            # just a plain arg
 
665
            argname = ap
 
666
            if not args:
 
667
                raise errors.BzrCommandError("command %r requires argument %s"
 
668
                               % (cmd, argname.upper()))
 
669
            else:
 
670
                argdict[argname] = args.pop(0)
 
671
 
 
672
    if args:
 
673
        raise errors.BzrCommandError("extra argument to command %s: %s"
 
674
                                     % (cmd, args[0]))
 
675
 
 
676
    return argdict
 
677
 
 
678
def apply_coveraged(dirname, the_callable, *args, **kwargs):
 
679
    # Cannot use "import trace", as that would import bzrlib.trace instead of
 
680
    # the standard library's trace.
 
681
    trace = __import__('trace')
 
682
 
 
683
    tracer = trace.Trace(count=1, trace=0)
 
684
    sys.settrace(tracer.globaltrace)
 
685
 
 
686
    try:
 
687
        return exception_to_return_code(the_callable, *args, **kwargs)
 
688
    finally:
 
689
        sys.settrace(None)
 
690
        results = tracer.results()
 
691
        results.write_results(show_missing=1, summary=False,
 
692
                              coverdir=dirname)
 
693
 
 
694
 
 
695
def apply_profiled(the_callable, *args, **kwargs):
 
696
    import hotshot
 
697
    import tempfile
 
698
    import hotshot.stats
 
699
    pffileno, pfname = tempfile.mkstemp()
 
700
    try:
 
701
        prof = hotshot.Profile(pfname)
 
702
        try:
 
703
            ret = prof.runcall(exception_to_return_code, the_callable, *args,
 
704
                **kwargs) or 0
 
705
        finally:
 
706
            prof.close()
 
707
        stats = hotshot.stats.load(pfname)
 
708
        stats.strip_dirs()
 
709
        stats.sort_stats('cum')   # 'time'
 
710
        ## XXX: Might like to write to stderr or the trace file instead but
 
711
        ## print_stats seems hardcoded to stdout
 
712
        stats.print_stats(20)
 
713
        return ret
 
714
    finally:
 
715
        os.close(pffileno)
 
716
        os.remove(pfname)
 
717
 
 
718
 
 
719
def exception_to_return_code(the_callable, *args, **kwargs):
 
720
    """UI level helper for profiling and coverage.
 
721
 
 
722
    This transforms exceptions into a return value of 3. As such its only
 
723
    relevant to the UI layer, and should never be called where catching
 
724
    exceptions may be desirable.
 
725
    """
 
726
    try:
 
727
        return the_callable(*args, **kwargs)
 
728
    except (KeyboardInterrupt, Exception), e:
 
729
        # used to handle AssertionError and KeyboardInterrupt
 
730
        # specially here, but hopefully they're handled ok by the logger now
 
731
        exc_info = sys.exc_info()
 
732
        exitcode = trace.report_exception(exc_info, sys.stderr)
 
733
        if os.environ.get('BZR_PDB'):
 
734
            print '**** entering debugger'
 
735
            tb = exc_info[2]
 
736
            import pdb
 
737
            if sys.version_info[:2] < (2, 6):
 
738
                # XXX: we want to do
 
739
                #    pdb.post_mortem(tb)
 
740
                # but because pdb.post_mortem gives bad results for tracebacks
 
741
                # from inside generators, we do it manually.
 
742
                # (http://bugs.python.org/issue4150, fixed in Python 2.6)
 
743
 
 
744
                # Setup pdb on the traceback
 
745
                p = pdb.Pdb()
 
746
                p.reset()
 
747
                p.setup(tb.tb_frame, tb)
 
748
                # Point the debugger at the deepest frame of the stack
 
749
                p.curindex = len(p.stack) - 1
 
750
                p.curframe = p.stack[p.curindex][0]
 
751
                # Start the pdb prompt.
 
752
                p.print_stack_entry(p.stack[p.curindex])
 
753
                p.execRcLines()
 
754
                p.cmdloop()
 
755
            else:
 
756
                pdb.post_mortem(tb)
 
757
        return exitcode
 
758
 
 
759
 
 
760
def apply_lsprofiled(filename, the_callable, *args, **kwargs):
 
761
    from bzrlib.lsprof import profile
 
762
    ret, stats = profile(exception_to_return_code, the_callable, *args, **kwargs)
 
763
    stats.sort()
 
764
    if filename is None:
 
765
        stats.pprint()
 
766
    else:
 
767
        stats.save(filename)
 
768
        trace.note('Profile data written to "%s".', filename)
 
769
    return ret
 
770
 
 
771
 
 
772
def shlex_split_unicode(unsplit):
 
773
    import shlex
 
774
    return [u.decode('utf-8') for u in shlex.split(unsplit.encode('utf-8'))]
 
775
 
 
776
 
 
777
def get_alias(cmd, config=None):
 
778
    """Return an expanded alias, or None if no alias exists.
 
779
 
 
780
    cmd
 
781
        Command to be checked for an alias.
 
782
    config
 
783
        Used to specify an alternative config to use,
 
784
        which is especially useful for testing.
 
785
        If it is unspecified, the global config will be used.
 
786
    """
 
787
    if config is None:
 
788
        import bzrlib.config
 
789
        config = bzrlib.config.GlobalConfig()
 
790
    alias = config.get_alias(cmd)
 
791
    if (alias):
 
792
        return shlex_split_unicode(alias)
 
793
    return None
 
794
 
 
795
 
 
796
def run_bzr(argv):
 
797
    """Execute a command.
 
798
 
 
799
    argv
 
800
       The command-line arguments, without the program name from argv[0]
 
801
       These should already be decoded. All library/test code calling
 
802
       run_bzr should be passing valid strings (don't need decoding).
 
803
 
 
804
    Returns a command status or raises an exception.
 
805
 
 
806
    Special master options: these must come before the command because
 
807
    they control how the command is interpreted.
 
808
 
 
809
    --no-plugins
 
810
        Do not load plugin modules at all
 
811
 
 
812
    --no-aliases
 
813
        Do not allow aliases
 
814
 
 
815
    --builtin
 
816
        Only use builtin commands.  (Plugins are still allowed to change
 
817
        other behaviour.)
 
818
 
 
819
    --profile
 
820
        Run under the Python hotshot profiler.
 
821
 
 
822
    --lsprof
 
823
        Run under the Python lsprof profiler.
 
824
 
 
825
    --coverage
 
826
        Generate line coverage report in the specified directory.
 
827
    """
 
828
    argv = list(argv)
 
829
    trace.mutter("bzr arguments: %r", argv)
 
830
 
 
831
    opt_lsprof = opt_profile = opt_no_plugins = opt_builtin =  \
 
832
                opt_no_aliases = False
 
833
    opt_lsprof_file = opt_coverage_dir = None
 
834
 
 
835
    # --no-plugins is handled specially at a very early stage. We need
 
836
    # to load plugins before doing other command parsing so that they
 
837
    # can override commands, but this needs to happen first.
 
838
 
 
839
    argv_copy = []
 
840
    i = 0
 
841
    while i < len(argv):
 
842
        a = argv[i]
 
843
        if a == '--profile':
 
844
            opt_profile = True
 
845
        elif a == '--lsprof':
 
846
            opt_lsprof = True
 
847
        elif a == '--lsprof-file':
 
848
            opt_lsprof = True
 
849
            opt_lsprof_file = argv[i + 1]
 
850
            i += 1
 
851
        elif a == '--no-plugins':
 
852
            opt_no_plugins = True
 
853
        elif a == '--no-aliases':
 
854
            opt_no_aliases = True
 
855
        elif a == '--builtin':
 
856
            opt_builtin = True
 
857
        elif a == '--coverage':
 
858
            opt_coverage_dir = argv[i + 1]
 
859
            i += 1
 
860
        elif a.startswith('-D'):
 
861
            debug.debug_flags.add(a[2:])
 
862
        else:
 
863
            argv_copy.append(a)
 
864
        i += 1
 
865
 
 
866
    debug.set_debug_flags_from_config()
 
867
 
 
868
    argv = argv_copy
 
869
    if (not argv):
 
870
        from bzrlib.builtins import cmd_help
 
871
        cmd_help().run_argv_aliases([])
 
872
        return 0
 
873
 
 
874
    if argv[0] == '--version':
 
875
        from bzrlib.builtins import cmd_version
 
876
        cmd_version().run_argv_aliases([])
 
877
        return 0
 
878
 
 
879
    if not opt_no_plugins:
 
880
        from bzrlib.plugin import load_plugins
 
881
        load_plugins()
 
882
    else:
 
883
        from bzrlib.plugin import disable_plugins
 
884
        disable_plugins()
 
885
 
 
886
    alias_argv = None
 
887
 
 
888
    if not opt_no_aliases:
 
889
        alias_argv = get_alias(argv[0])
 
890
        if alias_argv:
 
891
            user_encoding = osutils.get_user_encoding()
 
892
            alias_argv = [a.decode(user_encoding) for a in alias_argv]
 
893
            argv[0] = alias_argv.pop(0)
 
894
 
 
895
    cmd = argv.pop(0)
 
896
    # We want only 'ascii' command names, but the user may have typed
 
897
    # in a Unicode name. In that case, they should just get a
 
898
    # 'command not found' error later.
 
899
 
 
900
    cmd_obj = get_cmd_object(cmd, plugins_override=not opt_builtin)
 
901
    run = cmd_obj.run_argv_aliases
 
902
    run_argv = [argv, alias_argv]
 
903
 
 
904
    try:
 
905
        # We can be called recursively (tests for example), but we don't want
 
906
        # the verbosity level to propagate.
 
907
        saved_verbosity_level = option._verbosity_level
 
908
        option._verbosity_level = 0
 
909
        if opt_lsprof:
 
910
            if opt_coverage_dir:
 
911
                trace.warning(
 
912
                    '--coverage ignored, because --lsprof is in use.')
 
913
            ret = apply_lsprofiled(opt_lsprof_file, run, *run_argv)
 
914
        elif opt_profile:
 
915
            if opt_coverage_dir:
 
916
                trace.warning(
 
917
                    '--coverage ignored, because --profile is in use.')
 
918
            ret = apply_profiled(run, *run_argv)
 
919
        elif opt_coverage_dir:
 
920
            ret = apply_coveraged(opt_coverage_dir, run, *run_argv)
 
921
        else:
 
922
            ret = run(*run_argv)
 
923
        if 'memory' in debug.debug_flags:
 
924
            trace.debug_memory('Process status after command:', short=False)
 
925
        return ret or 0
 
926
    finally:
 
927
        # reset, in case we may do other commands later within the same
 
928
        # process. Commands that want to execute sub-commands must propagate
 
929
        # --verbose in their own way.
 
930
        option._verbosity_level = saved_verbosity_level
 
931
 
 
932
 
 
933
def display_command(func):
 
934
    """Decorator that suppresses pipe/interrupt errors."""
 
935
    def ignore_pipe(*args, **kwargs):
 
936
        try:
 
937
            result = func(*args, **kwargs)
 
938
            sys.stdout.flush()
 
939
            return result
 
940
        except IOError, e:
 
941
            if getattr(e, 'errno', None) is None:
 
942
                raise
 
943
            if e.errno != errno.EPIPE:
 
944
                # Win32 raises IOError with errno=0 on a broken pipe
 
945
                if sys.platform != 'win32' or (e.errno not in (0, errno.EINVAL)):
 
946
                    raise
 
947
            pass
 
948
        except KeyboardInterrupt:
 
949
            pass
 
950
    return ignore_pipe
 
951
 
 
952
 
 
953
def main(argv):
 
954
    import bzrlib.ui
 
955
    bzrlib.ui.ui_factory = bzrlib.ui.make_ui_for_terminal(
 
956
        sys.stdin, sys.stdout, sys.stderr)
 
957
 
 
958
    # Is this a final release version? If so, we should suppress warnings
 
959
    if bzrlib.version_info[3] == 'final':
 
960
        from bzrlib import symbol_versioning
 
961
        symbol_versioning.suppress_deprecation_warnings(override=False)
 
962
    try:
 
963
        user_encoding = osutils.get_user_encoding()
 
964
        argv = [a.decode(user_encoding) for a in argv[1:]]
 
965
    except UnicodeDecodeError:
 
966
        raise errors.BzrError(("Parameter '%r' is unsupported by the current "
 
967
                                                            "encoding." % a))
 
968
    ret = run_bzr_catch_errors(argv)
 
969
    trace.mutter("return code %d", ret)
 
970
    return ret
 
971
 
 
972
 
 
973
def run_bzr_catch_errors(argv):
 
974
    """Run a bzr command with parameters as described by argv.
 
975
 
 
976
    This function assumed that that UI layer is setup, that symbol deprecations
 
977
    are already applied, and that unicode decoding has already been performed on argv.
 
978
    """
 
979
    return exception_to_return_code(run_bzr, argv)
 
980
 
 
981
 
 
982
def run_bzr_catch_user_errors(argv):
 
983
    """Run bzr and report user errors, but let internal errors propagate.
 
984
 
 
985
    This is used for the test suite, and might be useful for other programs
 
986
    that want to wrap the commandline interface.
 
987
    """
 
988
    try:
 
989
        return run_bzr(argv)
 
990
    except Exception, e:
 
991
        if (isinstance(e, (OSError, IOError))
 
992
            or not getattr(e, 'internal_error', True)):
 
993
            trace.report_exception(sys.exc_info(), sys.stderr)
 
994
            return 3
 
995
        else:
 
996
            raise
 
997
 
 
998
 
 
999
class HelpCommandIndex(object):
 
1000
    """A index for bzr help that returns commands."""
 
1001
 
 
1002
    def __init__(self):
 
1003
        self.prefix = 'commands/'
 
1004
 
 
1005
    def get_topics(self, topic):
 
1006
        """Search for topic amongst commands.
 
1007
 
 
1008
        :param topic: A topic to search for.
 
1009
        :return: A list which is either empty or contains a single
 
1010
            Command entry.
 
1011
        """
 
1012
        if topic and topic.startswith(self.prefix):
 
1013
            topic = topic[len(self.prefix):]
 
1014
        try:
 
1015
            cmd = _get_cmd_object(topic)
 
1016
        except KeyError:
 
1017
            return []
 
1018
        else:
 
1019
            return [cmd]
 
1020
 
 
1021
 
 
1022
class Provider(object):
 
1023
    '''Generic class to be overriden by plugins'''
 
1024
 
 
1025
    def plugin_for_command(self, cmd_name):
 
1026
        '''Takes a command and returns the information for that plugin
 
1027
 
 
1028
        :return: A dictionary with all the available information
 
1029
        for the requested plugin
 
1030
        '''
 
1031
        raise NotImplementedError
 
1032
 
 
1033
 
 
1034
class ProvidersRegistry(registry.Registry):
 
1035
    '''This registry exists to allow other providers to exist'''
 
1036
 
 
1037
    def __iter__(self):
 
1038
        for key, provider in self.iteritems():
 
1039
            yield provider
 
1040
 
 
1041
command_providers_registry = ProvidersRegistry()
 
1042
 
 
1043
 
 
1044
if __name__ == '__main__':
 
1045
    sys.exit(main(sys.argv))