~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

Nathaniel McCallums patch for urandom friendliness on aix.

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
 
# TODO: "--profile=cum", to change sort order.  Is there any value in leaving
28
 
# the profile output behind so it can be interactively examined?
 
27
 
29
28
 
30
29
import sys
31
30
import os
32
31
from warnings import warn
33
32
from inspect import getdoc
34
 
import errno
35
33
 
36
34
import bzrlib
37
35
import bzrlib.trace
38
36
from bzrlib.trace import mutter, note, log_error, warning
39
 
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError, NotBranchError
40
 
from bzrlib.revisionspec import RevisionSpec
 
37
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError
 
38
from bzrlib.branch import find_branch
41
39
from bzrlib import BZRDIR
42
 
from bzrlib.option import Option
43
40
 
44
41
plugin_cmds = {}
45
42
 
69
66
    return cmd[4:].replace('_','-')
70
67
 
71
68
 
 
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
def get_merge_type(typestring):
 
154
    """Attempt to find the merge class/factory associated with a string."""
 
155
    from merge import merge_types
 
156
    try:
 
157
        return merge_types[typestring][0]
 
158
    except KeyError:
 
159
        templ = '%s%%7s: %%s' % (' '*12)
 
160
        lines = [templ % (f[0], f[1][1]) for f in merge_types.iteritems()]
 
161
        type_list = '\n'.join(lines)
 
162
        msg = "No known merge type %s. Supported types are:\n%s" %\
 
163
            (typestring, type_list)
 
164
        raise BzrCommandError(msg)
 
165
 
 
166
 
72
167
def _builtin_commands():
73
168
    import bzrlib.builtins
74
169
    r = {}
159
254
        repeated, etc.
160
255
 
161
256
    takes_options
162
 
        List of options that may be given for this command.  These can
163
 
        be either strings, referring to globally-defined options,
164
 
        or option objects.  Retrieve through options().
 
257
        List of options that may be given for this command.
165
258
 
166
259
    hidden
167
260
        If true, this command isn't advertised.  This is typically
168
261
        for commands intended for expert users.
169
262
    """
170
263
    aliases = []
 
264
    
171
265
    takes_args = []
172
266
    takes_options = []
173
267
 
178
272
        if self.__doc__ == Command.__doc__:
179
273
            warn("No help message set for %r" % self)
180
274
 
181
 
    def options(self):
182
 
        """Return dict of valid options for this command.
183
 
 
184
 
        Maps from long option name to option object."""
185
 
        r = dict()
186
 
        r['help'] = Option.OPTIONS['help']
187
 
        for o in self.takes_options:
188
 
            if not isinstance(o, Option):
189
 
                o = Option.OPTIONS[o]
190
 
            r[o.name] = o
191
 
        return r
192
275
 
193
276
    def run_argv(self, argv):
194
277
        """Parse command line and run."""
195
 
        args, opts = parse_args(self, argv)
 
278
        args, opts = parse_args(argv)
 
279
 
196
280
        if 'help' in opts:  # e.g. bzr add --help
197
281
            from bzrlib.help import help_on_command
198
282
            help_on_command(self.name())
199
283
            return 0
200
 
        # XXX: This should be handled by the parser
201
 
        allowed_names = self.options().keys()
 
284
 
 
285
        # check options are reasonable
 
286
        allowed = self.takes_options
202
287
        for oname in opts:
203
 
            if oname not in allowed_names:
 
288
            if oname not in allowed:
204
289
                raise BzrCommandError("option '--%s' is not allowed for command %r"
205
290
                                      % (oname, self.name()))
 
291
 
206
292
        # mix arguments and options into one dictionary
207
293
        cmdargs = _match_argform(self.name(), self.takes_args, args)
208
294
        cmdopts = {}
213
299
        all_cmd_args.update(cmdopts)
214
300
 
215
301
        return self.run(**all_cmd_args)
 
302
 
216
303
    
217
304
    def run(self):
218
305
        """Actually run the command.
268
355
        parsed = [spec, None]
269
356
    return parsed
270
357
 
271
 
def parse_args(command, argv):
 
358
 
 
359
 
 
360
 
 
361
# list of all available options; the rhs can be either None for an
 
362
# option that takes no argument, or a constructor function that checks
 
363
# the type.
 
