~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Aaron Bentley
  • Date: 2005-10-20 02:57:55 UTC
  • mto: (1185.25.1)
  • mto: This revision was merged to the branch mainline in revision 1474.
  • Revision ID: aaron.bentley@utoronto.ca-20051020025755-e03df41c52aa3156
tweaked spacing

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
44
39
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError, NotBranchError
45
40
from bzrlib.revisionspec import RevisionSpec
46
41
from bzrlib import BZRDIR
 
42
from bzrlib.option import Option
47
43
 
48
44
plugin_cmds = {}
49
45
 
73
69
    return cmd[4:].replace('_','-')
74
70
 
75
71
 
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
72
def _builtin_commands():
159
73
    import bzrlib.builtins
160
74
    r = {}
245
159
        repeated, etc.
246
160
 
247
161
    takes_options
248
 
        List of options that may be given for this command.
 
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().
249
165
 
250
166
    hidden
251
167
        If true, this command isn't advertised.  This is typically
252
168
        for commands intended for expert users.
253
169
    """
254
170
    aliases = []
255
 
    
256
171
    takes_args = []
257
172
    takes_options = []
258
173
 
263
178
        if self.__doc__ == Command.__doc__:
264
179
            warn("No help message set for %r" % self)
265
180
 
 
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
266
192
 
267
193
    def run_argv(self, argv):
268
194
        """Parse command line and run."""
269
 
        args, opts = parse_args(argv)
270
 
 
 
195
        args, opts = parse_args(self, argv)
271
196
        if 'help' in opts:  # e.g. bzr add --help
272
197
            from bzrlib.help import help_on_command
273
198
            help_on_command(self.name())
274
199
            return 0
275
 
 
276
 
        # check options are reasonable
277
 
        allowed = self.takes_options
 
200
        # XXX: This should be handled by the parser
 
201
        allowed_names = self.options().keys()
278
202
        for oname in opts:
279
 
            if oname not in allowed:
 
203
            if oname not in allowed_names:
280
204
                raise BzrCommandError("option '--%s' is not allowed for command %r"
281
205
                                      % (oname, self.name()))
282
 
 
283
206
        # mix arguments and options into one dictionary
284
207
        cmdargs = _match_argform(self.name(), self.takes_args, args)
285
208
        cmdopts = {}
346
269
        parsed = [spec, None]
347
270
    return parsed
348
271
 
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):
 
272
def parse_args(command, argv):
391
273
    """Parse command line.
392
274
    
393
275
    Arguments and options are parsed at this level before being passed
394
276
    down to specific command handlers.  This routine knows, from a
395
277
    lookup table, something about the available options, what optargs
396
278
    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
