~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Aaron Bentley
  • Date: 2005-10-01 06:48:01 UTC
  • mto: (1185.12.13)
  • mto: This revision was merged to the branch mainline in revision 1419.
  • Revision ID: aaron.bentley@utoronto.ca-20051001064801-7400c2ed0fe26080
Made iter_conflicts a WorkingTree method

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
 
 
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
 
29
35
 
30
36
import sys
31
37
import os
32
38
from warnings import warn
33
39
from inspect import getdoc
34
 
import errno
35
40
 
36
41
import bzrlib
37
42
import bzrlib.trace
39
44
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError, NotBranchError
40
45
from bzrlib.revisionspec import RevisionSpec
41
46
from bzrlib import BZRDIR
42
 
from bzrlib.option import Option
43
47
 
44
48
plugin_cmds = {}
45
49
 
69
73
    return cmd[4:].replace('_','-')
70
74
 
71
75
 
 
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
 
72
144
def _builtin_commands():
73
145
    import bzrlib.builtins
74
146
    r = {}
159
231
        repeated, etc.
160
232
 
161
233
    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().
 
234
        List of options that may be given for this command.
165
235
 
166
236
    hidden
167
237
        If true, this command isn't advertised.  This is typically
168
238
        for commands intended for expert users.
169
239
    """
170
240
    aliases = []
 
241
    
171
242
    takes_args = []
172
243
    takes_options = []
173
244
 
178
249
        if self.__doc__ == Command.__doc__:
179
250
            warn("No help message set for %r" % self)
180
251
 
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
252
 
193
253
    def run_argv(self, argv):
194
254
        """Parse command line and run."""
195
 
        args, opts = parse_args(self, argv)
 
255
        args, opts = parse_args(argv)
 
256
 
196
257
        if 'help' in opts:  # e.g. bzr add --help
197
258
            from bzrlib.help import help_on_command
198
259
            help_on_command(self.name())
199
260
            return 0
200
 
        # XXX: This should be handled by the parser
201
 
        allowed_names = self.options().keys()
 
261
 
 
262
        # check options are reasonable
 
263
        allowed = self.takes_options
202
264
        for oname in opts:
203
 
            if oname not in allowed_names:
 
265
            if oname not in allowed:
204
266
                raise BzrCommandError("option '--%s' is not allowed for command %r"
205
267
                                      % (oname, self.name()))
 
268
 
206
269
        # mix arguments and options into one dictionary
207
270
        cmdargs = _match_argform(self.name(), self.takes_args, args)
208
271
        cmdopts = {}
213
276
        all_cmd_args.update(cmdopts)
214
277
 
215
278
        return self.run(**all_cmd_args)
 
279
 
216
280
    
217
281
    def run(self):
218
282
        """Actually run the command.
268
332
        parsed = [spec, None]
269
333
    return parsed
270
334
 
271
 
def parse_args(command, argv):
 
335
 
 
336
# list of all available options; the rhs can be either None for an
 
337
# option that takes no argument, or a constructor function that checks
 
338
# the type.
 
339
OPTIONS = {
 
340
    'all':                    None,
 
341
    'basis':                  str,
 
342
    'diff-options':           str,
 
343
    'help':                   None,
 
344
    'file':                   unicode,
 
345
    'force':                  None,
 
346
    'format':                 unicode,
 
347
    'forward':                None,
 
348
    'message':                unicode,
 
349
    'no-recurse':             None,
 
350
    'profile':                None,
 
351
    'revision':               _parse_revision_str,
 
352
    'short':                  None,
 
353
    'show-ids':               None,
 
354
    'timezone':               str,
 
355
    'verbose':                None,
 
356
    'version':                None,
 
357
    'email':                  None,
 
358
    'unchanged':              None,
 
359
    'update':                 None,
 
360
    'long':                   None,
 
361
    'root':                   str,
 
362
    'no-backup':              None,
 
363
    'pattern':                str,
 
364
    }
 
365
 
 
366
SHORT_OPTIONS = {
 
367
    'F':                      'file', 
 
368
    'h':                      'help',
 
369
    'm':                      'message',
 
370
    'r':                      'revision',
 
371
    'v':                      'verbose',
 
372
    'l':                      'long',
 
373
}
 
374
 
 
375
 
 
376
def parse_args(argv):
272
377
    """Parse command line.
273
378
    
274
379
    Arguments and options are parsed at this level before being passed
275
380
    down to specific command handlers.  This routine knows, from a
276
381
    lookup table, something about the available options, what optargs
277
382
    they take, and which commands will accept them.
 
383
 
 
384
    >>> parse_args('--help'.split())
 
385
    ([], {'help': True})
 
386
    >>> parse_args('help -- --invalidcmd'.split())
 
387
    (['help', '--invalidcmd'], {})
 
388
    >>> parse_args('--version'.split())
 
389
    ([], {'version': True})
 
390
    >>> parse_args('status --all'.split())
 
391
    (['status'], {'all': True})
 
392
    >>> parse_args('commit --message=biter'.split())
 
393
    (['commit'], {'message': u'biter'})
 
394
    >>> parse_args('log -r 500'.split())
 
395
    (['log'], {'revision': [<RevisionSpec_int 500>]})
 
396
    >>> parse_args('log -r500..600'.split())
 
397
    (['log'], {'revision': [<RevisionSpec_int 500>, <RevisionSpec_int 600>]})
 
398
    >>> parse_args('log -vr500..600'.split())
 
399
    (['log'], {'verbose': True, 'revision': [<RevisionSpec_int 500>, <RevisionSpec_int 600>]})
 
400
    >>> parse_args('log -rrevno:500..600'.split()) #the r takes an argument
 
401
    (['log'], {'revision': [<RevisionSpec_revno revno:500>, <RevisionSpec_int 600>]})
278
402
    """
