~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Aaron Bentley
  • Date: 2005-10-17 20:17:55 UTC
  • mfrom: (1185.16.59)
  • mto: (1185.25.1)
  • mto: This revision was merged to the branch mainline in revision 1474.
  • Revision ID: abentley@panoramicfeedback.com-20051017201755-48ed4650792388ab
Merged latest from Martin

Show diffs side-by-side

added added

removed removed

Lines of Context:
38
38
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError, NotBranchError
39
39
from bzrlib.revisionspec import RevisionSpec
40
40
from bzrlib import BZRDIR
 
41
from bzrlib.option import Option
41
42
 
42
43
plugin_cmds = {}
43
44
 
67
68
    return cmd[4:].replace('_','-')
68
69
 
69
70
 
70
 
def _parse_revision_str(revstr):
71
 
    """This handles a revision string -> revno.
72
 
 
73
 
    This always returns a list.  The list will have one element for
74
 
    each revision specifier supplied.
75
 
 
76
 
    >>> _parse_revision_str('234')
77
 
    [<RevisionSpec_int 234>]
78
 
    >>> _parse_revision_str('234..567')
79
 
    [<RevisionSpec_int 234>, <RevisionSpec_int 567>]
80
 
    >>> _parse_revision_str('..')
81
 
    [<RevisionSpec None>, <RevisionSpec None>]
82
 
    >>> _parse_revision_str('..234')
83
 
    [<RevisionSpec None>, <RevisionSpec_int 234>]
84
 
    >>> _parse_revision_str('234..')
85
 
    [<RevisionSpec_int 234>, <RevisionSpec None>]
86
 
    >>> _parse_revision_str('234..456..789') # Maybe this should be an error
87
 
    [<RevisionSpec_int 234>, <RevisionSpec_int 456>, <RevisionSpec_int 789>]
88
 
    >>> _parse_revision_str('234....789') #Error ?
89
 
    [<RevisionSpec_int 234>, <RevisionSpec None>, <RevisionSpec_int 789>]
90
 
    >>> _parse_revision_str('revid:test@other.com-234234')
91
 
    [<RevisionSpec_revid revid:test@other.com-234234>]
92
 
    >>> _parse_revision_str('revid:test@other.com-234234..revid:test@other.com-234235')
93
 
    [<RevisionSpec_revid revid:test@other.com-234234>, <RevisionSpec_revid revid:test@other.com-234235>]
94
 
    >>> _parse_revision_str('revid:test@other.com-234234..23')
95
 
    [<RevisionSpec_revid revid:test@other.com-234234>, <RevisionSpec_int 23>]
96
 
    >>> _parse_revision_str('date:2005-04-12')
97
 
    [<RevisionSpec_date date:2005-04-12>]
98
 
    >>> _parse_revision_str('date:2005-04-12 12:24:33')
99
 
    [<RevisionSpec_date date:2005-04-12 12:24:33>]
100
 
    >>> _parse_revision_str('date:2005-04-12T12:24:33')
101
 
    [<RevisionSpec_date date:2005-04-12T12:24:33>]
102
 
    >>> _parse_revision_str('date:2005-04-12,12:24:33')
103
 
    [<RevisionSpec_date date:2005-04-12,12:24:33>]
104
 
    >>> _parse_revision_str('-5..23')
105
 
    [<RevisionSpec_int -5>, <RevisionSpec_int 23>]
106
 
    >>> _parse_revision_str('-5')
107
 
    [<RevisionSpec_int -5>]
108
 
    >>> _parse_revision_str('123a')
109
 
    Traceback (most recent call last):
110
 
      ...
111
 
    BzrError: No namespace registered for string: '123a'
112
 
    >>> _parse_revision_str('abc')
113
 
    Traceback (most recent call last):
114
 
      ...
115
 
    BzrError: No namespace registered for string: 'abc'
116
 
    >>> _parse_revision_str('branch:../branch2')
117
 
    [<RevisionSpec_branch branch:../branch2>]
118
 
    """
119
 
    import re
120
 
    old_format_re = re.compile('\d*:\d*')
121
 
    m = old_format_re.match(revstr)
122
 
    revs = []
123
 
    if m:
124
 
        warning('Colon separator for revision numbers is deprecated.'
125
 
                ' Use .. instead')
126
 
        for rev in revstr.split(':'):
127
 
            if rev:
128
 
                revs.append(RevisionSpec(int(rev)))
129
 
            else:
130
 
                revs.append(RevisionSpec(None))
131
 
    else:
132
 
        next_prefix = None
133
 
        for x in revstr.split('..'):
134
 
            if not x:
135
 
                revs.append(RevisionSpec(None))
136
 
            elif x[-1] == ':':
137
 
                # looks like a namespace:.. has happened
138
 
                next_prefix = x + '..'
139
 
            else:
140
 
                if next_prefix is not None:
141
 
                    x = next_prefix + x
142
 
                revs.append(RevisionSpec(x))
143
 
                next_prefix = None
144
 
        if next_prefix is not None:
145
 
            revs.append(RevisionSpec(next_prefix))
146
 
    return revs
147
 
 
148
 
 
149
71
def _builtin_commands():
150
72
    import bzrlib.builtins
151
73
    r = {}
236
158
        repeated, etc.
237
159
 
238
160
    takes_options
239
 
        List of options that may be given for this command.
 
161
        List of options that may be given for this command.  These can
 
162
        be either strings, referring to globally-defined options,
 
163
        or option objects.  Retrieve through options().
240
164
 
241
165
    hidden
242
166
        If true, this command isn't advertised.  This is typically
243
167
        for commands intended for expert users.
244
168
    """
245
169
    aliases = []
246
 
    
247
170
    takes_args = []
248
171
    takes_options = []
249
172
 
254
177
        if self.__doc__ == Command.__doc__:
255
178
            warn("No help message set for %r" % self)
256
179
 
 
180
    def options(self):
 
181
        """Return dict of valid options for this command.
 
182
 
 
183
        Maps from long option name to option object."""
 
184
        r = dict()
 
185
        r['help'] = Option.OPTIONS['help']
 
186
        for o in self.takes_options:
 
187
            if not isinstance(o, Option):
 
188
                o = Option.OPTIONS[o]
 
189
            r[o.name] = o
 
190
        return r
257
191
 
258
192
    def run_argv(self, argv):
259
193
        """Parse command line and run."""
260
 
        args, opts = parse_args(argv)
261
 
 
 
194
        args, opts = parse_args(self, argv)
262
195
        if 'help' in opts:  # e.g. bzr add --help
263
196
            from bzrlib.help import help_on_command
264
197
            help_on_command(self.name())
265
198
            return 0
266
 
 
267
 
        # check options are reasonable
268
 
        allowed = self.takes_options
 
199
        # XXX: This should be handled by the parser
 
200
        allowed_names = self.options().keys()
269
201
        for oname in opts:
270
 
            if oname not in allowed:
 
202
            if oname not in allowed_names:
271
203
                raise BzrCommandError("option '--%s' is not allowed for command %r"
272
204
                                      % (oname, self.name()))
273
 
 
274
205
        # mix arguments and options into one dictionary
275
206
        cmdargs = _match_argform(self.name(), self.takes_args, args)
276
207
        cmdopts = {}
337
268
        parsed = [spec, None]
338
269
    return parsed
339
270
 
340
 
 
341
 
# list of all available options; the rhs can be either None for an
342
 
# option that takes no argument, or a constructor function that checks
343
 
# the type.
344
 
OPTIONS = {
345
 
    'all':                    None,
346
 
    'basis':                  str,
347
 
    'diff-options':           str,
348
 
    'help':                   None,
349
 
    'file':                   unicode,
350
 
    'force':                  None,
351
 
    'format':                 unicode,
352
 
    'forward':                None,
353
 
    'quiet':                  None,
354
 
    'message':                unicode,
355
 
    'no-recurse':             None,
356
 
    'line':                   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
 
    'remember':               None,
372
 
    }
373
 
 
374
 
SHORT_OPTIONS = {
375
 
    'F':                      'file', 
376
 
    'h':                      'help',
377
 
    'm':                      'message',
378
 
    'r':                      'revision',
379
 
    'v':                      'verbose',
380
 
    'l':                      'long',
381
 
    'q':                      'quiet',
382
 
}
383
 
 
384
 
 
385
 
def parse_args(argv):
 
271
def parse_args(command, argv):
386
272
    """Parse command line.
387
273
    
388
274
    Arguments and options are parsed at this level before being passed
389
275
    down to specific command handlers.  This routine knows, from a
390
276
    lookup table, something about the available options, what optargs
391
277
    they take, and which commands will accept them.
392
 
 
393
 
    >>> parse_args('--help'.split())
