~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

Exclude more files from dumb-rsync upload

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
 
 
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
 
 
 
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?
42
29
 
43
30
import sys
44
31
import os
45
32
from warnings import warn
46
33
from inspect import getdoc
 
34
import errno
47
35
 
48
36
import bzrlib
49
37
import bzrlib.trace
50
38
from bzrlib.trace import mutter, note, log_error, warning
51
 
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError, NotBranchError
 
39
from bzrlib.errors import (BzrError, 
 
40
                           BzrCheckError,
 
41
                           BzrCommandError,
 
42
                           BzrOptionError,
 
43
                           NotBranchError)
52
44
from bzrlib.revisionspec import RevisionSpec
53
45
from bzrlib import BZRDIR
 
46
from bzrlib.option import Option
54
47
 
55
48
plugin_cmds = {}
56
49
 
57
50
 
58
 
def register_command(cmd):
 
51
def register_command(cmd, decorate=False):
59
52
    "Utility function to help register a command"
60
53
    global plugin_cmds
61
54
    k = cmd.__name__
66
59
    if not plugin_cmds.has_key(k_unsquished):
67
60
        plugin_cmds[k_unsquished] = cmd
68
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
69
68
    else:
70
69
        log_error('Two plugins defined the same command: %r' % k)
71
70
        log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
80
79
    return cmd[4:].replace('_','-')
81
80
 
82
81
 
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
82
def _builtin_commands():
152
83
    import bzrlib.builtins
153
84
    r = {}
237
168
        List of argument forms, marked with whether they are optional,
238
169
        repeated, etc.
239
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
 
240
179
    takes_options
241
 
        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().
242
183
 
243
184
    hidden
244
185
        If true, this command isn't advertised.  This is typically
245
186
        for commands intended for expert users.
246
187
    """
247
188
    aliases = []
248
 
    
249
189
    takes_args = []
250
190
    takes_options = []
251
191
 
256
196
        if self.__doc__ == Command.__doc__:
257
197
            warn("No help message set for %r" % self)
258
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
259
210
 
260
211
    def run_argv(self, argv):
261
212
        """Parse command line and run."""
262
 
        args, opts = parse_args(argv)
263
 
 
 
213
        args, opts = parse_args(self, argv)
264
214
        if 'help' in opts:  # e.g. bzr add --help
265
215
            from bzrlib.help import help_on_command
266
216
            help_on_command(self.name())
267
217
            return 0
268
 
 
269
 
        # check options are reasonable
270
 
        allowed = self.takes_options
 
218
        # XXX: This should be handled by the parser
 
219
        allowed_names = self.options().keys()
271
220
        for oname in opts:
272
 
            if oname not in allowed:
 
221
            if oname not in allowed_names:
273
222
                raise BzrCommandError("option '--%s' is not allowed for command %r"
274
223
                                      % (oname, self.name()))
275
 
 
276
224
        # mix arguments and options into one dictionary
277
225
        cmdargs = _match_argform(self.name(), self.takes_args, args)
278
226
        cmdopts = {}
283
231
        all_cmd_args.update(cmdopts)
284
232
 
285
233
        return self.run(**all_cmd_args)
286
 
 
287
234
    
288
235
    def run(self):
289
236
        """Actually run the command.
339
286
        parsed = [spec, None]
340
287
    return parsed
341
288
 
342
 
 
343
 
# list of all available options; the rhs can be either None for an
344
 
# option that takes no argument, or a constructor function that checks
345
 
# the type.
346
 
OPTIONS = {
347
 
    'all':                    None,
348
 
    'basis':                  str,
349
 
    'diff-options':           str,
350
 
    'help':                   None,
351
 
    'file':                   unicode,
352
 
    'force':                  None,
353
 
    'format':                 unicode,
354
 
    'forward':                None,
355
 
    'message':                unicode,
356
 
    'no-recurse':             None,
357
 
    'profile':                None,
358
 
    'revision':               _parse_revision_str,
359
 
    'short':                  None,
360
 
    'show-ids':               None,
361
 
    'timezone':               str,
362
 
    'verbose':                None,
363
 
    'version':                None,
364
 
    'email':                  None,
365
 
    'unchanged':              None,
366
 
    'update':                 None,
367
 
    'long':                   None,
368
 
    'root':                   str,
369
 
    'no-backup':              None,
370
 
    'pattern':                str,
371
 
    }
372
 
 
373
 
