~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

(Marius Kruger) Update the wsgi bindings to load plugins and enable
        logging (optional)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2004, 2005 by Canonical Ltd
2
 
 
 
1
# Copyright (C) 2006 Canonical Ltd
 
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
7
 
 
 
7
#
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
11
# GNU General Public License for more details.
12
 
 
 
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
18
# TODO: probably should say which arguments are candidates for glob
19
19
# expansion on windows and do that at the command level.
20
20
 
21
 
# TODO: Help messages for options.
22
 
 
23
21
# TODO: Define arguments by objects, rather than just using names.
24
22
# Those objects can specify the expected type of the argument, which
25
 
# would help with validation and shell completion.
26
 
 
27
 
 
28
 
 
 
23
# would help with validation and shell completion.  They could also provide
 
24
# help/explanation for that argument in a structured way.
 
25
 
 
26
# TODO: Specific "examples" property on commands for consistent formatting.
 
27
 
 
28
# TODO: "--profile=cum", to change sort order.  Is there any value in leaving
 
29
# the profile output behind so it can be interactively examined?
 
30
 
 
31
import os
29
32
import sys
30
 
import os
 
33
 
 
34
from bzrlib.lazy_import import lazy_import
 
35
lazy_import(globals(), """
 
36
import codecs
 
37
import errno
31
38
from warnings import warn
32
 
from inspect import getdoc
33
39
 
34
40
import bzrlib
35
 
import bzrlib.trace
36
 
from bzrlib.trace import mutter, note, log_error, warning
37
 
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError
38
 
from bzrlib.branch import find_branch
39
 
from bzrlib import BZRDIR
 
41
from bzrlib import (
 
42
    debug,
 
43
    errors,
 
44
    option,
 
45
    osutils,
 
46
    registry,
 
47
    trace,
 
48
    win32utils,
 
49
    )
 
50
""")
 
51
 
 
52
from bzrlib.symbol_versioning import (
 
53
    deprecated_function,
 
54
    deprecated_method,
 
55
    )
 
56
# Compatibility
 
57
from bzrlib.option import Option
 
58
 
40
59
 
41
60
plugin_cmds = {}
42
61
 
43
62
 
44
 
def register_command(cmd):
45
 
    "Utility function to help register a command"
 
63
def register_command(cmd, decorate=False):
 
64
    """Utility function to help register a command
 
65
 
 
66
    :param cmd: Command subclass to register
 
67
    :param decorate: If true, allow overriding an existing command
 
68
        of the same name; the old command is returned by this function.
 
69
        Otherwise it is an error to try to override an existing command.
 
70
    """
46
71
    global plugin_cmds
47
72
    k = cmd.__name__
48
73
    if k.startswith("cmd_"):
49
74
        k_unsquished = _unsquish_command_name(k)
50
75
    else:
51
76
        k_unsquished = k
52
 
    if not plugin_cmds.has_key(k_unsquished):
53
 
        plugin_cmds[k_unsquished] = cmd
54
 
        mutter('registered plugin command %s', k_unsquished)      
 
77
    if k_unsquished not in plugin_cmds:
 
78
        plugin_cmds[k_unsquished] = cmd
 
79
        ## trace.mutter('registered plugin command %s', k_unsquished)
 
80
        if decorate and k_unsquished in builtin_command_names():
 
81
            return _builtin_commands()[k_unsquished]
 
82
    elif decorate:
 
83
        result = plugin_cmds[k_unsquished]
 
84
        plugin_cmds[k_unsquished] = cmd
 
85
        return result
55
86
    else:
56
 
        log_error('Two plugins defined the same command: %r' % k)
57
 
        log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
 
87
        trace.log_error('Two plugins defined the same command: %r' % k)
 
88
        trace.log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
 
89
        trace.log_error('Previously this command was registered from %r' %
 
90
                        sys.modules[plugin_cmds[k_unsquished].__module__])
58
91
 
59
92
 
60
93
def _squish_command_name(cmd):
62
95
 
63
96
 
64
97
def _unsquish_command_name(cmd):
65
 
    assert cmd.startswith("cmd_")
66
98
    return cmd[4:].replace('_','-')
67
99
 
68
100
 
69
 
def _parse_revision_str(revstr):
70
 
    """This handles a revision string -> revno.
71
 
 
72
 
    This always returns a list.  The list will have one element for 
73
 
 
74
 
    It supports integers directly, but everything else it
75
 
    defers for passing to Branch.get_revision_info()
76
 
 
77
 
    >>> _parse_revision_str('234')
78
 
    [234]
79
 
    >>> _parse_revision_str('234..567')
80
 
    [234, 567]
81
 
    >>> _parse_revision_str('..')
82
 
    [None, None]
83
 
    >>> _parse_revision_str('..234')
84
 
    [None, 234]
85
 
    >>> _parse_revision_str('234..')
86
 
    [234, None]
87
 
    >>> _parse_revision_str('234..456..789') # Maybe this should be an error
88
 
    [234, 456, 789]
89
 
    >>> _parse_revision_str('234....789') # Error?
90
 
    [234, None, 789]
91
 
    >>> _parse_revision_str('revid:test@other.com-234234')
92
 
    ['revid:test@other.com-234234']
93
 
    >>> _parse_revision_str('revid:test@other.com-234234..revid:test@other.com-234235')
94
 
    ['revid:test@other.com-234234', 'revid:test@other.com-234235']
95
 
    >>> _parse_revision_str('revid:test@other.com-234234..23')
96
 
    ['revid:test@other.com-234234', 23]
97
 
    >>> _parse_revision_str('date:2005-04-12')
98
 
    ['date:2005-04-12']
99
 
    >>> _parse_revision_str('date:2005-04-12 12:24:33')
100
 
    ['date:2005-04-12 12:24:33']
101
 
    >>> _parse_revision_str('date:2005-04-12T12:24:33')
102
 
    ['date:2005-04-12T12:24:33']
103
 
    >>> _parse_revision_str('date:2005-04-12,12:24:33')
104
 
    ['date:2005-04-12,12:24:33']
105
 
    >>> _parse_revision_str('-5..23')
106
 
    [-5, 23]
107
 
    >>> _parse_revision_str('-5')
108
 
    [-5]
109
 
    >>> _parse_revision_str('123a')
110
 
    ['123a']
111
 
    >>> _parse_revision_str('abc')
112
 
    ['abc']
113
 
    """