394
 
    ([], {'help': True})
395
 
    >>> parse_args('help -- --invalidcmd'.split())
396
 
    (['help', '--invalidcmd'], {})
397
 
    >>> parse_args('--version'.split())
398
 
    ([], {'version': True})
399
 
    >>> parse_args('status --all'.split())
400
 
    (['status'], {'all': True})
401
 
    >>> parse_args('commit --message=biter'.split())
402
 
    (['commit'], {'message': u'biter'})
403
 
    >>> parse_args('log -r 500'.split())
404
 
    (['log'], {'revision': [<RevisionSpec_int 500>]})
405
 
    >>> parse_args('log -r500..600'.split())
406
 
    (['log'], {'revision': [<RevisionSpec_int 500>, <RevisionSpec_int 600>]})
407
 
    >>> parse_args('log -vr500..600'.split())
408
 
    (['log'], {'verbose': True, 'revision': [<RevisionSpec_int 500>, <RevisionSpec_int 600>]})
409
 
    >>> parse_args('log -rrevno:500..600'.split()) #the r takes an argument
410
 
    (['log'], {'revision': [<RevisionSpec_revno revno:500>, <RevisionSpec_int 600>]})
411
278
    """
 
279
    # TODO: chop up this beast; make it a method of the Command
412
280
    args = []
413
281
    opts = {}
414
282
 
 
283
    cmd_options = command.options()
415
284
    argsover = False
416
285
    while argv:
417
286
        a = argv.pop(0)
418
 
        if not argsover and a[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] == '-':
419
295
            # option names must not be unicode
420
296
            a = str(a)
421
297
            optarg = None
422
298
            if a[1] == '-':
423
 
                if a == '--':
424
 
                    # We've received a standalone -- No more flags
425
 
                    argsover = True
426
 
                    continue
427
299
                mutter("  got option %r" % a)
428
300
                if '=' in a:
429
301
                    optname, optarg = a[2:].split('=', 1)
430
302
                else:
431
303
                    optname = a[2:]
432
 
                if optname not in OPTIONS:
433
 
                    raise BzrError('unknown long option %r' % a)
 
304
                if optname not in cmd_options:
 
305
                    raise BzrCommandError('unknown long option %r for command %s' 
 
306
                            % (a, command.name))
434
307
            else:
435
308
                shortopt = a[1:]
436
 
                if shortopt in SHORT_OPTIONS:
 
309
                if shortopt in Option.SHORT_OPTIONS:
437
310
                    # Multi-character options must have a space to delimit
438
311
                    # their value
439
 
                    optname = SHORT_OPTIONS[shortopt]
 
312
                    # ^^^ what does this mean? mbp 20051014
 
313
                    optname = Option.SHORT_OPTIONS[shortopt].name
440
314
                else:
441
315
                    # Single character short options, can be chained,
442
316
                    # and have their value appended to their name
443
317
                    shortopt = a[1:2]
444
 
                    if shortopt not in SHORT_OPTIONS:
 
318
                    if shortopt not in Option.SHORT_OPTIONS:
445
319
                        # We didn't find the multi-character name, and we
446
320
                        # didn't find the single char name
447
321
                        raise BzrError('unknown short option %r' % a)
448
 
                    optname = SHORT_OPTIONS[shortopt]
 
322
                    optname = Option.SHORT_OPTIONS[shortopt].name
449
323
 
450
324
                    if a[2:]:
451
325
                        # There are extra things on this option
452
326
                        # see if it is the value, or if it is another
453
327
                        # short option
454
 
                        optargfn = OPTIONS[optname]
 
328
                        optargfn = Option.OPTIONS[optname].type
455
329
                        if optargfn is None:
456
330
                            # This option does not take an argument, so the
457
331
                            # next entry is another short option, pack it back
466
340
                # XXX: Do we ever want to support this, e.g. for -r?
467
341
                raise BzrError('repeated option %r' % a)
468
342
                
469
 
            optargfn = OPTIONS[optname]
 
343
            option_obj = cmd_options[optname]
 
344
            optargfn = option_obj.type
470
345
            if optargfn:
471
346
                if optarg == None:
472
347
                    if not argv:
480
355
                opts[optname] = True
481
356
        else:
482
357
            args.append(a)
483
 
 
484
358
    return args, opts
485
359
 
486
360
 
487
 
 
488
 
 
489
361
def _match_argform(cmd, takes_args, args):
490
362
    argdict = {}
491
363