279
    """
 
280
    # TODO: chop up this beast; make it a method of the Command
417
281
    args = []
418
282
    opts = {}
419
283
 
 
284
    cmd_options = command.options()
420
285
    argsover = False
421
286
    while argv:
422
287
        a = argv.pop(0)
423
 
        if not argsover and a[0] == '-':
 
288
        if argsover:
 
289
            args.append(a)
 
290
            continue
 
291
        elif a == '--':
 
292
            # We've received a standalone -- No more flags
 
293
            argsover = True
 
294
            continue
 
295
        if a[0] == '-':
424
296
            # option names must not be unicode
425
297
            a = str(a)
426
298
            optarg = None
427
299
            if a[1] == '-':
428
 
                if a == '--':
429
 
                    # We've received a standalone -- No more flags
430
 
                    argsover = True
431
 
                    continue
432
300
                mutter("  got option %r" % a)
433
301
                if '=' in a:
434
302
                    optname, optarg = a[2:].split('=', 1)
435
303
                else:
436
304
                    optname = a[2:]
437
 
                if optname not in OPTIONS:
438
 
                    raise BzrError('unknown long option %r' % a)
 
305
                if optname not in cmd_options:
 
306
                    raise BzrCommandError('unknown long option %r for command %s' 
 
307
                            % (a, command.name()))
439
308
            else:
440
309
                shortopt = a[1:]
441
 
                if shortopt in SHORT_OPTIONS:
 
310
                if shortopt in Option.SHORT_OPTIONS:
442
311
                    # Multi-character options must have a space to delimit
443
312
                    # their value
444
 
                    optname = SHORT_OPTIONS[shortopt]
 
313
                    # ^^^ what does this mean? mbp 20051014
 
314
                    optname = Option.SHORT_OPTIONS[shortopt].name
445
315
                else:
446
316
                    # Single character short options, can be chained,
447
317
                    # and have their value appended to their name
448
318
                    shortopt = a[1:2]
449
 
                    if shortopt not in SHORT_OPTIONS:
 
319
                    if shortopt not in Option.SHORT_OPTIONS:
450
320
                        # We didn't find the multi-character name, and we
451
321
                        # didn't find the single char name
452
322
                        raise BzrError('unknown short option %r' % a)
453
 
                    optname = SHORT_OPTIONS[shortopt]
 
323
                    optname = Option.SHORT_OPTIONS[shortopt].name
454
324
 
455
325
                    if a[2:]:
456
326
                        # There are extra things on this option
457
327
                        # see if it is the value, or if it is another
458
328
                        # short option
459
 
                        optargfn = OPTIONS[optname]
 
329
                        optargfn = Option.OPTIONS[optname].type
460
330
                        if optargfn is None:
461
331
                            # This option does not take an argument, so the
462
332
                            # next entry is another short option, pack it back
471
341
                # XXX: Do we ever want to support this, e.g. for -r?
472
342
                raise BzrError('repeated option %r' % a)
473
343
                
474
 
            optargfn = OPTIONS[optname]
 
344
            option_obj = cmd_options[optname]
 
345
            optargfn = option_obj.type
475
346
            if optargfn:
476
347
                if optarg == None:
477
348
                    if not argv:
485
356
                opts[optname] = True
486
357
        else:
487
358
            args.append(a)
488
 
 
489
359
    return args, opts
490
360
 
491
361
 
492
 
 
493
 
 
494
362
def _match_argform(cmd, takes_args, args):
495
363
    argdict = {}
496
364
 
539
407
def apply_profiled(the_callable, *args, **kwargs):
540
408
    import hotshot
541
409
    import tempfile
 
410
    import hotshot.stats
542
411
    pffileno, pfname = tempfile.mkstemp()
543
412
    try:
544
413
        prof = hotshot.Profile(pfname)
546
415
            ret = prof.runcall(the_callable, *args, **kwargs) or 0
547
416
        finally:
548
417
            prof.close()
549
 
 
550
 
        import hotshot.stats
551
418
        stats = hotshot.stats.load(pfname)
552
 
        #stats.strip_dirs()
553
 
        stats.sort_stats('time')
 
419
        stats.strip_dirs()
 
420
        stats.sort_stats('cum')   # 'time'
554
421
        ## XXX: Might like to write to stderr or the trace file instead but
555
422
        ## print_stats seems hardcoded to stdout
556
423
        stats.print_stats(20)
557
 
 
558
424
        return ret
559
425
    finally:
560
426
        os.close(pffileno)
585
451
    --profile
586
452
        Run under the Python profiler.
587
453
    """
588
 
    
589
454
    argv = [a.decode(bzrlib.user_encoding) for a in argv]
590
455
 
591
456
    opt_profile = opt_no_plugins = opt_builtin = False
632
497
        ret = cmd_obj.run_argv(argv)
633
498
    return ret or 0
634
499
 
 
500
def display_command(func):
 
501
    def ignore_pipe(*args, **kwargs):
 
502
        try:
 
503
            func(*args, **kwargs)
 
504
        except IOError, e:
 
505
            if e.errno != errno.EPIPE:
 
506
                raise
 
507
    return ignore_pipe
635
508
 
636
509
def main(argv):
637
510
    import bzrlib.ui
644
517
def run_bzr_catch_errors(argv):
645
518
    try:
646
519
        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))
 
520
            return run_bzr(argv)
 
521
        finally:
 
522
            # do this here inside the exception wrappers to catch EPIPE
 
523
            sys.stdout.flush()
655
524
    except BzrCommandError, e:
656
525
        # command line syntax error, etc
657
526
        log_error(str(e))
663
532
        bzrlib.trace.log_exception('assertion failed: ' + str(e))
664
533
        return 3
665
534
    except KeyboardInterrupt, e:
666
 
        bzrlib.trace.note('interrupted')
 
535
        bzrlib.trace.log_exception('interrupted')
667
536
        return 2
668
537
    except Exception, e:
669
538
        import errno
673
542
            bzrlib.trace.note('broken pipe')
674
543
            return 2
675
544
        else:
 
545
            ## import pdb
 
546
            ## pdb.pm()
676
547
            bzrlib.trace.log_exception()
677
548
            return 2
678
549
 
679
 
 
680
550
if __name__ == '__main__':
681
551
    sys.exit(main(sys.argv))