114
 
    import re
115
 
    old_format_re = re.compile('\d*:\d*')
116
 
    m = old_format_re.match(revstr)
117
 
    if m:
118
 
        warning('Colon separator for revision numbers is deprecated.'
119
 
                ' Use .. instead')
120
 
        revs = []
121
 
        for rev in revstr.split(':'):
122
 
            if rev:
123
 
                revs.append(int(rev))
124
 
            else:
125
 
                revs.append(None)
126
 
        return revs
127
 
    revs = []
128
 
    for x in revstr.split('..'):
129
 
        if not x:
130
 
            revs.append(None)
131
 
        else:
132
 
            try:
133
 
                revs.append(int(x))
134
 
            except ValueError:
135
 
                revs.append(x)
136
 
    return revs
137
 
 
138
 
 
139
 
def get_merge_type(typestring):
140
 
    """Attempt to find the merge class/factory associated with a string."""
141
 
    from merge import merge_types
142
 
    try:
143
 
        return merge_types[typestring][0]
144
 
    except KeyError:
145
 
        templ = '%s%%7s: %%s' % (' '*12)
146
 
        lines = [templ % (f[0], f[1][1]) for f in merge_types.iteritems()]
147
 
        type_list = '\n'.join(lines)
148
 
        msg = "No known merge type %s. Supported types are:\n%s" %\
149
 
            (typestring, type_list)
150
 
        raise BzrCommandError(msg)
151
 
    
152
 
 
153
101
def _builtin_commands():
154
102
    import bzrlib.builtins
155
103
    r = {}
156
104
    builtins = bzrlib.builtins.__dict__
157
105
    for name in builtins:
158
106
        if name.startswith("cmd_"):
159
 
            real_name = _unsquish_command_name(name)        
 
107
            real_name = _unsquish_command_name(name)
160
108
            r[real_name] = builtins[name]
161
109
    return r
162
 
 
163
110
            
164
111
 
165
112
def builtin_command_names():
191
138
    plugins_override
192
139
        If true, plugin commands can override builtins.
193
140
    """
 
141
    try:
 
142
        return _get_cmd_object(cmd_name, plugins_override)
 
143
    except KeyError:
 
144
        raise errors.BzrCommandError('unknown command "%s"' % cmd_name)
 
145
 
 
146
 
 
147
def _get_cmd_object(cmd_name, plugins_override=True):
 
148
    """Worker for get_cmd_object which raises KeyError rather than BzrCommandError."""
194
149
    from bzrlib.externalcommand import ExternalCommand
195
150
 
196
 
    cmd_name = str(cmd_name)            # not unicode
 
151
    # We want only 'ascii' command names, but the user may have typed
 
152
    # in a Unicode name. In that case, they should just get a
 
153
    # 'command not found' error later.
 
154
    # In the future, we may actually support Unicode command names.
197
155
 
198
156
    # first look up this command under the specified name
199
157
    cmds = _get_cmd_dict(plugins_override=plugins_override)
211
169
    if cmd_obj:
212
170
        return cmd_obj
213
171
 
214
 
    raise BzrCommandError("unknown command %r" % cmd_name)
 
172
    # look for plugins that provide this command but aren't installed
 
173
    for provider in command_providers_registry:
 
174
        try:
 
175
            plugin_metadata = provider.plugin_for_command(cmd_name)
 
176
        except errors.NoPluginAvailable:
 
177
            pass
 
178
        else:
 
179
            raise errors.CommandAvailableInPlugin(cmd_name, 
 
180
                                                  plugin_metadata, provider)
 
181
 
 
182
    raise KeyError
215
183
 
216
184
 
217
185
class Command(object):
239
207
        List of argument forms, marked with whether they are optional,
240
208
        repeated, etc.
241
209
 
 
210
                Examples:
 
211
 
 
212
                ['to_location', 'from_branch?', 'file*']
 
213
 
 
214
                'to_location' is required
 
215
                'from_branch' is optional
 
216
                'file' can be specified 0 or more times
 
217
 
242
218
    takes_options
243
 
        List of options that may be given for this command.
 
219
        List of options that may be given for this command.  These can
 
220
        be either strings, referring to globally-defined options,
 
221
        or option objects.  Retrieve through options().
244
222
 
245
223
    hidden
246
224
        If true, this command isn't advertised.  This is typically
247
225
        for commands intended for expert users.
 
226
 
 
227
    encoding_type
 
228
        Command objects will get a 'outf' attribute, which has been
 
229
        setup to properly handle encoding of unicode strings.
 
230
        encoding_type determines what will happen when characters cannot
 
231
        be encoded
 
232
            strict - abort if we cannot decode
 
233
            replace - put in a bogus character (typically '?')
 
234
            exact - do not encode sys.stdout
 
235
 
 
236
            NOTE: by default on Windows, sys.stdout is opened as a text
 
237
            stream, therefore LF line-endings are converted to CRLF.
 
238
            When a command uses encoding_type = 'exact', then
 
239
            sys.stdout is forced to be a binary stream, and line-endings
 
240
            will not mangled.
 
241
 
248
242
    """
249
243
    aliases = []
250
 
    
251
244
    takes_args = []
252
245
    takes_options = []
 
246
    encoding_type = 'strict'
253
247
 
254
248
    hidden = False
255
249
    
257
251
        """Construct an instance of this command."""
258
252
        if self.__doc__ == Command.__doc__:
259
253
            warn("No help message set for %r" % self)
260
 
 
261
 
 
262
 
    def run_argv(self, argv):
263
 
        """Parse command line and run."""
264
 
        args, opts = parse_args(argv)
265
 
 
 
