~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

[merge] fix \t in commit messages

Show diffs side-by-side

added added

removed removed

Lines of Context:
24
24
# Those objects can specify the expected type of the argument, which
25
25
# would help with validation and shell completion.
26
26
 
27
 
 
 
27
# TODO: "--profile=cum", to change sort order.  Is there any value in leaving
 
28
# the profile output behind so it can be interactively examined?
28
29
 
29
30
import sys
30
31
import os
31
32
from warnings import warn
32
33
from inspect import getdoc
 
34
import errno
33
35
 
34
36
import bzrlib
35
37
import bzrlib.trace
36
38
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.errors import (BzrError, 
 
40
                           BzrCheckError,
 
41
                           BzrCommandError,
 
42
                           BzrOptionError,
 
43
                           NotBranchError)
 
44
from bzrlib.revisionspec import RevisionSpec
39
45
from bzrlib import BZRDIR
 
46
from bzrlib.option import Option
40
47
 
41
48
plugin_cmds = {}
42
49
 
43
50
 
44
 
def register_command(cmd):
 
51
def register_command(cmd, decorate=False):
45
52
    "Utility function to help register a command"
46
53
    global plugin_cmds
47
54
    k = cmd.__name__
52
59
    if not plugin_cmds.has_key(k_unsquished):
53
60
        plugin_cmds[k_unsquished] = cmd
54
61
        mutter('registered plugin command %s', k_unsquished)      
 
62
        if decorate and k_unsquished in builtin_command_names():
 
63
            return _builtin_commands()[k_unsquished]
 
64
    elif decorate:
 
65
        result = plugin_cmds[k_unsquished]
 
66
        plugin_cmds[k_unsquished] = cmd
 
67
        return result
55
68
    else:
56
69
        log_error('Two plugins defined the same command: %r' % k)
57
70
        log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
66
79
    return cmd[4:].replace('_','-')
67
80
 
68
81
 
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
82
def _builtin_commands():
154
83
    import bzrlib.builtins
155
84
    r = {}
239
168
        List of argument forms, marked with whether they are optional,
240
169
        repeated, etc.
241
170
 
 
171
                Examples:
 
172
 
 
173
                ['to_location', 'from_branch?', 'file*']
 
174
 
 
175
                'to_location' is required
 
176
                'from_branch' is optional
 
177
                'file' can be specified 0 or more times
 
178
 
242
179
    takes_options
243
 
        List of options that may be given for this command.
 
180
        List of options that may be given for this command.  These can
 
181
        be either strings, referring to globally-defined options,
 
182
        or option objects.  Retrieve through options().
244
183
 
245
184
    hidden
246
185
        If true, this command isn't advertised.  This is typically
247
186
        for commands intended for expert users.
248
187
    """
249
188
    aliases = []
250
 
    
251
189
    takes_args = []
252
190
    takes_options = []
253
191
 
258
196
        if self.__doc__ == Command.__doc__:
259
197
            warn("No help message set for %r" % self)
260
198
 
 
199
    def options(self):
 
200
        """Return dict of valid options for this command.
 
201
 
 
202
        Maps from long option name to option object."""
 
203
        r = dict()
 
204
        r['help'] = Option.OPTIONS['help']
 
205
        for o in self.takes_options:
 
206
            if not isinstance(o, Option):
 
207
                o = Option.OPTIONS[o]
 
208
            r[o.name] = o
 
209
        return r
261
210
 
262
211
    def run_argv(self, argv):
263
212
        """Parse command line and run."""
264
 
        args, opts = parse_args(argv)
265
 
 
 
213
        args, opts = parse_args(self, argv)
266
214
        if 'help' in opts:  # e.g. bzr add --help
267
215
            from bzrlib.help import help_on_command
268
216
            help_on_command(self.name())
269
217
            return 0
270
 
 
271
 
        # check options are reasonable
272
 
        allowed = self.takes_options
 
218
        # XXX: This should be handled by the parser
 
219
        allowed_names = self.options().keys()
273
220
        for oname in opts:
274
 
            if oname not in allowed:
 
221
            if oname not in allowed_names:
275
222
                raise BzrCommandError("option '--%s' is not allowed for command %r"
276
223
                                      % (oname, self.name()))
277
 
 
278
224
        # mix arguments and options into one dictionary
279
225
        cmdargs = _match_argform(self.name(), self.takes_args, args)
280
226
        cmdopts = {}
285
231
        all_cmd_args.update(cmdopts)
286
232
 
287
233
        return self.run(**all_cmd_args)
288
 
 
289
234
    
290
235
    def run(self):
291
236
        """Actually run the command.
