~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Robert Collins
  • Date: 2006-02-11 11:58:06 UTC
  • mto: (1534.1.22 integration)
  • mto: This revision was merged to the branch mainline in revision 1554.
  • Revision ID: robertc@robertcollins.net-20060211115806-732dabc1e35714ed
Give format3 working trees their own last-revision marker.

Show diffs side-by-side

added added

removed removed

Lines of Context:
31
31
import os
32
32
from warnings import warn
33
33
from inspect import getdoc
 
34
import errno
34
35
 
35
36
import bzrlib
36
37
import bzrlib.trace
37
 
from bzrlib.trace import mutter, note, log_error, warning
38
 
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError, NotBranchError
 
38
from bzrlib.trace import mutter, note, log_error, warning, be_quiet
 
39
from bzrlib.errors import (BzrError, 
 
40
                           BzrCheckError,
 
41
                           BzrCommandError,
 
42
                           BzrOptionError,
 
43
                           NotBranchError)
39
44
from bzrlib.revisionspec import RevisionSpec
40
45
from bzrlib import BZRDIR
 
46
from bzrlib.option import Option
41
47
 
42
48
plugin_cmds = {}
43
49
 
44
50
 
45
 
def register_command(cmd):
 
51
def register_command(cmd, decorate=False):
46
52
    "Utility function to help register a command"
47
53
    global plugin_cmds
48
54
    k = cmd.__name__
53
59
    if not plugin_cmds.has_key(k_unsquished):
54
60
        plugin_cmds[k_unsquished] = cmd
55
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
56
68
    else:
57
69
        log_error('Two plugins defined the same command: %r' % k)
58
70
        log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
67
79
    return cmd[4:].replace('_','-')
68
80
 
69
81
 
70
 
def _parse_revision_str(revstr):
71
 
    """This handles a revision string -> revno.
72
 
 
73
 
    This always returns a list.  The list will have one element for
74
 
    each revision.
75
 
 
76
 
    >>> _parse_revision_str('234')
77
 
    [<RevisionSpec_int 234>]
78
 
    >>> _parse_revision_str('234..567')
79
 
    [<RevisionSpec_int 234>, <RevisionSpec_int 567>]
80
 
    >>> _parse_revision_str('..')
81
 
    [<RevisionSpec None>, <RevisionSpec None>]
82
 
    >>> _parse_revision_str('..234')
83
 
    [<RevisionSpec None>, <RevisionSpec_int 234>]
84
 
    >>> _parse_revision_str('234..')
85
 
    [<RevisionSpec_int 234>, <RevisionSpec None>]
86
 
    >>> _parse_revision_str('234..456..789') # Maybe this should be an error
87
 
    [<RevisionSpec_int 234>, <RevisionSpec_int 456>, <RevisionSpec_int 789>]
88
 
    >>> _parse_revision_str('234....789') # Error?
89
 
    [<RevisionSpec_int 234>, <RevisionSpec None>, <RevisionSpec_int 789>]
90
 
    >>> _parse_revision_str('revid:test@other.com-234234')
91
 
    [<RevisionSpec_revid revid:test@other.com-234234>]
92
 
    >>> _parse_revision_str('revid:test@other.com-234234..revid:test@other.com-234235')
93
 
    [<RevisionSpec_revid revid:test@other.com-234234>, <RevisionSpec_revid revid:test@other.com-234235>]
94
 
    >>> _parse_revision_str('revid:test@other.com-234234..23')
95
 
    [<RevisionSpec_revid revid:test@other.com-234234>, <RevisionSpec_int 23>]
96
 
    >>> _parse_revision_str('date:2005-04-12')
97
 
    [<RevisionSpec_date date:2005-04-12>]
98
 
    >>> _parse_revision_str('date:2005-04-12 12:24:33')
99
 
    [<RevisionSpec_date date:2005-04-12 12:24:33>]
100
 
    >>> _parse_revision_str('date:2005-04-12T12:24:33')
101
 
    [<RevisionSpec_date date:2005-04-12T12:24:33>]
102
 
    >>> _parse_revision_str('date:2005-04-12,12:24:33')
103
 
    [<RevisionSpec_date date:2005-04-12,12:24:33>]
104
 
    >>> _parse_revision_str('-5..23')
105
 
    [<RevisionSpec_int -5>, <RevisionSpec_int 23>]
106
 
    >>> _parse_revision_str('-5')
107
 
    [<RevisionSpec_int -5>]
108
 
    >>> _parse_revision_str('123a')
109
 
    Traceback (most recent call last):
110
 
      ...
111
 
    BzrError: No namespace registered for string: '123a'
112
 
    >>> _parse_revision_str('abc')
113
 
    Traceback (most recent call last):
114
 
      ...
115
 
    BzrError: No namespace registered for string: 'abc'
116
 
    """