254
        # List of standard options directly supported
 
255
        self.supported_std_options = []
 
256
 
 
257
    def _maybe_expand_globs(self, file_list):
 
258
        """Glob expand file_list if the platform does not do that itself.
 
259
        
 
260
        :return: A possibly empty list of unicode paths.
 
261
 
 
262
        Introduced in bzrlib 0.18.
 
263
        """
 
264
        if not file_list:
 
265
            file_list = []
 
266
        if sys.platform == 'win32':
 
267
            file_list = win32utils.glob_expand(file_list)
 
268
        return list(file_list)
 
269
 
 
270
    def _usage(self):
 
271
        """Return single-line grammar for this command.
 
272
 
 
273
        Only describes arguments, not options.
 
274
        """
 
275
        s = 'bzr ' + self.name() + ' '
 
276
        for aname in self.takes_args:
 
277
            aname = aname.upper()
 
278
            if aname[-1] in ['$', '+']:
 
279
                aname = aname[:-1] + '...'
 
280
            elif aname[-1] == '?':
 
281
                aname = '[' + aname[:-1] + ']'
 
282
            elif aname[-1] == '*':
 
283
                aname = '[' + aname[:-1] + '...]'
 
284
            s += aname + ' '
 
285
        s = s[:-1]      # remove last space
 
286
        return s
 
287
 
 
288
    def get_help_text(self, additional_see_also=None, plain=True,
 
289
                      see_also_as_links=False):
 
290
        """Return a text string with help for this command.
 
291
        
 
292
        :param additional_see_also: Additional help topics to be
 
293
            cross-referenced.
 
294
        :param plain: if False, raw help (reStructuredText) is
 
295
            returned instead of plain text.
 
296
        :param see_also_as_links: if True, convert items in 'See also'
 
297
            list to internal links (used by bzr_man rstx generator)
 
298
        """
 
299
        doc = self.help()
 
300
        if doc is None:
 
301
            raise NotImplementedError("sorry, no detailed help yet for %r" % self.name())
 
302
 
 
303
        # Extract the summary (purpose) and sections out from the text
 
304
        purpose,sections = self._get_help_parts(doc)
 
305
 
 
306
        # If a custom usage section was provided, use it
 
307
        if sections.has_key('Usage'):
 
308
            usage = sections.pop('Usage')
 
309
        else:
 
310
            usage = self._usage()
 
311
 
 
312
        # The header is the purpose and usage
 
313
        result = ""
 
314
        result += ':Purpose: %s\n' % purpose
 
315
        if usage.find('\n') >= 0:
 
316
            result += ':Usage:\n%s\n' % usage
 
317
        else:
 
318
            result += ':Usage:   %s\n' % usage
 
319
        result += '\n'
 
320
 
 
321
        # Add the options
 
322
        options = option.get_optparser(self.options()).format_option_help()
 
323
        if options.startswith('Options:'):
 
324
            result += ':' + options
 
325
        elif options.startswith('options:'):
 
326
            # Python 2.4 version of optparse
 
327
            result += ':Options:' + options[len('options:'):]
 
328
        else:
 
329
            result += options
 
330
        result += '\n'
 
331
 
 
332
        # Add the description, indenting it 2 spaces
 
333
        # to match the indentation of the options
 
334
        if sections.has_key(None):
 
335
            text = sections.pop(None)
 
336
            text = '\n  '.join(text.splitlines())
 
337
            result += ':%s:\n  %s\n\n' % ('Description',text)
 
338
 
 
339
        # Add the custom sections (e.g. Examples). Note that there's no need
 
340
        # to indent these as they must be indented already in the source.
 
341
        if sections:
 
342
            labels = sorted(sections.keys())
 
343
            for label in labels:
 
344
                result += ':%s:\n%s\n\n' % (label,sections[label])
 
345
 
 
346
        # Add the aliases, source (plug-in) and see also links, if any
 
347
        if self.aliases:
 
348
            result += ':Aliases:  '
 
349
            result += ', '.join(self.aliases) + '\n'
 
350
        plugin_name = self.plugin_name()
 
351
        if plugin_name is not None:
 
352
            result += ':From:     plugin "%s"\n' % plugin_name
 
353
        see_also = self.get_see_also(additional_see_also)
 
354
        if see_also:
 
355
            if not plain and see_also_as_links:
 
356
                see_also_links = []
 
357
                for item in see_also:
 
358
                    if item == 'topics':
 
359
                        # topics doesn't have an independent section
 
360
                        # so don't create a real link
 
361
                        see_also_links.append(item)
 
362
                    else:
 
363
                        # Use a reST link for this entry
 
364
                        see_also_links.append("`%s`_" % (item,))
 
365
                see_also = see_also_links
 
366
            result += ':See also: '
 
367
            result += ', '.join(see_also) + '\n'
 
368
 
 
369
        # If this will be rendered as plain text, convert it
 
370
        if plain:
 
371
            import bzrlib.help_topics
 
372
            result = bzrlib.help_topics.help_as_plain_text(result)
 
373
        return result
 
374
 
 
375
    @staticmethod
 
376
    def _get_help_parts(text):
 
377
        """Split help text into a summary and named sections.
 
378
 
 
379
        :return: (summary,sections) where summary is the top line and
 
380
            sections is a dictionary of the rest indexed by section name.
 
381
            A section starts with a heading line of the form ":xxx:".
 
382
            Indented text on following lines is the section value.
 
383
            All text found outside a named section is assigned to the
 
384
            default section which is given the key of None.
 
385
        """
 
386
        def save_section(sections, label, section):
 
387
            if len(section) > 0:
 
388
                if sections.has_key(label):
 
389
                    sections[label] += '\n' + section
 
390
                else:
 
391
                    sections[label] = section
 
392
 
 
393
        lines = text.rstrip().splitlines()
 
394
        summary = lines.pop(0)
 
395
        sections = {}
 
396
        label,section = None,''
 
397
        for line in lines:
 
398
            if line.startswith(':') and line.endswith(':') and len(line) > 2:
 