279
 
    # TODO: chop up this beast; make it a method of the Command
280
403
    args = []
281
404
    opts = {}
282
405
 
283
 
    cmd_options = command.options()
284
406
    argsover = False
285
407
    while argv:
286
408
        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] == '-':
 
409
        if not argsover and a[0] == '-':
295
410
            # option names must not be unicode
296
411
            a = str(a)
297
412
            optarg = None
298
413
            if a[1] == '-':
 
414
                if a == '--':
 
415
                    # We've received a standalone -- No more flags
 
416
                    argsover = True
 
417
                    continue
299
418
                mutter("  got option %r" % a)
300
419
                if '=' in a:
301
420
                    optname, optarg = a[2:].split('=', 1)
302
421
                else:
303
422
                    optname = a[2:]
304
 
                if optname not in cmd_options:
305
 
                    raise BzrCommandError('unknown long option %r for command %s' 
306
 
                            % (a, command.name()))
 
423
                if optname not in OPTIONS:
 
424
                    raise BzrError('unknown long option %r' % a)
307
425
            else:
308
426
                shortopt = a[1:]
309
 
                if shortopt in Option.SHORT_OPTIONS:
 
427
                if shortopt in SHORT_OPTIONS:
310
428
                    # Multi-character options must have a space to delimit
311
429
                    # their value
312
 
                    # ^^^ what does this mean? mbp 20051014
313
 
                    optname = Option.SHORT_OPTIONS[shortopt].name
 
430
                    optname = SHORT_OPTIONS[shortopt]
314
431
                else:
315
432
                    # Single character short options, can be chained,
316
433
                    # and have their value appended to their name
317
434
                    shortopt = a[1:2]
318
 
                    if shortopt not in Option.SHORT_OPTIONS:
 
435
                    if shortopt not in SHORT_OPTIONS:
319
436
                        # We didn't find the multi-character name, and we
320
437
                        # didn't find the single char name
321
438
                        raise BzrError('unknown short option %r' % a)
322
 
                    optname = Option.SHORT_OPTIONS[shortopt].name
 
439
                    optname = SHORT_OPTIONS[shortopt]
323
440
 
324
441
                    if a[2:]:
325
442
                        # There are extra things on this option
326
443
                        # see if it is the value, or if it is another
327
444
                        # short option
328
 
                        optargfn = Option.OPTIONS[optname].type
 
445
                        optargfn = OPTIONS[optname]
329
446
                        if optargfn is None:
330
447
                            # This option does not take an argument, so the
331
448
                            # next entry is another short option, pack it back
340
457
                # XXX: Do we ever want to support this, e.g. for -r?
341
458
                raise BzrError('repeated option %r' % a)
342
459
                
343
 
            option_obj = cmd_options[optname]
344
 
            optargfn = option_obj.type
 
460
            optargfn = OPTIONS[optname]
345
461
            if optargfn:
346
462
                if optarg == None:
347
463
                    if not argv:
355
471
                opts[optname] = True
356
472
        else:
357
473
            args.append(a)
 
474
 
358
475
    return args, opts
359
476
 
360
477
 
 
478
 
 
479
 
361
480
def _match_argform(cmd, takes_args, args):
362
481
    argdict = {}
363
482
 
406
525
def apply_profiled(the_callable, *args, **kwargs):
407
526
    import hotshot
408
527
    import tempfile
409
 
    import hotshot.stats
410
528
    pffileno, pfname = tempfile.mkstemp()
411
529
    try:
412
530
        prof = hotshot.Profile(pfname)
414
532
            ret = prof.runcall(the_callable, *args, **kwargs) or 0
415
533
        finally:
416
534
            prof.close()
 
535
 
 
536
        import hotshot.stats
417
537
        stats = hotshot.stats.load(pfname)
418
 
        stats.strip_dirs()
419
 
        stats.sort_stats('cum')   # 'time'
 
538
        #stats.strip_dirs()
 
539
        stats.sort_stats('time')
420
540
        ## XXX: Might like to write to stderr or the trace file instead but
421
541
        ## print_stats seems hardcoded to stdout
422
542
        stats.print_stats(20)
 
543
 
423
544
        return ret
424
545
    finally:
425
546
        os.close(pffileno)
450
571
    --profile
451
572
        Run under the Python profiler.
452
573
    """
 
574
    # Load all of the transport methods
 
575
    import bzrlib.transport.local, bzrlib.transport.http
 
576
    
453
577
    argv = [a.decode(bzrlib.user_encoding) for a in argv]
454
578
 
455
579
    opt_profile = opt_no_plugins = opt_builtin = False
496
620
        ret = cmd_obj.run_argv(argv)
497
621
    return ret or 0
498
622
 
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
623
 
510
624
def main(argv):
511
625
    import bzrlib.ui
518
632
def run_bzr_catch_errors(argv):
519
633
    try:
520
634
        try:
521
 
            return run_bzr(argv)
522
 
        finally:
523
 
            # do this here inside the exception wrappers to catch EPIPE
524
 
            sys.stdout.flush()
 
635
            try:
 
636
                return run_bzr(argv)
 
637
            finally:
 
638
                # do this here inside the exception wrappers to catch EPIPE
 
639
                sys.stdout.flush()
 
640
        #wrap common errors as CommandErrors.
 
641
        except (NotBranchError,), e:
 
642
            raise BzrCommandError(str(e))
525
643
    except BzrCommandError, e:
526
644
        # command line syntax error, etc
527
645
        log_error(str(e))
543
661
            bzrlib.trace.note('broken pipe')
544
662
            return 2
545
663
        else:
546
 
            ## import pdb
547
 
            ## pdb.pm()
548
664
            bzrlib.trace.log_exception()
549
665
            return 2
550
666