117
 
    import re
118
 
    old_format_re = re.compile('\d*:\d*')
119
 
    m = old_format_re.match(revstr)
120
 
    revs = []
121
 
    if m:
122
 
        warning('Colon separator for revision numbers is deprecated.'
123
 
                ' Use .. instead')
124
 
        for rev in revstr.split(':'):
125
 
            if rev:
126
 
                revs.append(RevisionSpec(int(rev)))
127
 
            else:
128
 
                revs.append(RevisionSpec(None))
129
 
    else:
130
 
        for x in revstr.split('..'):
131
 
            if not x:
132
 
                revs.append(RevisionSpec(None))
133
 
            else:
134
 
                revs.append(RevisionSpec(x))
135
 
    return revs
136
 
 
137
 
 
138
82
def _builtin_commands():
139
83
    import bzrlib.builtins
140
84
    r = {}
224
168
        List of argument forms, marked with whether they are optional,
225
169
        repeated, etc.
226
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
 
227
179
    takes_options
228
 
        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().
229
183
 
230
184
    hidden
231
185
        If true, this command isn't advertised.  This is typically
232
186
        for commands intended for expert users.
233
187
    """
234
188
    aliases = []
235
 
    
236
189
    takes_args = []
237
190
    takes_options = []
238
191
 
243
196
        if self.__doc__ == Command.__doc__:
244
197
            warn("No help message set for %r" % self)
245
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
246
210
 
247
211
    def run_argv(self, argv):
248
212
        """Parse command line and run."""
249
 
        args, opts = parse_args(argv)
250
 
 
 
213
        args, opts = parse_args(self, argv)
251
214
        if 'help' in opts:  # e.g. bzr add --help
252
215
            from bzrlib.help import help_on_command
253
216
            help_on_command(self.name())
254
217
            return 0
255
 
 
256
 
        # check options are reasonable
257
 
        allowed = self.takes_options
 
218
        # XXX: This should be handled by the parser
 
219
        allowed_names = self.options().keys()
258
220
        for oname in opts:
259
 
            if oname not in allowed:
 
221
            if oname not in allowed_names:
260
222
                raise BzrCommandError("option '--%s' is not allowed for command %r"
261
223
                                      % (oname, self.name()))
262
 
 
263
224
        # mix arguments and options into one dictionary
264
225
        cmdargs = _match_argform(self.name(), self.takes_args, args)
265
226
        cmdopts = {}
270
231
        all_cmd_args.update(cmdopts)
271
232
 
272
233
        return self.run(**all_cmd_args)
273
 
 
274
234
    
275
235
    def run(self):
276
236
        """Actually run the command.
326
286
        parsed = [spec, None]
327
287
    return parsed
328
288
 
329
 
 
330
 
# list of all available options; the rhs can be either None for an
331
 
# option that takes no argument, or a constructor function that checks
332
 
# the type.
333
 