399
                save_section(sections, label, section)
 
400
                label,section = line[1:-1],''
 
401
            elif (label is not None) and len(line) > 1 and not line[0].isspace():
 
402
                save_section(sections, label, section)
 
403
                label,section = None,line
 
404
            else:
 
405
                if len(section) > 0:
 
406
                    section += '\n' + line
 
407
                else:
 
408
                    section = line
 
409
        save_section(sections, label, section)
 
410
        return summary, sections
 
411
 
 
412
    def get_help_topic(self):
 
413
        """Return the commands help topic - its name."""
 
414
        return self.name()
 
415
 
 
416
    def get_see_also(self, additional_terms=None):
 
417
        """Return a list of help topics that are related to this command.
 
418
        
 
419
        The list is derived from the content of the _see_also attribute. Any
 
420
        duplicates are removed and the result is in lexical order.
 
421
        :param additional_terms: Additional help topics to cross-reference.
 
422
        :return: A list of help topics.
 
423
        """
 
424
        see_also = set(getattr(self, '_see_also', []))
 
425
        if additional_terms:
 
426
            see_also.update(additional_terms)
 
427
        return sorted(see_also)
 
428
 
 
429
    def options(self):
 
430
        """Return dict of valid options for this command.
 
431
 
 
432
        Maps from long option name to option object."""
 
433
        r = Option.STD_OPTIONS.copy()
 
434
        std_names = r.keys()
 
435
        for o in self.takes_options:
 
436
            if isinstance(o, basestring):
 
437
                o = option.Option.OPTIONS[o]
 
438
            r[o.name] = o
 
439
            if o.name in std_names:
 
440
                self.supported_std_options.append(o.name)
 
441
        return r
 
442
 
 
443
    def _setup_outf(self):
 
444
        """Return a file linked to stdout, which has proper encoding."""
 
445
        # Originally I was using self.stdout, but that looks
 
446
        # *way* too much like sys.stdout
 
447
        if self.encoding_type == 'exact':
 
448
            # force sys.stdout to be binary stream on win32
 
449
            if sys.platform == 'win32':
 
450
                fileno = getattr(sys.stdout, 'fileno', None)
 
451
                if fileno:
 
452
                    import msvcrt
 
453
                    msvcrt.setmode(fileno(), os.O_BINARY)
 
454
            self.outf = sys.stdout
 
455
            return
 
456
 
 
457
        output_encoding = osutils.get_terminal_encoding()
 
458
 
 
459
        self.outf = codecs.getwriter(output_encoding)(sys.stdout,
 
460
                        errors=self.encoding_type)
 
461
        # For whatever reason codecs.getwriter() does not advertise its encoding
 
462
        # it just returns the encoding of the wrapped file, which is completely
 
463
        # bogus. So set the attribute, so we can find the correct encoding later.
 
464
        self.outf.encoding = output_encoding
 
465
 
 
466
    def run_argv_aliases(self, argv, alias_argv=None):
 
467
        """Parse the command line and run with extra aliases in alias_argv."""
 
468
        if argv is None:
 
469
            warn("Passing None for [] is deprecated from bzrlib 0.10",
 
470
                 DeprecationWarning, stacklevel=2)
 
471
            argv = []
 
472
        args, opts = parse_args(self, argv, alias_argv)
 
473
 
 
474
        # Process the standard options
266
475
        if 'help' in opts:  # e.g. bzr add --help
267
 
            from bzrlib.help import help_on_command
268
 
            help_on_command(self.name())
 
476
            sys.stdout.write(self.get_help_text())
269
477
            return 0
270
 
 
271
 
        # check options are reasonable
272
 
        allowed = self.takes_options
273
 
        for oname in opts:
274
 
            if oname not in allowed:
275
 
                raise BzrCommandError("option '--%s' is not allowed for command %r"
276
 
                                      % (oname, self.name()))
 
478
        trace.set_verbosity_level(option._verbosity_level)
 
479
        if 'verbose' in self.supported_std_options:
 
480
            opts['verbose'] = trace.is_verbose()
 
481
        elif opts.has_key('verbose'):
 
482
            del opts['verbose']
 
483
        if 'quiet' in self.supported_std_options:
 
484
            opts['quiet'] = trace.is_quiet()
 
485
        elif opts.has_key('quiet'):
 
486
            del opts['quiet']
277
487
 
278
488
        # mix arguments and options into one dictionary
279
489
        cmdargs = _match_argform(self.name(), self.takes_args, args)
284
494
        all_cmd_args = cmdargs.copy()
285
495
        all_cmd_args.update(cmdopts)
286
496
 
 
497
        self._setup_outf()
 
498
 
287
499
        return self.run(**all_cmd_args)
288
500
 
289
 
    
290
501
    def run(self):
291
502
        """Actually run the command.
292
503
 
297
508
        shell error code if not.  It's OK for this method to allow
298
509
        an exception to raise up.
299
510
        """
300
 
        raise NotImplementedError()
301
 
 
 
511
        raise NotImplementedError('no implementation of command %r'
 
512
                                  % self.name())
302
513
 
303
514
    def help(self):
304
515
        """Return help message for this class."""
 
516
        from inspect import getdoc
305
517
        if self.__doc__ is Command.__doc__:
306
518
            return None
307
519
        return getdoc(self)
309
521
    def name(self):
310
522
        return _unsquish_command_name(self.__class__.__name__)
311
523
 
 
524
    def plugin_name(self):
 
525
        """Get the name of the plugin that provides this command.
312
526
 
313
 
def parse_spec(spec):
314
 
    """
315
 
    >>> parse_spec(None)
316
 
    [None, None]
317
 
    >>> parse_spec("./")
318
 
    ['./', None]
319
 
    >>> parse_spec("../@")
320
 
    ['..', -1]
321
 
    >>> parse_spec("../f/@35")
322
 
    ['../f', 35]
323
 
    >>> parse_spec('./@revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67')
324
 
    ['.', 'revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67']