364
OPTIONS = {
 
365
    'all':                    None,
 
366
    'diff-options':           str,
 
367
    'help':                   None,
 
368
    'file':                   unicode,
 
369
    'force':                  None,
 
370
    'format':                 unicode,
 
371
    'forward':                None,
 
372
    'message':                unicode,
 
373
    'no-recurse':             None,
 
374
    'profile':                None,
 
375
    'revision':               _parse_revision_str,
 
376
    'short':                  None,
 
377
    'show-ids':               None,
 
378
    'timezone':               str,
 
379
    'verbose':                None,
 
380
    'version':                None,
 
381
    'email':                  None,
 
382
    'unchanged':              None,
 
383
    'update':                 None,
 
384
    'long':                   None,
 
385
    'root':                   str,
 
386
    'no-backup':              None,
 
387
    'merge-type':             get_merge_type,
 
388
    'pattern':                str,
 
389
    }
 
390
 
 
391
SHORT_OPTIONS = {
 
392
    'F':                      'file', 
 
393
    'h':                      'help',
 
394
    'm':                      'message',
 
395
    'r':                      'revision',
 
396
    'v':                      'verbose',
 
397
    'l':                      'long',
 
398
}
 
399
 
 
400
 
 
401
def parse_args(argv):
272
402
    """Parse command line.
273
403
    
274
404
    Arguments and options are parsed at this level before being passed
275
405
    down to specific command handlers.  This routine knows, from a
276
406
    lookup table, something about the available options, what optargs
277
407
    they take, and which commands will accept them.
 
408
 
 
409
    >>> parse_args('--help'.split())
 
410
    ([], {'help': True})
 
411
    >>> parse_args('help -- --invalidcmd'.split())
 
412
    (['help', '--invalidcmd'], {})
 
413
    >>> parse_args('--version'.split())
 
414
    ([], {'version': True})
 
415
    >>> parse_args('status --all'.split())
 
416
    (['status'], {'all': True})
 
417
    >>> parse_args('commit --message=biter'.split())
 
418
    (['commit'], {'message': u'biter'})
 
419
    >>> parse_args('log -r 500'.split())
 
420
    (['log'], {'revision': [500]})
 
421
    >>> parse_args('log -r500..600'.split())
 
422
    (['log'], {'revision': [500, 600]})
 
423
    >>> parse_args('log -vr500..600'.split())
 
424
    (['log'], {'verbose': True, 'revision': [500, 600]})
 
425
    >>> parse_args('log -rv500..600'.split()) #the r takes an argument
 
426
    (['log'], {'revision': ['v500', 600]})
278
427
    """
279
 
    # TODO: chop up this beast; make it a method of the Command
280
428
    args = []
281
429
    opts = {}
282
430
 
283
 
    cmd_options = command.options()
284
431
    argsover = False
285
432
    while argv:
286
433
        a = argv.pop(0)
287
 
        if argsover:
288
 
            args.append(a)
289
 
            continue
290
 
        elif a == '--':
291
 
            # We've received a standalone -- No more flags
292
 
            argsover = True
293
 
            continue
294
 
        if a[0] == '-':
 
434
        if not argsover and a[0] == '-':
295
435
            # option names must not be unicode
296
436
            a = str(a)
297
437
            optarg = None
298
438
            if a[1] == '-':
 
439
                if a == '--':
 
440
                    # We've received a standalone -- No more flags
 
441
                    argsover = True
 
442
                    continue
299
443
                mutter("  got option %r" % a)
300
444
                if '=' in a:
301
445
                    optname, optarg = a[2:].split('=', 1)
302
446
                else:
303
447
                    optname = a[2:]
304
 
                if optname not in cmd_options:
305
 
                    raise BzrCommandError('unknown long option %r for command %s' 
306
 
                            % (a, command.name()))
 
448
                if optname not in OPTIONS:
 
449
                    raise BzrError('unknown long option %r' % a)
307
450
            else:
308
451
                shortopt = a[1:]
309
 
                if shortopt in Option.SHORT_OPTIONS:
 
452
                if shortopt in SHORT_OPTIONS:
310
453
                    # Multi-character options must have a space to delimit
311
454
                    # their value
312
 
                    # ^^^ what does this mean? mbp 20051014
313
 
                    optname = Option.SHORT_OPTIONS[shortopt].name
 
455
                    optname = SHORT_OPTIONS[shortopt]
314
456
                else:
315
457
                    # Single character short options, can be chained,
316
458
                    # and have their value appended to their name
317
459
                    shortopt = a[1:2]
318
 
                    if shortopt not in Option.SHORT_OPTIONS:
 
460
                    if shortopt not in SHORT_OPTIONS:
319
461
                        # We didn't find the multi-character name, and we
320
462
                        # didn't find the single char name
321
463
                        raise BzrError('unknown short option %r' % a)
322
 
                    optname = Option.SHORT_OPTIONS[shortopt].name
 
464
                    optname = SHORT_OPTIONS[shortopt]
323
465
 
324
466
                    if a[2:]:
325
467
                        # There are extra things on this option
326
468
                        # see if it is the value, or if it is another
327
469
                        # short option
328
 
                        optargfn = Option.OPTIONS[optname].type
 
470
                        optargfn = OPTIONS[optname]
329
471
                        if optargfn is None:
330
472
                            # This option does not take an argument, so the
331
473
                            # next entry is another short option, pack it back
340
482
                # XXX: Do we ever want to support this, e.g. for -r?
341
483
                raise BzrError('repeated option %r' % a)
342
484
                
343
 
            option_obj = cmd_options[optname]
344
 
            optargfn = option_obj.type
 
485
            optargfn = OPTIONS[optname]
345
486
            if optargfn:
346
487
                if optarg == None:
347
488
                    if not argv:
355
496
                opts[optname] = True
356
497
        else:
357
498
            args.append(a)
 
499
 
358
500
    return args, opts
359
501
 
360
502
 
 
503
 
 
504
 
361
505
def _match_argform(cmd, takes_args, args):
362
506
    argdict = {}
363
507
 
406
550
def apply_profiled(the_callable, *args, **kwargs):
407
551
    import hotshot
408
552
    import tempfile
409
 
    import hotshot.stats
410
553
    pffileno, pfname = tempfile.mkstemp()
411
554
    try:
412
555
        prof = hotshot.Profile(pfname)
414
557
            ret = prof.runcall(the_callable, *args, **kwargs) or 0
415
558
        finally:
416
559
            prof.close()
 
560
 
 
561
        import hotshot.stats
417
562
        stats = hotshot.stats.load(pfname)
418
 
        stats.strip_dirs()
419
 
        stats.sort_stats('cum')   # 'time'
 
563
        #stats.strip_dirs()
 
564
        stats.sort_stats('time')
420
565
        ## XXX: Might like to write to stderr or the trace file instead but
421
566
        ## print_stats seems hardcoded to stdout
422
567
        stats.print_stats(20)
 
568
 
423
569
        return ret
424
570
    finally:
425
571
        os.close(pffileno)
450
596
    --profile
451
597
        Run under the Python profiler.
452
598
    """
 
599
    
453
600
    argv = [a.decode(bzrlib.user_encoding) for a in argv]
454
601
 
455
602
    opt_profile = opt_no_plugins = opt_builtin = False
496
643
        ret = cmd_obj.run_argv(argv)
497
644
    return ret or 0
498
645
 
499
 
def display_command(func):
500
 
    def ignore_pipe(*args, **kwargs):
501
 
        try:
502
 
            return func(*args, **kwargs)
503
 
        except IOError, e:
504
 
            if e.errno != errno.EPIPE:
505
 
                raise
506
 
        except KeyboardInterrupt:
507
 
            pass
508
 
    return ignore_pipe
509
646
 
510
647
def main(argv):
511
648
    import bzrlib.ui
512
649
    bzrlib.trace.log_startup(argv)
513
650
    bzrlib.ui.ui_factory = bzrlib.ui.TextUIFactory()
514
651
 
515
 
    return run_bzr_catch_errors(argv[1:])
516
 
 
517
 
 
518
 
def run_bzr_catch_errors(argv):
519
652
    try:
520
653
        try:
521
 
            return run_bzr(argv)
 
654
            return run_bzr(argv[1:])
522
655
        finally:
523
656
            # do this here inside the exception wrappers to catch EPIPE
524
657
            sys.stdout.flush()
533
666
        bzrlib.trace.log_exception('assertion failed: ' + str(e))
534
667
        return 3
535
668
    except KeyboardInterrupt, e:
536
 
        bzrlib.trace.log_exception('interrupted')
 
669
        bzrlib.trace.note('interrupted')
537
670
        return 2
538
671
    except Exception, e:
539
672
        import errno
543
676
            bzrlib.trace.note('broken pipe')
544
677
            return 2
545
678
        else:
546
 
            ## import pdb
547
 
            ## pdb.pm()
548
679
            bzrlib.trace.log_exception()
549
680
            return 2
550
681
 
 
682
 
551
683
if __name__ == '__main__':
552
684
    sys.exit(main(sys.argv))