OPTIONS = {
334
 
    'all':                    None,
335
 
    'basis':                  str,
336
 
    'diff-options':           str,
337
 
    'help':                   None,
338
 
    'file':                   unicode,
339
 
    'force':                  None,
340
 
    'format':                 unicode,
341
 
    'forward':                None,
342
 
    'message':                unicode,
343
 
    'no-recurse':             None,
344
 
    'profile':                None,
345
 
    'revision':               _parse_revision_str,
346
 
    'short':                  None,
347
 
    'show-ids':               None,
348
 
    'timezone':               str,
349
 
    'verbose':                None,
350
 
    'version':                None,
351
 
    'email':                  None,
352
 
    'unchanged':              None,
353
 
    'update':                 None,
354
 
    'long':                   None,
355
 
    'root':                   str,
356
 
    'no-backup':              None,
357
 
    'pattern':                str,
358
 
    }
359
 
 
360
 
SHORT_OPTIONS = {
361
 
    'F':                      'file', 
362
 
    'h':                      'help',
363
 
    'm':                      'message',
364
 
    'r':                      'revision',
365
 
    'v':                      'verbose',
366
 
    'l':                      'long',
367
 
}
368
 
 
369
 
 
370
 
def parse_args(argv):
 
289
def parse_args(command, argv):
371
290
    """Parse command line.
372
291
    
373
292
    Arguments and options are parsed at this level before being passed
374
293
    down to specific command handlers.  This routine knows, from a
375
294
    lookup table, something about the available options, what optargs
376
295
    they take, and which commands will accept them.
377
 
 
378
 
    >>> parse_args('--help'.split())
379
 
    ([], {'help': True})
380
 
    >>> parse_args('help -- --invalidcmd'.split())
381
 
    (['help', '--invalidcmd'], {})
382
 
    >>> parse_args('--version'.split())
383
 
    ([], {'version': True})
384
 
    >>> parse_args('status --all'.split())
385
 
    (['status'], {'all': True})
386
 
    >>> parse_args('commit --message=biter'.split())
387
 
    (['commit'], {'message': u'biter'})
388
 
    >>> parse_args('log -r 500'.split())
389
 
    (['log'], {'revision': [<RevisionSpec_int 500>]})
390
 
    >>> parse_args('log -r500..600'.split())
391
 
    (['log'], {'revision': [<RevisionSpec_int 500>, <RevisionSpec_int 600>]})
392
 
    >>> parse_args('log -vr500..600'.split())
393
 
    (['log'], {'verbose': True, 'revision': [<RevisionSpec_int 500>, <RevisionSpec_int 600>]})
394
 
    >>> parse_args('log -rrevno:500..600'.split()) #the r takes an argument
395
 
    (['log'], {'revision': [<RevisionSpec_revno revno:500>, <RevisionSpec_int 600>]})
396
296
    """
 
297
    # TODO: chop up this beast; make it a method of the Command
397
298
    args = []
398
299
    opts = {}
399
300
 
 
301
    cmd_options = command.options()
400
302
    argsover = False
401
303
    while argv:
402
304
        a = argv.pop(0)
403
 
        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] == '-':
404
313
            # option names must not be unicode
405
314
            a = str(a)
406
315
            optarg = None
407
316
            if a[1] == '-':
408
 
                if a == '--':
409
 
                    # We've received a standalone -- No more flags
410
 
                    argsover = True
411
 
                    continue
412
 
                mutter("  got option %r" % a)
 
317
                mutter("  got option %r", a)
413
318
                if '=' in a:
414
319
                    optname, optarg = a[2:].split('=', 1)
415
320
                else:
416
321
                    optname = a[2:]
417
 
                if optname not in OPTIONS:
418
 
                    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()))
419
325
            else:
420
326
                shortopt = a[1:]
421
 
                if shortopt in SHORT_OPTIONS:
 
327
                if shortopt in Option.SHORT_OPTIONS:
422
328
                    # Multi-character options must have a space to delimit
423
329
                    # their value
424
 
                    optname = SHORT_OPTIONS[shortopt]
 
330
                    # ^^^ what does this mean? mbp 20051014
 
331
                    optname = Option.SHORT_OPTIONS[shortopt].name
425
332
                else:
426
333
                    # Single character short options, can be chained,
427
334
                    # and have their value appended to their name
428
335
                    shortopt = a[1:2]
429
 
                    if shortopt not in SHORT_OPTIONS:
 