325
 
    """
326
 
    if spec is None:
327
 
        return [None, None]
328
 
    if '/@' in spec:
329
 
        parsed = spec.split('/@')
330
 
        assert len(parsed) == 2
331
 
        if parsed[1] == "":
332
 
            parsed[1] = -1
 
527
        :return: The name of the plugin or None if the command is builtin.
 
528
        """
 
529
        mod_parts = self.__module__.split('.')
 
530
        if len(mod_parts) >= 3 and mod_parts[1] == 'plugins':
 
531
            return mod_parts[2]
333
532
        else:
334
 
            try:
335
 
                parsed[1] = int(parsed[1])
336
 
            except ValueError:
337
 
                pass # We can allow stuff like ./@revid:blahblahblah
338
 
            else:
339
 
                assert parsed[1] >=0
340
 
    else:
341
 
        parsed = [spec, None]
342
 
    return parsed
343
 
 
344
 
 
345
 
 
346
 
 
347
 
# list of all available options; the rhs can be either None for an
348
 
# option that takes no argument, or a constructor function that checks
349
 
# the type.
350
 
OPTIONS = {
351
 
    'all':                    None,
352
 
    'diff-options':           str,
353
 
    'help':                   None,
354
 
    'file':                   unicode,
355
 
    'force':                  None,
356
 
    'format':                 unicode,
357
 
    'forward':                None,
358
 
    'message':                unicode,
359
 
    'no-recurse':             None,
360
 
    'profile':                None,
361
 
    'revision':               _parse_revision_str,
362
 
    'short':                  None,
363
 
    'show-ids':               None,
364
 
    'timezone':               str,
365
 
    'verbose':                None,
366
 
    'version':                None,
367
 
    'email':                  None,
368
 
    'unchanged':              None,
369
 
    'update':                 None,
370
 
    'long':                   None,
371
 
    'root':                   str,
372
 
    'no-backup':              None,
373
 
    'merge-type':             get_merge_type,
374
 
    'pattern':                str,
375
 
    }
376
 
 
377
 
SHORT_OPTIONS = {
378
 
    'F':                      'file', 
379
 
    'h':                      'help',
380
 
    'm':                      'message',
381
 
    'r':                      'revision',
382
 
    'v':                      'verbose',
383
 
    'l':                      'long',
384
 
}
385
 
 
386
 
 
387
 
def parse_args(argv):
 
533
            return None
 
534
 
 
535
 
 
536
def parse_args(command, argv, alias_argv=None):
388
537
    """Parse command line.
389
538
    
390
539
    Arguments and options are parsed at this level before being passed
391
540
    down to specific command handlers.  This routine knows, from a
392
541
    lookup table, something about the available options, what optargs
393
542
    they take, and which commands will accept them.
394
 
 
395
 
    >>> parse_args('--help'.split())
396
 
    ([], {'help': True})
397
 
    >>> parse_args('help -- --invalidcmd'.split())
398
 
    (['help', '--invalidcmd'], {})
399
 
    >>> parse_args('--version'.split())
400
 
    ([], {'version': True})
401
 
    >>> parse_args('status --all'.split())
402
 
    (['status'], {'all': True})
403
 
    >>> parse_args('commit --message=biter'.split())
404
 
    (['commit'], {'message': u'biter'})
405
 
    >>> parse_args('log -r 500'.split())
406
 
    (['log'], {'revision': [500]})
407
 
    >>> parse_args('log -r500..600'.split())
408
 
    (['log'], {'revision': [500, 600]})
409
 
    >>> parse_args('log -vr500..600'.split())
410
 
    (['log'], {'verbose': True, 'revision': [500, 600]})
411
 
    >>> parse_args('log -rv500..600'.split()) #the r takes an argument
412
 
    (['log'], {'revision': ['v500', 600]})
413
543
    """
414
 
    args = []
415
 
    opts = {}
416
 
 
417
 
    argsover = False
418
 
    while argv:
419
 
        a = argv.pop(0)
420
 
        if not argsover and a[0] == '-':
421
 
            # option names must not be unicode
422
 
            a = str(a)
423
 
            optarg = None
424
 
            if a[1] == '-':
425
 
                if a == '--':
426
 
                    # We've received a standalone -- No more flags
427
 
                    argsover = True
428
 
                    continue
429
 
                mutter("  got option %r" % a)
430
 
                if '=' in a:
431
 
                    optname, optarg = a[2:].split('=', 1)
432
 
                else:
433
 
                    optname = a[2:]
434
 
                if optname not in OPTIONS:
435
 
                    raise BzrError('unknown long option %r' % a)
436
 
            else:
437
 
                shortopt = a[1:]
438
 
                if shortopt in SHORT_OPTIONS:
439
 
                    # Multi-character options must have a space to delimit
440
 
                    # their value
441
 
                    optname = SHORT_OPTIONS[shortopt]
442
 
                else:
443
 
                    # Single character short options, can be chained,
444
 
                    # and have their value appended to their name
445
 
                    shortopt = a[1:2]
446
 
                    if shortopt not in SHORT_OPTIONS:
447
 
                        # We didn't find the multi-character name, and we
448
 
                        # didn't find the single char name
449
 
                        raise BzrError('unknown short option %r' % a)
450
 
                    optname = SHORT_OPTIONS[shortopt]
451
 
 
452
 
                    if a[2:]:
453
 
                        # There are extra things on this option
454
 
                        # see if it is the value, or if it is another
455
 
                        # short option
456
 
                        optargfn = OPTIONS[optname]
457
 
                        if optargfn is None:
458
 
                            # This option does not take an argument, so the
459
 
                            # next entry is another short option, pack it back
460
 
                            # into the list
461
 
                            argv.insert(0, '-' + a[2:])
462
 
                        else:
463
 
                            # This option takes an argument, so pack it
464
 
                            # into the array
465
 
                            optarg = a[2:]
466
 
            
467
 
            if optname in opts:
468
 
                # XXX: Do we ever want to support this, e.g. for -r?
469
 
                raise BzrError('repeated option %r' % a)