341
286
        parsed = [spec, None]
342
287
    return parsed
343
288
 
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):
 
289
def parse_args(command, argv):
388
290
    """Parse command line.
389
291
    
390
292
    Arguments and options are parsed at this level before being passed
391
293
    down to specific command handlers.  This routine knows, from a
392
294
    lookup table, something about the available options, what optargs
393
295
    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
296
    """
 
297
    # TODO: chop up this beast; make it a method of the Command
414
298
    args = []
415
299
    opts = {}
416
300
 
 
301
    cmd_options = command.options()
417
302
    argsover = False
418
303
    while argv:
419
304
        a = argv.pop(0)
420
 
        if not argsover and a[0] == '-':
 
305
        if argsover:
 
306
            args.append(a)
 
307
            continue
 
308
        elif a == '--':
 
309
            # We've received a standalone -- No more flags
 
310
            argsover = True
 
311
            continue
 
312
        if a[0] == '-':
421
313
            # option names must not be unicode
422
314
            a = str(a)
423
315
            optarg = None
424
316
            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)
 
317
                mutter("  got option %r", a)
430
318
                if '=' in a:
431
319
                    optname, optarg = a[2:].split('=', 1)
432
320
                else:
433
321
                    optname = a[2:]
434
 
                if optname not in OPTIONS:
435
 
                    raise BzrError('unknown long option %r' % a)
 
322
                if optname not in cmd_options:
 
323
                    raise BzrOptionError('unknown long option %r for command %s'
 
324
                        % (a, command.name()))
436
325
            else:
437
326
                shortopt = a[1:]
438
 
                if shortopt in SHORT_OPTIONS:
 
327
                if shortopt in Option.SHORT_OPTIONS:
439
328
                    # Multi-character options must have a space to delimit
440
329
                    # their value
441
 
                    optname = SHORT_OPTIONS[shortopt]
 
330
                    # ^^^ what does this mean? mbp 20051014
 
331
                    optname = Option.SHORT_OPTIONS[shortopt].name
442
332
                else:
443
333
                    # Single character short options, can be chained,
444
334
                    # and have their value appended to their name
445
335
                    shortopt = a[1:2]
446
 
                    if shortopt not in SHORT_OPTIONS:
 
336
                    if shortopt not in Option.SHORT_OPTIONS:
447
337
                        # We didn't find the multi-character name, and we
448
338
                        # didn't find the single char name
449
339
                        raise BzrError('unknown short option %r' % a)
450
 
                    optname = SHORT_OPTIONS[shortopt]
 
340
                    optname = Option.SHORT_OPTIONS[shortopt].name
451
341
 
452
342
                    if a[2:]:
453
343
                        # There are extra things on this option
454
344
                        # see if it is the value, or if it is another
455
345
                        # short option
456
 
                        optargfn = OPTIONS[optname]
 
346
                        optargfn = Option.OPTIONS[optname].type
457
347
                        if optargfn is None:
458
348
                            # This option does not take an argument, so the
459
349
                            # next entry is another short option, pack it back
464
354
                            # into the array
465
355
                            optarg = a[2:]
466
356
            
 
357
                if optname not in cmd_options:
 
358
                    raise BzrOptionError('unknown short option %r for command'
 
359
                        ' %s' % (shortopt, command.name()))
467
360
            if optname in opts:
468
361
                # XXX: Do we ever want to support this, e.g. for -r?
469
362
                raise BzrError('repeated option %r' % a)