336
                    if shortopt not in Option.SHORT_OPTIONS:
430
337
                        # We didn't find the multi-character name, and we
431
338
                        # didn't find the single char name
432
339
                        raise BzrError('unknown short option %r' % a)
433
 
                    optname = SHORT_OPTIONS[shortopt]
 
340
                    optname = Option.SHORT_OPTIONS[shortopt].name
434
341
 
435
342
                    if a[2:]:
436
343
                        # There are extra things on this option
437
344
                        # see if it is the value, or if it is another
438
345
                        # short option
439
 
                        optargfn = OPTIONS[optname]
 
346
                        optargfn = Option.OPTIONS[optname].type
440
347
                        if optargfn is None:
441
348
                            # This option does not take an argument, so the
442
349
                            # next entry is another short option, pack it back
447
354
                            # into the array
448
355
                            optarg = a[2:]
449
356
            
 
357
                if optname not in cmd_options:
 
358
                    raise BzrOptionError('unknown short option %r for command'
 
359
                        ' %s' % (shortopt, command.name()))
450
360
            if optname in opts:
451
361
                # XXX: Do we ever want to support this, e.g. for -r?
452
362
                raise BzrError('repeated option %r' % a)
453
363
                
454
 
            optargfn = OPTIONS[optname]
 
364
            option_obj = cmd_options[optname]
 
365
            optargfn = option_obj.type
455
366
            if optargfn:
456
367
                if optarg == None:
457
368
                    if not argv:
465
376
                opts[optname] = True
466
377
        else:
467
378
            args.append(a)
468
 
 
469
379
    return args, opts
470
380
 
471
381
 
472
 
 
473
 
 
474
382
def _match_argform(cmd, takes_args, args):
475
383
    argdict = {}
476
384
 
539
447
        os.remove(pfname)
540
448
 
541
449
 
 
450
def apply_lsprofiled(the_callable, *args, **kwargs):
 
451
    from bzrlib.lsprof import profile
 
452
    ret,stats = profile(the_callable,*args,**kwargs)
 
453
    stats.sort()
 
454
    stats.pprint()
 
455
    return ret
 
456
 
542
457
def run_bzr(argv):
543
458
    """Execute a command.
544
459
 
561
476
        other behaviour.)
562
477
 
563
478
    --profile
564
 
        Run under the Python profiler.
 
479
        Run under the Python hotshot profiler.
 
480
 
 
481
    --lsprof
 
482
        Run under the Python lsprof profiler.
565
483
    """
566
 
    # Load all of the transport methods
567
 
    import bzrlib.transport.local, bzrlib.transport.http
568
 
    
569
484
    argv = [a.decode(bzrlib.user_encoding) for a in argv]
570
485
 
571
 
    opt_profile = opt_no_plugins = opt_builtin = False
 
486
    opt_lsprof = opt_profile = opt_no_plugins = opt_builtin = False
572
487
 
573
488
    # --no-plugins is handled specially at a very early stage. We need
574
489
    # to load plugins before doing other command parsing so that they
577
492
    for a in argv:
578
493
        if a == '--profile':
579
494
            opt_profile = True
 
495
        elif a == '--lsprof':
 
496
            opt_lsprof = True
580
497
        elif a == '--no-plugins':
581
498
            opt_no_plugins = True
582
499
        elif a == '--builtin':
583
500
            opt_builtin = True
 
501
        elif a in ('--quiet', '-q'):
 
502
            be_quiet()
584
503
        else:
585
 
            break
 
504
            continue
586
505
        argv.remove(a)
587
506
 
588
507
    if (not argv) or (argv[0] == '--help'):
601
520
    if not opt_no_plugins:
602
521
        from bzrlib.plugin import load_plugins
603
522
        load_plugins()
 
523
    else:
 
524
        from bzrlib.plugin import disable_plugins
 
525
        disable_plugins()
604
526
 
605
527
    cmd = str(argv.pop(0))
606
528
 
607
529
    cmd_obj = get_cmd_object(cmd, plugins_override=not opt_builtin)