470
 
                
471
 
            optargfn = OPTIONS[optname]
472
 
            if optargfn:
473
 
                if optarg == None:
474
 
                    if not argv:
475
 
                        raise BzrError('option %r needs an argument' % a)
476
 
                    else:
477
 
                        optarg = argv.pop(0)
478
 
                opts[optname] = optargfn(optarg)
479
 
            else:
480
 
                if optarg != None:
481
 
                    raise BzrError('option %r takes no argument' % optname)
482
 
                opts[optname] = True
483
 
        else:
484
 
            args.append(a)
485
 
 
 
544
    # TODO: make it a method of the Command?
 
545
    parser = option.get_optparser(command.options())
 
546
    if alias_argv is not None:
 
547
        args = alias_argv + argv
 
548
    else:
 
549
        args = argv
 
550
 
 
551
    options, args = parser.parse_args(args)
 
552
    opts = dict([(k, v) for k, v in options.__dict__.iteritems() if
 
553
                 v is not option.OptionParser.DEFAULT_VALUE])
486
554
    return args, opts
487
555
 
488
556
 
489
 
 
490
 
 
491
557
def _match_argform(cmd, takes_args, args):
492
558
    argdict = {}
493
559
 
505
571
                argdict[argname + '_list'] = None
506
572
        elif ap[-1] == '+':
507
573
            if not args:
508
 
                raise BzrCommandError("command %r needs one or more %s"
509
 
                        % (cmd, argname.upper()))
 
574
                raise errors.BzrCommandError("command %r needs one or more %s"
 
575
                                             % (cmd, argname.upper()))
510
576
            else:
511
577
                argdict[argname + '_list'] = args[:]
512
578
                args = []
513
579
        elif ap[-1] == '$': # all but one
514
580
            if len(args) < 2:
515
 
                raise BzrCommandError("command %r needs one or more %s"
516
 
                        % (cmd, argname.upper()))
 
581
                raise errors.BzrCommandError("command %r needs one or more %s"
 
582
                                             % (cmd, argname.upper()))
517
583
            argdict[argname + '_list'] = args[:-1]
518
 
            args[:-1] = []                
 
584
            args[:-1] = []
519
585
        else:
520
586
            # just a plain arg
521
587
            argname = ap
522
588
            if not args:
523
 
                raise BzrCommandError("command %r requires argument %s"
524
 
                        % (cmd, argname.upper()))
 
589
                raise errors.BzrCommandError("command %r requires argument %s"
 
590
                               % (cmd, argname.upper()))
525
591
            else:
526
592
                argdict[argname] = args.pop(0)
527
593
            
528
594
    if args:
529
 
        raise BzrCommandError("extra argument to command %s: %s"
530
 
                              % (cmd, args[0]))
 
595
        raise errors.BzrCommandError("extra argument to command %s: %s"
 
596
                                     % (cmd, args[0]))
531
597
 
532
598
    return argdict
533
599
 
 
600
def apply_coveraged(dirname, the_callable, *args, **kwargs):
 
601
    # Cannot use "import trace", as that would import bzrlib.trace instead of
 
602
    # the standard library's trace.
 
603
    trace = __import__('trace')
 
604
 
 
605
    tracer = trace.Trace(count=1, trace=0)
 
606
    sys.settrace(tracer.globaltrace)
 
607
 
 
608
    ret = the_callable(*args, **kwargs)
 
609
 
 
610
    sys.settrace(None)
 
611
    results = tracer.results()
 
612
    results.write_results(show_missing=1, summary=False,
 
613
                          coverdir=dirname)
534
614
 
535
615
 
536
616
def apply_profiled(the_callable, *args, **kwargs):
537
617
    import hotshot
538
618
    import tempfile
 
619
    import hotshot.stats
539
620
    pffileno, pfname = tempfile.mkstemp()
540
621
    try:
541
622
        prof = hotshot.Profile(pfname)
543
624
            ret = prof.runcall(the_callable, *args, **kwargs) or 0
544
625
        finally:
545
626
            prof.close()
546
 
 
547
 
        import hotshot.stats
548
627
        stats = hotshot.stats.load(pfname)
549
 
        #stats.strip_dirs()
550
 
        stats.sort_stats('time')
 
628
        stats.strip_dirs()
 
629
        stats.sort_stats('cum')   # 'time'
551
630
        ## XXX: Might like to write to stderr or the trace file instead but
552
631
        ## print_stats seems hardcoded to stdout
553
632
        stats.print_stats(20)
554
 
 
555
633
        return ret
556
634
    finally:
557
635
        os.close(pffileno)
558
636
        os.remove(pfname)
559
637
 
560
638
 
 
639
def apply_lsprofiled(filename, the_callable, *args, **kwargs):
 
640
    from bzrlib.lsprof import profile
 
641
    ret, stats = profile(the_callable, *args, **kwargs)
 
642
    stats.sort()
 
643
    if filename is None:
 
644
        stats.pprint()
 
645
    else:
 
646
        stats.save(filename)
 
647
        trace.note('Profile data written to "%s".', filename)
 
648
    return ret
 
649
 
 
650
 
 
651
def shlex_split_unicode(unsplit):
 
652
    import shlex
 
653
    return [u.decode('utf-8') for u in shlex.split(unsplit.encode('utf-8'))]
 
654
 
 
655
 
 
656
def get_alias(cmd, config=None):
 
657
    """Return an expanded alias, or None if no alias exists.
 
658
 
 
659
    cmd
 
660
        Command to be checked for an alias.
 
661
    config
 
662
        Used to specify an alternative config to use,
 
663
        which is especially useful for testing.
 
664
        If it is unspecified, the global config will be used.
 
665
    """
 
666
    if config is None:
 
667
        import bzrlib.config
 
668
        config = bzrlib.config.GlobalConfig()
 
669
    alias = config.get_alias(cmd)
 
670
    if (alias):
 
671
        return shlex_split_unicode(alias)
 
672
    return None
 
673
 
 
674
 