470
363
                
471
 
            optargfn = OPTIONS[optname]
 
364
            option_obj = cmd_options[optname]
 
365
            optargfn = option_obj.type
472
366
            if optargfn:
473
367
                if optarg == None:
474
368
                    if not argv:
482
376
                opts[optname] = True
483
377
        else:
484
378
            args.append(a)
485
 
 
486
379
    return args, opts
487
380
 
488
381
 
489
 
 
490
 
 
491
382
def _match_argform(cmd, takes_args, args):
492
383
    argdict = {}
493
384
 
536
427
def apply_profiled(the_callable, *args, **kwargs):
537
428
    import hotshot
538
429
    import tempfile
 
430
    import hotshot.stats
539
431
    pffileno, pfname = tempfile.mkstemp()
540
432
    try:
541
433
        prof = hotshot.Profile(pfname)
543
435
            ret = prof.runcall(the_callable, *args, **kwargs) or 0
544
436
        finally:
545
437
            prof.close()
546
 
 
547
 
        import hotshot.stats
548
438
        stats = hotshot.stats.load(pfname)
549
 
        #stats.strip_dirs()
550
 
        stats.sort_stats('time')
 
439
        stats.strip_dirs()
 
440
        stats.sort_stats('cum')   # 'time'
551
441
        ## XXX: Might like to write to stderr or the trace file instead but
552
442
        ## print_stats seems hardcoded to stdout
553
443
        stats.print_stats(20)
554
 
 
555
444
        return ret
556
445
    finally:
557
446
        os.close(pffileno)
582
471
    --profile
583
472
        Run under the Python profiler.
584
473
    """
585
 
    
586
474
    argv = [a.decode(bzrlib.user_encoding) for a in argv]
587
475
 
588
476
    opt_profile = opt_no_plugins = opt_builtin = False
629
517
        ret = cmd_obj.run_argv(argv)
630
518
    return ret or 0
631
519
 
 
520
def display_command(func):
 
521
    """Decorator that suppresses pipe/interrupt errors."""
 
522
    def ignore_pipe(*args, **kwargs):
 
523
        try:
 
524
            result = func(*args, **kwargs)
 
525
            sys.stdout.flush()
 
526
            return result
 
527
        except IOError, e:
 
528
            if not hasattr(e, 'errno'):
 
529
                raise
 
530
            if e.errno != errno.EPIPE:
 
531
                raise
 
532
            pass
 
533
        except KeyboardInterrupt:
 
534
            pass
 
535
    return ignore_pipe
632
536
 
633
537
def main(argv):
634
538
    import bzrlib.ui
635
539
    bzrlib.trace.log_startup(argv)
636
540
    bzrlib.ui.ui_factory = bzrlib.ui.TextUIFactory()
637
541
 
 
542
    return run_bzr_catch_errors(argv[1:])
 
543
 
 
544
 
 
545
def run_bzr_catch_errors(argv):
638
546
    try:
639
547
        try:
640
 
            return run_bzr(argv[1:])
 
548
            return run_bzr(argv)
641
549
        finally:
642
550
            # do this here inside the exception wrappers to catch EPIPE
643
551
            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
657
552
    except Exception, e:
 
553
        # used to handle AssertionError and KeyboardInterrupt
 
554
        # specially here, but hopefully they're handled ok by the logger now
658
555
        import errno
659
556
        if (isinstance(e, IOError) 
660
557
            and hasattr(e, 'errno')
661
558
            and e.errno == errno.EPIPE):
662
559
            bzrlib.trace.note('broken pipe')
663
 
            return 2
 
560
            return 3
664
561
        else:
665
562
            bzrlib.trace.log_exception()
666
 
            return 2
667
 
 
 
563
            if os.environ.get('BZR_PDB'):
 
564
                print '**** entering debugger'
 
565
                import pdb
 
566
                pdb.post_mortem(sys.exc_traceback)
 
567
            return 3
668
568
 
669
569
if __name__ == '__main__':
670
570
    sys.exit(main(sys.argv))