608
530
 
609
 
    if opt_profile:
610
 
        ret = apply_profiled(cmd_obj.run_argv, argv)
611
 
    else:
612
 
        ret = cmd_obj.run_argv(argv)
613
 
    return ret or 0
 
531
    try:
 
532
        if opt_lsprof:
 
533
            ret = apply_lsprofiled(cmd_obj.run_argv, argv)
 
534
        elif opt_profile:
 
535
            ret = apply_profiled(cmd_obj.run_argv, argv)
 
536
        else:
 
537
            ret = cmd_obj.run_argv(argv)
 
538
        return ret or 0
 
539
    finally:
 
540
        # reset, in case we may do other commands later within the same process
 
541
        be_quiet(False)
 
542
 
 
543
def display_command(func):
 
544
    """Decorator that suppresses pipe/interrupt errors."""
 
545
    def ignore_pipe(*args, **kwargs):
 
546
        try:
 
547
            result = func(*args, **kwargs)
 
548
            sys.stdout.flush()
 
549
            return result
 
550
        except IOError, e:
 
551
            if not hasattr(e, 'errno'):
 
552
                raise
 
553
            if e.errno != errno.EPIPE:
 
554
                raise
 
555
            pass
 
556
        except KeyboardInterrupt:
 
557
            pass
 
558
    return ignore_pipe
614
559
 
615
560
 
616
561
def main(argv):
617
562
    import bzrlib.ui
 
563
    from bzrlib.ui.text import TextUIFactory
 
564
    ## bzrlib.trace.enable_default_logging()
618
565
    bzrlib.trace.log_startup(argv)
619
 
    bzrlib.ui.ui_factory = bzrlib.ui.TextUIFactory()
620
 
 
621
 
    return run_bzr_catch_errors(argv[1:])
 
566
    bzrlib.ui.ui_factory = TextUIFactory()
 
567
    ret = run_bzr_catch_errors(argv[1:])
 
568
    mutter("return code %d", ret)
 
569
    return ret
622
570
 
623
571
 
624
572
def run_bzr_catch_errors(argv):
625
573
    try:
626
574
        try:
627
 
            try:
628
 
                return run_bzr(argv)
629
 
            finally:
630
 
                # do this here inside the exception wrappers to catch EPIPE
631
 
                sys.stdout.flush()
632
 
        #wrap common errors as CommandErrors.
633
 
        except (NotBranchError,), e:
634
 
            raise BzrCommandError(str(e))
635
 
    except BzrCommandError, e:
636
 
        # command line syntax error, etc
637
 
        log_error(str(e))
638
 
        return 1
639
 
    except BzrError, e:
640
 
        bzrlib.trace.log_exception()
641
 
        return 1
642
 
    except AssertionError, e:
643
 
        bzrlib.trace.log_exception('assertion failed: ' + str(e))
644
 
        return 3
645
 
    except KeyboardInterrupt, e:
646
 
        bzrlib.trace.log_exception('interrupted')
647
 
        return 2
 
575
            return run_bzr(argv)
 
576
        finally:
 
577
            # do this here inside the exception wrappers to catch EPIPE
 
578
            sys.stdout.flush()
648
579
    except Exception, e:
 
580
        # used to handle AssertionError and KeyboardInterrupt
 
581
        # specially here, but hopefully they're handled ok by the logger now
649
582
        import errno
650
583
        if (isinstance(e, IOError) 
651
584
            and hasattr(e, 'errno')
652
585
            and e.errno == errno.EPIPE):
653
586
            bzrlib.trace.note('broken pipe')
654
 
            return 2
 
587
            return 3
655
588
        else:
656
589
            bzrlib.trace.log_exception()
657
 
            return 2
 
590
            if os.environ.get('BZR_PDB'):
 
591
                print '**** entering debugger'
 
592
                import pdb
 
593
                pdb.post_mortem(sys.exc_traceback)
 
594
            return 3
658
595
 
659
596
if __name__ == '__main__':
660
597
    sys.exit(main(sys.argv))