561
675
def run_bzr(argv):
562
676
    """Execute a command.
563
677
 
566
680
    
567
681
    argv
568
682
       The command-line arguments, without the program name from argv[0]
 
683
       These should already be decoded. All library/test code calling
 
684
       run_bzr should be passing valid strings (don't need decoding).
569
685
    
570
686
    Returns a command status or raises an exception.
571
687
 
575
691
    --no-plugins
576
692
        Do not load plugin modules at all
577
693
 
 
694
    --no-aliases
 
695
        Do not allow aliases
 
696
 
578
697
    --builtin
579
698
        Only use builtin commands.  (Plugins are still allowed to change
580
699
        other behaviour.)
581
700
 
582
701
    --profile
583
 
        Run under the Python profiler.
 
702
        Run under the Python hotshot profiler.
 
703
 
 
704
    --lsprof
 
705
        Run under the Python lsprof profiler.
 
706
 
 
707
    --coverage
 
708
        Generate line coverage report in the specified directory.
584
709
    """
585
 
    
586
 
    argv = [a.decode(bzrlib.user_encoding) for a in argv]
 
710
    argv = list(argv)
 
711
    trace.mutter("bzr arguments: %r", argv)
587
712
 
588
 
    opt_profile = opt_no_plugins = opt_builtin = False
 
713
    opt_lsprof = opt_profile = opt_no_plugins = opt_builtin =  \
 
714
                opt_no_aliases = False
 
715
    opt_lsprof_file = opt_coverage_dir = None
589
716
 
590
717
    # --no-plugins is handled specially at a very early stage. We need
591
718
    # to load plugins before doing other command parsing so that they
592
719
    # can override commands, but this needs to happen first.
593
720
 
594
 
    for a in argv:
 
721
    argv_copy = []
 
722
    i = 0
 
723
    while i < len(argv):
 
724
        a = argv[i]
595
725
        if a == '--profile':
596
726
            opt_profile = True
 
727
        elif a == '--lsprof':
 
728
            opt_lsprof = True
 
729
        elif a == '--lsprof-file':
 
730
            opt_lsprof = True
 
731
            opt_lsprof_file = argv[i + 1]
 
732
            i += 1
597
733
        elif a == '--no-plugins':
598
734
            opt_no_plugins = True
 
735
        elif a == '--no-aliases':
 
736
            opt_no_aliases = True
599
737
        elif a == '--builtin':
600
738
            opt_builtin = True
 
739
        elif a == '--coverage':
 
740
            opt_coverage_dir = argv[i + 1]
 
741
            i += 1
 
742
        elif a.startswith('-D'):
 
743
            debug.debug_flags.add(a[2:])
601
744
        else:
602
 
            break
603
 
        argv.remove(a)
 
745
            argv_copy.append(a)
 
746
        i += 1
604
747
 
605
 
    if (not argv) or (argv[0] == '--help'):
606
 
        from bzrlib.help import help
607
 
        if len(argv) > 1:
608
 
            help(argv[1])
609
 
        else:
610
 
            help()
 
748
    argv = argv_copy
 
749
    if (not argv):
 
750
        from bzrlib.builtins import cmd_help
 
751
        cmd_help().run_argv_aliases([])
611
752
        return 0
612
753
 
613
754
    if argv[0] == '--version':
614
 
        from bzrlib.builtins import show_version
615
 
        show_version()
 
755
        from bzrlib.builtins import cmd_version
 
756
        cmd_version().run_argv_aliases([])
616
757
        return 0
617
758
        
618
759
    if not opt_no_plugins:
619
760
        from bzrlib.plugin import load_plugins
620
761
        load_plugins()
621
 
 
622
 
    cmd = str(argv.pop(0))
 
762
    else:
 
763
        from bzrlib.plugin import disable_plugins
 
764
        disable_plugins()
 
765
 
 
766
    alias_argv = None
 
767
 
 
768
    if not opt_no_aliases:
 
769
        alias_argv = get_alias(argv[0])
 
770
        if alias_argv:
 
771
            alias_argv = [a.decode(bzrlib.user_encoding) for a in alias_argv]
 
772
            argv[0] = alias_argv.pop(0)
 
773
 
 
774
    cmd = argv.pop(0)
 
775
    # We want only 'ascii' command names, but the user may have typed
 
776
    # in a Unicode name. In that case, they should just get a
 
777
    # 'command not found' error later.
623
778
 
624
779
    cmd_obj = get_cmd_object(cmd, plugins_override=not opt_builtin)
625
 
 
626
 
    if opt_profile:
627
 
        ret = apply_profiled(cmd_obj.run_argv, argv)
628
 
    else:
629
 
        ret = cmd_obj.run_argv(argv)
630
 
    return ret or 0
 
780
    run = cmd_obj.run_argv_aliases
 
781
    run_argv = [argv, alias_argv]
 
782
 
 
783
    try:
 
784
        if opt_lsprof:
 
785
            if opt_coverage_dir:
 
786
                trace.warning(
 
787
                    '--coverage ignored, because --lsprof is in use.')
 
788
            ret = apply_lsprofiled(opt_lsprof_file, run, *run_argv)
 
789
        elif opt_profile:
 
790
            if opt_coverage_dir:
 
791
                trace.warning(
 
792
                    '--coverage ignored, because --profile is in use.')
 
793
            ret = apply_profiled(run, *run_argv)
 
794
        elif opt_coverage_dir:
 
795
            ret = apply_coveraged(opt_coverage_dir, run, *run_argv)
 
796
        else:
 
797
            ret = run(*run_argv)
 
798
        if 'memory' in debug.debug_flags:
 
799
            try:
 
800
                status_file = file('/proc/%s/status' % os.getpid(), 'rb')
 
801
            except IOError:
 
802
                pass
 
803
            else:
 
804
                status = status_file.read()
 
805
                status_file.close()
 
806
                trace.note("Process status after command:")
 
807
                for line in status.splitlines():
 
808
                    trace.note(line)
 
809
        return ret or 0
 
810
    finally:
 
