~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Robert Collins
  • Date: 2005-09-29 02:55:34 UTC
  • mfrom: (1185.1.47)
  • mto: This revision was merged to the branch mainline in revision 1397.
  • Revision ID: robertc@robertcollins.net-20050929025534-1782933743abbfd5
update with integration

Show diffs side-by-side

added added

removed removed

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