SHORT_OPTIONS = {
374
 
    'F':                      'file', 
375
 
    'h':                      'help',
376
 
    'm':                      'message',
377
 
    'r':                      'revision',
378
 
    'v':                      'verbose',
379
 
    'l':                      'long',
380
 
}
381
 
 
382
 
 
383
 
def parse_args(argv):
 
289
def parse_args(command, argv):
384
290
    """Parse command line.
385
291
    
386
292
    Arguments and options are parsed at this level before being passed
387
293
    down to specific command handlers.  This routine knows, from a
388
294
    lookup table, something about the available options, what optargs
389
295
    they take, and which commands will accept them.
390
 
 
391
 
    >>> parse_args('--help'.split())
392
 
    ([], {'help': True})
393
 
    >>> parse_args('help -- --invalidcmd'.split())
394
 
    (['help', '--invalidcmd'], {})
395
 
    >>> parse_args('--version'.split())
396
 
    ([], {'version': True})
397
 
    >>> parse_args('status --all'.split())
398
 
    (['status'], {'all': True})
399
 
    >>> parse_args('commit --message=biter'.split())
400
 
    (['commit'], {'message': u'biter'})
401
 
    >>> parse_args('log -r 500'.split())
402
 
    (['log'], {'revision': [<RevisionSpec_int 500>]})
403
 
    >>> parse_args('log -r500..600'.split())
404
 
    (['log'], {'revision': [<RevisionSpec_int 500>, <RevisionSpec_int 600>]})
405
 
    >>> parse_args('log -vr500..600'.split())
406
 
    (['log'], {'verbose': True, 'revision': [<RevisionSpec_int 500>, <RevisionSpec_int 600>]})
407
 
    >>> parse_args('log -rrevno:500..600'.split()) #the r takes an argument
408
 
    (['log'], {'revision': [<RevisionSpec_revno revno:500>, <RevisionSpec_int 600>]})
409
296
    """
 
297
    # TODO: chop up this beast; make it a method of the Command
410
298
    args = []
411
299
    opts = {}
412
300
 
 
301
    cmd_options = command.options()
413
302
    argsover = False
414
303
    while argv:
415
304
        a = argv.pop(0)
416
 
        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] == '-':
417
313
            # option names must not be unicode
418
314
            a = str(a)
419
315
            optarg = None
420
316
            if a[1] == '-':
421
 
                if a == '--':
422
 
                    # We've received a standalone -- No more flags
423
 
                    argsover = True
424
 
                    continue
425
 
                mutter("  got option %r" % a)
 
317
                mutter("  got option %r", a)
426
318
                if '=' in a:
427
319
                    optname, optarg = a[2:].split('=', 1)
428
320
                else:
429
321
                    optname = a[2:]
430
 
                if optname not in OPTIONS:
431
 
                    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()))
432
325
            else:
433
326
                shortopt = a[1:]
434
 
                if shortopt in SHORT_OPTIONS:
 
327
                if shortopt in Option.SHORT_OPTIONS:
435
328
                    # Multi-character options must have a space to delimit
436
329
                    # their value
437
 
                    optname = SHORT_OPTIONS[shortopt]
 
330
                    # ^^^ what does this mean? mbp 20051014
 
331
                    optname = Option.SHORT_OPTIONS[shortopt].name
438
332
                else:
439
333
                    # Single character short options, can be chained,
440
334
                    # and have their value appended to their name
441
335
                    shortopt = a[1:2]
442
 
                    if shortopt not in SHORT_OPTIONS:
 
336
                    if shortopt not in Option.SHORT_OPTIONS:
443
337
                        # We didn't find the multi-character name, and we
444
338
                        # didn't find the single char name
445
339
                        raise BzrError('unknown short option %r' % a)
446
 
                    optname = SHORT_OPTIONS[shortopt]
 
340
                    optname = Option.SHORT_OPTIONS[shortopt].name
447
341
 
448
342
                    if a[2:]:
449
343
                        # There are extra things on this option
450
344
                        # see if it is the value, or if it is another
451
345
                        # short option
452
 
                        optargfn = OPTIONS[optname]
 
346
                        optargfn = Option.OPTIONS[optname].type
453
347
                        if optargfn is None:
454
348
                            # This option does not take an argument, so the
455
349
                            # next entry is another short option, pack it back
460
354
                            # into the array
461
355
                            optarg = a[2:]
462
356
            
 
357
                if optname not in cmd_options:
 