811
        # reset, in case we may do other commands later within the same process
 
812
        option._verbosity_level = 0
 
813
 
 
814
def display_command(func):
 
815
    """Decorator that suppresses pipe/interrupt errors."""
 
816
    def ignore_pipe(*args, **kwargs):
 
817
        try:
 
818
            result = func(*args, **kwargs)
 
819
            sys.stdout.flush()
 
820
            return result
 
821
        except IOError, e:
 
822
            if getattr(e, 'errno', None) is None:
 
823
                raise
 
824
            if e.errno != errno.EPIPE:
 
825
                # Win32 raises IOError with errno=0 on a broken pipe
 
826
                if sys.platform != 'win32' or (e.errno not in (0, errno.EINVAL)):
 
827
                    raise
 
828
            pass
 
829
        except KeyboardInterrupt:
 
830
            pass
 
831
    return ignore_pipe
631
832
 
632
833
 
633
834
def main(argv):
634
835
    import bzrlib.ui
635
 
    bzrlib.trace.log_startup(argv)
636
 
    bzrlib.ui.ui_factory = bzrlib.ui.TextUIFactory()
637
 
 
638
 
    try:
639
 
        try:
640
 
            return run_bzr(argv[1:])
641
 
        finally:
642
 
            # do this here inside the exception wrappers to catch EPIPE
643
 
            sys.stdout.flush()
644
 
    except BzrCommandError, e:
645
 
        # command line syntax error, etc
646
 
        log_error(str(e))
647
 
        return 1
648
 
    except BzrError, e:
649
 
        bzrlib.trace.log_exception()
650
 
        return 1
651
 
    except AssertionError, e:
652
 
        bzrlib.trace.log_exception('assertion failed: ' + str(e))
653
 
        return 3
654
 
    except KeyboardInterrupt, e:
655
 
        bzrlib.trace.note('interrupted')
656
 
        return 2
 
836
    from bzrlib.ui.text import TextUIFactory
 
837
    bzrlib.ui.ui_factory = TextUIFactory()
 
838
     
 
839
    # Is this a final release version? If so, we should suppress warnings
 
840
    if bzrlib.version_info[3] == 'final':
 
841
        from bzrlib import symbol_versioning
 
842
        symbol_versioning.suppress_deprecation_warnings(override=False)
 
843
    try:
 
844
        argv = [a.decode(bzrlib.user_encoding) for a in argv[1:]]
 
845
    except UnicodeDecodeError:
 
846
        raise errors.BzrError(("Parameter '%r' is unsupported by the current "
 
847
                                                            "encoding." % a))
 
848
    ret = run_bzr_catch_errors(argv)
 
849
    trace.mutter("return code %d", ret)
 
850
    return ret
 
851
 
 
852
 
 
853
def run_bzr_catch_errors(argv):
 
854
    # Note: The except clause logic below should be kept in sync with the
 
855
    # profile() routine in lsprof.py.
 
856
    try:
 
857
        return run_bzr(argv)
 
858
    except (KeyboardInterrupt, Exception), e:
 
859
        # used to handle AssertionError and KeyboardInterrupt
 
860
        # specially here, but hopefully they're handled ok by the logger now
 
861
        exitcode = trace.report_exception(sys.exc_info(), sys.stderr)
 
862
        if os.environ.get('BZR_PDB'):
 
863
            print '**** entering debugger'
 
864
            import pdb
 
865
            pdb.post_mortem(sys.exc_traceback)
 
866
        return exitcode
 
867
 
 
868
 
 
869
def run_bzr_catch_user_errors(argv):
 
870
    """Run bzr and report user errors, but let internal errors propagate.
 
871
 
 
872
    This is used for the test suite, and might be useful for other programs
 
873
    that want to wrap the commandline interface.
 
874
    """
 
875
    try:
 
876
        return run_bzr(argv)
657
877
    except Exception, e:
658
 
        import errno
659
 
        if (isinstance(e, IOError) 
660
 
            and hasattr(e, 'errno')
661
 
            and e.errno == errno.EPIPE):
662
 
            bzrlib.trace.note('broken pipe')
663
 
            return 2
664
 
        else:
665
 
            bzrlib.trace.log_exception()
666
 
            return 2
 
878
        if (isinstance(e, (OSError, IOError))
 
879
            or not getattr(e, 'internal_error', True)):
 
880
            trace.report_exception(sys.exc_info(), sys.stderr)
 
881
            return 3
 
882
        else:
 
883
            raise
 
884
 
 
885
 
 
886
class HelpCommandIndex(object):
 
887
    """A index for bzr help that returns commands."""
 
888
 
 
889
    def __init__(self):
 
890
        self.prefix = 'commands/'
 
891
 
 
892
    def get_topics(self, topic):
 
893
        """Search for topic amongst commands.
 
894
 
 
895
        :param topic: A topic to search for.
 
896
        :return: A list which is either empty or contains a single
 
897
            Command entry.
 
898
        """
 
899
        if topic and topic.startswith(self.prefix):
 
900
            topic = topic[len(self.prefix):]
 
901
        try:
 
902
            cmd = _get_cmd_object(topic)
 
903
        except KeyError:
 
904
            return []
 
905
        else:
 
906
            return [cmd]
 
907
 
 
908
 
 
909
class Provider(object):
 
910
    '''Generic class to be overriden by plugins'''
 
911
 
 
912
    def plugin_for_command(self, cmd_name):
 
913
        '''Takes a command and returns the information for that plugin
 
914
        
 
915
        :return: A dictionary with all the available information 
 
916
        for the requested plugin
 
917
        '''
 
918
        raise NotImplementedError
 
919
 
 
920
 
 
921
class ProvidersRegistry(registry.Registry):
 
922
    '''This registry exists to allow other providers to exist'''
 
923
 
 
924
    def __iter__(self):
 
925
        for key, provider in self.iteritems():
 
926
            yield provider
 
927
 
 
928
command_providers_registry = ProvidersRegistry()
667
929
 
668
930
 
669
931
if __name__ == '__main__':