1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
|
#!/usr/bin/env python
# Copyright (c) 2011 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Parse a command line, retrieving a command and its arguments.
Supports the concept of command line commands, each with its own set
of arguments. Supports dependent arguments and mutually exclusive arguments.
Basically, a better optparse. I took heed of epg's WHINE() in gvn.cmdline
and dumped optparse in favor of something better.
"""
import os.path
import re
import string
import sys
import textwrap
import types
def IsString(var):
"""Little helper function to see if a variable is a string."""
return type(var) in types.StringTypes
class ParseError(Exception):
"""Encapsulates errors from parsing, string arg is description."""
pass
class Command(object):
"""Implements a single command."""
def __init__(self, names, helptext, validator=None, impl=None):
"""Initializes Command from names and helptext, plus optional callables.
Args:
names: command name, or list of synonyms
helptext: brief string description of the command
validator: callable for custom argument validation
Should raise ParseError if it wants
impl: callable to be invoked when command is called
"""
self.names = names
self.validator = validator
self.helptext = helptext
self.impl = impl
self.args = []
self.required_groups = []
self.arg_dict = {}
self.positional_args = []
self.cmdline = None
class Argument(object):
"""Encapsulates an argument to a command."""
VALID_TYPES = ['string', 'readfile', 'int', 'flag', 'coords']
TYPES_WITH_VALUES = ['string', 'readfile', 'int', 'coords']
def __init__(self, names, helptext, type, metaname,
required, default, positional):
"""Command-line argument to a command.
Args:
names: argument name, or list of synonyms
helptext: brief description of the argument
type: type of the argument. Valid values include:
string - a string
readfile - a file which must exist and be available
for reading
int - an integer
flag - an optional flag (bool)
coords - (x,y) where x and y are ints
metaname: Name to display for value in help, inferred if not
specified
required: True if argument must be specified
default: Default value if not specified
positional: Argument specified by location, not name
Raises:
ValueError: the argument name is invalid for some reason
"""
if type not in Command.Argument.VALID_TYPES:
raise ValueError("Invalid type: %r" % type)
if required and default is not None:
raise ValueError("required and default are mutually exclusive")
if required and type == 'flag':
raise ValueError("A required flag? Give me a break.")
if metaname and type not in Command.Argument.TYPES_WITH_VALUES:
raise ValueError("Type %r can't have a metaname" % type)
# If no metaname is provided, infer it: use the alphabetical characters
# of the last provided name
if not metaname and type in Command.Argument.TYPES_WITH_VALUES:
metaname = (
names[-1].lstrip(string.punctuation + string.whitespace).upper())
self.names = names
self.helptext = helptext
self.type = type
self.required = required
self.default = default
self.positional = positional
self.metaname = metaname
self.mutex = [] # arguments that are mutually exclusive with
# this one
self.depends = [] # arguments that must be present for this
# one to be valid
self.present = False # has this argument been specified?
def AddDependency(self, arg):
"""Makes this argument dependent on another argument.
Args:
arg: name of the argument this one depends on
"""
if arg not in self.depends:
self.depends.append(arg)
def AddMutualExclusion(self, arg):
"""Makes this argument invalid if another is specified.
Args:
arg: name of the mutually exclusive argument.
"""
if arg not in self.mutex:
self.mutex.append(arg)
def GetUsageString(self):
"""Returns a brief string describing the argument's usage."""
if not self.positional:
string = self.names[0]
if self.type in Command.Argument.TYPES_WITH_VALUES:
string += "="+self.metaname
else:
string = self.metaname
if not self.required:
string = "["+string+"]"
return string
def GetNames(self):
"""Returns a string containing a list of the arg's names."""
if self.positional:
return self.metaname
else:
return ", ".join(self.names)
def GetHelpString(self, width=80, indent=5, names_width=20, gutter=2):
"""Returns a help string including help for all the arguments."""
names = [" "*indent + line +" "*(names_width-len(line)) for line in
textwrap.wrap(self.GetNames(), names_width)]
helpstring = textwrap.wrap(self.helptext, width-indent-names_width-gutter)
if len(names) < len(helpstring):
names += [" "*(indent+names_width)]*(len(helpstring)-len(names))
if len(helpstring) < len(names):
helpstring += [""]*(len(names)-len(helpstring))
return "\n".join([name_line + " "*gutter + help_line for
name_line, help_line in zip(names, helpstring)])
def __repr__(self):
if self.present:
string = '= %r' % self.value
else:
string = "(absent)"
return "Argument %s '%s'%s" % (self.type, self.names[0], string)
# end of nested class Argument
def AddArgument(self, names, helptext, type="string", metaname=None,
required=False, default=None, positional=False):
"""Command-line argument to a command.
Args:
names: argument name, or list of synonyms
helptext: brief description of the argument
type: type of the argument
metaname: Name to display for value in help, inferred if not
required: True if argument must be specified
default: Default value if not specified
positional: Argument specified by location, not name
Raises:
ValueError: the argument already exists or is invalid
Returns:
The newly-created argument
"""
if IsString(names): names = [names]
names = [name.lower() for name in names]
for name in names:
if name in self.arg_dict:
raise ValueError("%s is already an argument"%name)
if (positional and required and
[arg for arg in self.args if arg.positional] and
not [arg for arg in self.args if arg.positional][-1].required):
raise ValueError(
"A required positional argument may not follow an optional one.")
arg = Command.Argument(names, helptext, type, metaname,
required, default, positional)
self.args.append(arg)
for name in names:
self.arg_dict[name] = arg
return arg
def GetArgument(self, name):
"""Return an argument from a name."""
return self.arg_dict[name.lower()]
def AddMutualExclusion(self, args):
"""Specifies that a list of arguments are mutually exclusive."""
if len(args) < 2:
raise ValueError("At least two arguments must be specified.")
args = [arg.lower() for arg in args]
for index in xrange(len(args)-1):
for index2 in xrange(index+1, len(args)):
self.arg_dict[args[index]].AddMutualExclusion(self.arg_dict[args[index2]])
def AddDependency(self, dependent, depends_on):
"""Specifies that one argument may only be present if another is.
Args:
dependent: the name of the dependent argument
depends_on: the name of the argument on which it depends
"""
self.arg_dict[dependent.lower()].AddDependency(
self.arg_dict[depends_on.lower()])
def AddMutualDependency(self, args):
"""Specifies that a list of arguments are all mutually dependent."""
if len(args) < 2:
raise ValueError("At least two arguments must be specified.")
args = [arg.lower() for arg in args]
for (arg1, arg2) in [(arg1, arg2) for arg1 in args for arg2 in args]:
if arg1 == arg2: continue
self.arg_dict[arg1].AddDependency(self.arg_dict[arg2])
def AddRequiredGroup(self, args):
"""Specifies that at least one of the named arguments must be present."""
if len(args) < 2:
raise ValueError("At least two arguments must be in a required group.")
args = [self.arg_dict[arg.lower()] for arg in args]
self.required_groups.append(args)
def ParseArguments(self):
"""Given a command line, parse and validate the arguments."""
# reset all the arguments before we parse
for arg in self.args:
arg.present = False
arg.value = None
self.parse_errors = []
# look for arguments remaining on the command line
while len(self.cmdline.rargs):
try:
self.ParseNextArgument()
except ParseError, e:
self.parse_errors.append(e.args[0])
# after all the arguments are parsed, check for problems
for arg in self.args:
if not arg.present and arg.required:
self.parse_errors.append("'%s': required parameter was missing"
% arg.names[0])
if not arg.present and arg.default:
arg.present = True
arg.value = arg.default
if arg.present:
for mutex in arg.mutex:
if mutex.present:
self.parse_errors.append(
"'%s', '%s': arguments are mutually exclusive" %
(arg.argstr, mutex.argstr))
for depend in arg.depends:
if not depend.present:
self.parse_errors.append("'%s': '%s' must be specified as well" %
(arg.argstr, depend.names[0]))
# check for required groups
for group in self.required_groups:
if not [arg for arg in group if arg.present]:
self.parse_errors.append("%s: at least one must be present" %
(", ".join(["'%s'" % arg.names[-1] for arg in group])))
# if we have any validators, invoke them
if not self.parse_errors and self.validator:
try:
self.validator(self)
except ParseError, e:
self.parse_errors.append(e.args[0])
# Helper methods so you can treat the command like a dict
def __getitem__(self, key):
arg = self.arg_dict[key.lower()]
if arg.type == 'flag':
return arg.present
else:
return arg.value
def __iter__(self):
return [arg for arg in self.args if arg.present].__iter__()
def ArgumentPresent(self, key):
"""Tests if an argument exists and has been specified."""
return key.lower() in self.arg_dict and self.arg_dict[key.lower()].present
def __contains__(self, key):
return self.ArgumentPresent(key)
def ParseNextArgument(self):
"""Find the next argument in the command line and parse it."""
arg = None
value = None
argstr = self.cmdline.rargs.pop(0)
# First check: is this a literal argument?
if argstr.lower() in self.arg_dict:
arg = self.arg_dict[argstr.lower()]
if arg.type in Command.Argument.TYPES_WITH_VALUES:
if len(self.cmdline.rargs):
value = self.cmdline.rargs.pop(0)
# Second check: is this of the form "arg=val" or "arg:val"?
if arg is None:
delimiter_pos = -1
for delimiter in [':', '=']:
pos = argstr.find(delimiter)
if pos >= 0:
if delimiter_pos < 0 or pos < delimiter_pos:
delimiter_pos = pos
if delimiter_pos >= 0:
testarg = argstr[:delimiter_pos]
testval = argstr[delimiter_pos+1:]
if testarg.lower() in self.arg_dict:
arg = self.arg_dict[testarg.lower()]
argstr = testarg
value = testval
# Third check: does this begin an argument?
if arg is None:
for key in self.arg_dict.iterkeys():
if (len(key) < len(argstr) and
self.arg_dict[key].type in Command.Argument.TYPES_WITH_VALUES and
argstr[:len(key)].lower() == key):
value = argstr[len(key):]
argstr = argstr[:len(key)]
arg = self.arg_dict[argstr]
# Fourth check: do we have any positional arguments available?
if arg is None:
for positional_arg in [
testarg for testarg in self.args if testarg.positional]:
if not positional_arg.present:
arg = positional_arg
value = argstr
argstr = positional_arg.names[0]
break
# Push the retrieved argument/value onto the largs stack
if argstr: self.cmdline.largs.append(argstr)
if value: self.cmdline.largs.append(value)
# If we've made it this far and haven't found an arg, give up
if arg is None:
raise ParseError("Unknown argument: '%s'" % argstr)
# Convert the value, if necessary
if arg.type in Command.Argument.TYPES_WITH_VALUES and value is None:
raise ParseError("Argument '%s' requires a value" % argstr)
if value is not None:
value = self.StringToValue(value, arg.type, argstr)
arg.argstr = argstr
arg.value = value
arg.present = True
# end method ParseNextArgument
def StringToValue(self, value, type, argstr):
"""Convert a string from the command line to a value type."""
try:
if type == 'string':
pass # leave it be
elif type == 'int':
try:
value = int(value)
except ValueError:
raise ParseError
elif type == 'readfile':
if not os.path.isfile(value):
raise ParseError("'%s': '%s' does not exist" % (argstr, value))
elif type == 'coords':
try:
value = [int(val) for val in
re.match("\(\s*(\d+)\s*\,\s*(\d+)\s*\)\s*\Z", value).
groups()]
except AttributeError:
raise ParseError
else:
raise ValueError("Unknown type: '%s'" % type)
except ParseError, e:
# The bare exception is raised in the generic case; more specific errors
# will arrive with arguments and should just be reraised
if not e.args:
e = ParseError("'%s': unable to convert '%s' to type '%s'" %
(argstr, value, type))
raise e
return value
def SortArgs(self):
"""Returns a method that can be passed to sort() to sort arguments."""
def ArgSorter(arg1, arg2):
"""Helper for sorting arguments in the usage string.
Positional arguments come first, then required arguments,
then optional arguments. Pylint demands this trivial function
have both Args: and Returns: sections, sigh.
Args:
arg1: the first argument to compare
arg2: the second argument to compare
Returns:
-1 if arg1 should be sorted first, +1 if it should be sorted second,
and 0 if arg1 and arg2 have the same sort level.
"""
return ((arg2.positional-arg1.positional)*2 +
(arg2.required-arg1.required))
return ArgSorter
def GetUsageString(self, width=80, name=None):
"""Gets a string describing how the command is used."""
if name is None: name = self.names[0]
initial_indent = "Usage: %s %s " % (self.cmdline.prog, name)
subsequent_indent = " " * len(initial_indent)
sorted_args = self.args[:]
sorted_args.sort(self.SortArgs())
return textwrap.fill(
" ".join([arg.GetUsageString() for arg in sorted_args]), width,
initial_indent=initial_indent,
subsequent_indent=subsequent_indent)
def GetHelpString(self, width=80):
"""Returns a list of help strings for all this command's arguments."""
sorted_args = self.args[:]
sorted_args.sort(self.SortArgs())
return "\n".join([arg.GetHelpString(width) for arg in sorted_args])
# end class Command
class CommandLine(object):
"""Parse a command line, extracting a command and its arguments."""
def __init__(self):
self.commands = []
self.cmd_dict = {}
# Add the help command to the parser
help_cmd = self.AddCommand(["help", "--help", "-?", "-h"],
"Displays help text for a command",
ValidateHelpCommand,
DoHelpCommand)
help_cmd.AddArgument(
"command", "Command to retrieve help for", positional=True)
help_cmd.AddArgument(
"--width", "Width of the output", type='int', default=80)
self.Exit = sys.exit # override this if you don't want the script to halt
# on error or on display of help
self.out = sys.stdout # override these if you want to redirect
self.err = sys.stderr # output or error messages
def AddCommand(self, names, helptext, validator=None, impl=None):
"""Add a new command to the parser.
Args:
names: command name, or list of synonyms
helptext: brief string description of the command
validator: method to validate a command's arguments
impl: callable to be invoked when command is called
Raises:
ValueError: raised if command already added
Returns:
The new command
"""
if IsString(names): names = [names]
for name in names:
if name in self.cmd_dict:
raise ValueError("%s is already a command"%name)
cmd = Command(names, helptext, validator, impl)
cmd.cmdline = self
self.commands.append(cmd)
for name in names:
self.cmd_dict[name.lower()] = cmd
return cmd
def GetUsageString(self):
"""Returns simple usage instructions."""
return "Type '%s help' for usage." % self.prog
def ParseCommandLine(self, argv=None, prog=None, execute=True):
"""Does the work of parsing a command line.
Args:
argv: list of arguments, defaults to sys.args[1:]
prog: name of the command, defaults to the base name of the script
execute: if false, just parse, don't invoke the 'impl' member
Returns:
The command that was executed
"""
if argv is None: argv = sys.argv[1:]
if prog is None: prog = os.path.basename(sys.argv[0]).split('.')[0]
# Store off our parameters, we may need them someday
self.argv = argv
self.prog = prog
# We shouldn't be invoked without arguments, that's just lame
if not len(argv):
self.out.writelines(self.GetUsageString())
self.Exit()
return None # in case the client overrides Exit
# Is it a valid command?
self.command_string = argv[0].lower()
if not self.command_string in self.cmd_dict:
self.err.write("Unknown command: '%s'\n\n" % self.command_string)
self.out.write(self.GetUsageString())
self.Exit()
return None # in case the client overrides Exit
self.command = self.cmd_dict[self.command_string]
# "rargs" = remaining (unparsed) arguments
# "largs" = already parsed, "left" of the read head
self.rargs = argv[1:]
self.largs = []
# let the command object do the parsing
self.command.ParseArguments()
if self.command.parse_errors:
# there were errors, output the usage string and exit
self.err.write(self.command.GetUsageString()+"\n\n")
self.err.write("\n".join(self.command.parse_errors))
self.err.write("\n\n")
self.Exit()
elif execute and self.command.impl:
self.command.impl(self.command)
return self.command
def __getitem__(self, key):
return self.cmd_dict[key]
def __iter__(self):
return self.cmd_dict.__iter__()
def ValidateHelpCommand(command):
"""Checks to make sure an argument to 'help' is a valid command."""
if 'command' in command and command['command'] not in command.cmdline:
raise ParseError("'%s': unknown command" % command['command'])
def DoHelpCommand(command):
"""Executed when the command is 'help'."""
out = command.cmdline.out
width = command['--width']
if 'command' not in command:
out.write(command.GetUsageString())
out.write("\n\n")
indent = 5
gutter = 2
command_width = (
max([len(cmd.names[0]) for cmd in command.cmdline.commands]) + gutter)
for cmd in command.cmdline.commands:
cmd_name = cmd.names[0]
initial_indent = (" "*indent + cmd_name + " "*
(command_width+gutter-len(cmd_name)))
subsequent_indent = " "*(indent+command_width+gutter)
out.write(textwrap.fill(cmd.helptext, width,
initial_indent=initial_indent,
subsequent_indent=subsequent_indent))
out.write("\n")
out.write("\n")
else:
help_cmd = command.cmdline[command['command']]
out.write(textwrap.fill(help_cmd.helptext, width))
out.write("\n\n")
out.write(help_cmd.GetUsageString(width=width))
out.write("\n\n")
out.write(help_cmd.GetHelpString(width=width))
out.write("\n")
command.cmdline.Exit()
def main():
# If we're invoked rather than imported, run some tests
cmdline = CommandLine()
# Since we're testing, override Exit()
def TestExit():
pass
cmdline.Exit = TestExit
# Actually, while we're at it, let's override error output too
cmdline.err = open(os.path.devnull, "w")
test = cmdline.AddCommand(["test", "testa", "testb"], "test command")
test.AddArgument(["-i", "--int", "--integer", "--optint", "--optionalint"],
"optional integer parameter", type='int')
test.AddArgument("--reqint", "required integer parameter", type='int',
required=True)
test.AddArgument("pos1", "required positional argument", positional=True,
required=True)
test.AddArgument("pos2", "optional positional argument", positional=True)
test.AddArgument("pos3", "another optional positional arg",
positional=True)
# mutually dependent arguments
test.AddArgument("--mutdep1", "mutually dependent parameter 1")
test.AddArgument("--mutdep2", "mutually dependent parameter 2")
test.AddArgument("--mutdep3", "mutually dependent parameter 3")
test.AddMutualDependency(["--mutdep1", "--mutdep2", "--mutdep3"])
# mutually exclusive arguments
test.AddArgument("--mutex1", "mutually exclusive parameter 1")
test.AddArgument("--mutex2", "mutually exclusive parameter 2")
test.AddArgument("--mutex3", "mutually exclusive parameter 3")
test.AddMutualExclusion(["--mutex1", "--mutex2", "--mutex3"])
# dependent argument
test.AddArgument("--dependent", "dependent argument")
test.AddDependency("--dependent", "--int")
# other argument types
test.AddArgument("--file", "filename argument", type='readfile')
test.AddArgument("--coords", "coordinate argument", type='coords')
test.AddArgument("--flag", "flag argument", type='flag')
test.AddArgument("--req1", "part of a required group", type='flag')
test.AddArgument("--req2", "part 2 of a required group", type='flag')
test.AddRequiredGroup(["--req1", "--req2"])
# a few failure cases
exception_cases = """
test.AddArgument("failpos", "can't have req'd pos arg after opt",
positional=True, required=True)
+++
test.AddArgument("--int", "this argument already exists")
+++
test.AddDependency("--int", "--doesntexist")
+++
test.AddMutualDependency(["--doesntexist", "--mutdep2"])
+++
test.AddMutualExclusion(["--doesntexist", "--mutex2"])
+++
test.AddArgument("--reqflag", "required flag", required=True, type='flag')
+++
test.AddRequiredGroup(["--req1", "--doesntexist"])
"""
for exception_case in exception_cases.split("+++"):
try:
exception_case = exception_case.strip()
exec exception_case # yes, I'm using exec, it's just for a test.
except ValueError:
# this is expected
pass
except KeyError:
# ...and so is this
pass
else:
print ("FAILURE: expected an exception for '%s'"
" and didn't get it" % exception_case)
# Let's do some parsing! first, the minimal success line:
MIN = "test --reqint 123 param1 --req1 "
# tuples of (command line, expected error count)
test_lines = [
("test --int 3 foo --req1", 1), # missing required named parameter
("test --reqint 3 --req1", 1), # missing required positional parameter
(MIN, 0), # success!
("test param1 --reqint 123 --req1", 0), # success, order shouldn't matter
("test param1 --reqint 123 --req2", 0), # success, any of required group ok
(MIN+"param2", 0), # another positional parameter is okay
(MIN+"param2 param3", 0), # and so are three
(MIN+"param2 param3 param4", 1), # but four are just too many
(MIN+"--int", 1), # where's the value?
(MIN+"--int 456", 0), # this is fine
(MIN+"--int456", 0), # as is this
(MIN+"--int:456", 0), # and this
(MIN+"--int=456", 0), # and this
(MIN+"--file c:\\windows\\system32\\kernel32.dll", 0), # yup
(MIN+"--file c:\\thisdoesntexist", 1), # nope
(MIN+"--mutdep1 a", 2), # no!
(MIN+"--mutdep2 b", 2), # also no!
(MIN+"--mutdep3 c", 2), # dream on!
(MIN+"--mutdep1 a --mutdep2 b", 2), # almost!
(MIN+"--mutdep1 a --mutdep2 b --mutdep3 c", 0), # yes
(MIN+"--mutex1 a", 0), # yes
(MIN+"--mutex2 b", 0), # yes
(MIN+"--mutex3 c", 0), # fine
(MIN+"--mutex1 a --mutex2 b", 1), # not fine
(MIN+"--mutex1 a --mutex2 b --mutex3 c", 3), # even worse
(MIN+"--dependent 1", 1), # no
(MIN+"--dependent 1 --int 2", 0), # ok
(MIN+"--int abc", 1), # bad type
(MIN+"--coords abc", 1), # also bad
(MIN+"--coords (abc)", 1), # getting warmer
(MIN+"--coords (abc,def)", 1), # missing something
(MIN+"--coords (123)", 1), # ooh, so close
(MIN+"--coords (123,def)", 1), # just a little farther
(MIN+"--coords (123,456)", 0), # finally!
("test --int 123 --reqint=456 foo bar --coords(42,88) baz --req1", 0)
]
badtests = 0
for (test, expected_failures) in test_lines:
cmdline.ParseCommandLine([x.strip() for x in test.strip().split(" ")])
if not len(cmdline.command.parse_errors) == expected_failures:
print "FAILED:\n issued: '%s'\n expected: %d\n received: %d\n\n" % (
test, expected_failures, len(cmdline.command.parse_errors))
badtests += 1
print "%d failed out of %d tests" % (badtests, len(test_lines))
cmdline.ParseCommandLine(["help", "test"])
if __name__ == "__main__":
sys.exit(main())
|