/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

Merge from mbp.

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