358
                    raise BzrOptionError('unknown short option %r for command'
 
359
                        ' %s' % (shortopt, command.name()))
463
360
            if optname in opts:
464
361
                # XXX: Do we ever want to support this, e.g. for -r?
465
362
                raise BzrError('repeated option %r' % a)
466
363
                
467
 
            optargfn = OPTIONS[optname]
 
364
            option_obj = cmd_options[optname]
 
365
            optargfn = option_obj.type
468
366
            if optargfn:
469
367
                if optarg == None:
470
368
                    if not argv:
478
376
                opts[optname] = True
479
377
        else:
480
378
            args.append(a)
481
 
 
482
379
    return args, opts
483
380
 
484
381
 
485
 
 
486
 
 
487
382
def _match_argform(cmd, takes_args, args):
488
383
    argdict = {}
489
384
 
532
427
def apply_profiled(the_callable, *args, **kwargs):
533
428
    import hotshot
534
429
    import tempfile
 
430
    import hotshot.stats
535
431
    pffileno, pfname = tempfile.mkstemp()
536
432
    try:
537
433
        prof = hotshot.Profile(pfname)
539
435
            ret = prof.runcall(the_callable, *args, **kwargs) or 0
540
436
        finally:
541
437
            prof.close()
542
 
 
543
 
        import hotshot.stats
544
438
        stats = hotshot.stats.load(pfname)
545
 
        #stats.strip_dirs()
546
 
        stats.sort_stats('time')
 
439
        stats.strip_dirs()
 
440
        stats.sort_stats('cum')   # 'time'
547
441
        ## XXX: Might like to write to stderr or the trace file instead but
548
442
        ## print_stats seems hardcoded to stdout
549
443
        stats.print_stats(20)
550
 
 
551
444
        return ret
552
445
    finally:
553
446
        os.close(pffileno)
578
471
    --profile
579
472
        Run under the Python profiler.
580
473
    """
581
 
    # Load all of the transport methods
582
 
    import bzrlib.transport.local, bzrlib.transport.http
583
 
    
584
474
    argv = [a.decode(bzrlib.user_encoding) for a in argv]
585
475
 
586
476
    opt_profile = opt_no_plugins = opt_builtin = False
627
517
        ret = cmd_obj.run_argv(argv)
628
518
    return ret or 0
629
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
630
536
 
631
537
def main(argv):
632
538
    import bzrlib.ui
639
545
def run_bzr_catch_errors(argv):
640
546
    try:
641
547
        try:
642
 
            try:
643
 
                return run_bzr(argv)
644
 
            finally:
645
 
                # do this here inside the exception wrappers to catch EPIPE
646
 
                sys.stdout.flush()
647
 
        #wrap common errors as CommandErrors.
648
 
        except (NotBranchError,), e:
649
 
            raise BzrCommandError(str(e))
650
 
    except BzrCommandError, e:
651
 
        # command line syntax error, etc
652
 
        log_error(str(e))
653
 
        return 1
654
 
    except BzrError, e:
655
 
        bzrlib.trace.log_exception()
656
 
        return 1
657
 
    except AssertionError, e:
658
 
        bzrlib.trace.log_exception('assertion failed: ' + str(e))
659
 
        return 3
660
 
    except KeyboardInterrupt, e:
661
 
        bzrlib.trace.log_exception('interrupted')
662
 
        return 2
 
548
            return run_bzr(argv)
 
549
        finally:
 
550
            # do this here inside the exception wrappers to catch EPIPE
 
551
            sys.stdout.flush()
663
552
    except Exception, e:
 
553
        # used to handle AssertionError and KeyboardInterrupt
 
554
        # specially here, but hopefully they're handled ok by the logger now
664
555
        import errno
665
556
        if (isinstance(e, IOError) 
666
557
            and hasattr(e, 'errno')
667
558
            and e.errno == errno.EPIPE):
668
559
            bzrlib.trace.note('broken pipe')
669
 
            return 2
 
560
            return 3
670
561
        else:
671
562
            bzrlib.trace.log_exception()
672
 
            return 2
 
563
            if os.environ.get('BZR_PDB'):
 
564
                print '**** entering debugger'
 
565
                import pdb
 
566
                pdb.post_mortem(sys.exc_traceback)
 
567
            return 3
673
568
 
674
569
if __name__ == '__main__':
675
570
    sys.exit(main(sys.argv))