@@ -2833,26 +2833,30 @@ def test_failures_when_not_required(self):
28332833parse_args = self .get_parser (required = False ).parse_args
28342834error = ArgumentParserError
28352835for args_string in self .failures :
2836- self .assertRaises (error ,parse_args ,args_string .split ())
2836+ with self .subTest (args = args_string ):
2837+ self .assertRaises (error ,parse_args ,args_string .split ())
28372838
28382839def test_failures_when_required (self ):
28392840parse_args = self .get_parser (required = True ).parse_args
28402841error = ArgumentParserError
28412842for args_string in self .failures + ['' ]:
2842- self .assertRaises (error ,parse_args ,args_string .split ())
2843+ with self .subTest (args = args_string ):
2844+ self .assertRaises (error ,parse_args ,args_string .split ())
28432845
28442846def test_successes_when_not_required (self ):
28452847parse_args = self .get_parser (required = False ).parse_args
28462848successes = self .successes + self .successes_when_not_required
28472849for args_string ,expected_ns in successes :
2848- actual_ns = parse_args (args_string .split ())
2849- self .assertEqual (actual_ns ,expected_ns )
2850+ with self .subTest (args = args_string ):
2851+ actual_ns = parse_args (args_string .split ())
2852+ self .assertEqual (actual_ns ,expected_ns )
28502853
28512854def test_successes_when_required (self ):
28522855parse_args = self .get_parser (required = True ).parse_args
28532856for args_string ,expected_ns in self .successes :
2854- actual_ns = parse_args (args_string .split ())
2855- self .assertEqual (actual_ns ,expected_ns )
2857+ with self .subTest (args = args_string ):
2858+ actual_ns = parse_args (args_string .split ())
2859+ self .assertEqual (actual_ns ,expected_ns )
28562860
28572861def test_usage_when_not_required (self ):
28582862format_usage = self .get_parser (required = False ).format_usage
@@ -3239,6 +3243,111 @@ def get_parser(self, required):
32393243test_successes_when_not_required = None
32403244test_successes_when_required = None
32413245
3246+
3247+ class TestMutuallyExclusiveOptionalOptional (MEMixin ,TestCase ):
3248+ def get_parser (self ,required = None ):
3249+ parser = ErrorRaisingArgumentParser (prog = 'PROG' )
3250+ group = parser .add_mutually_exclusive_group (required = required )
3251+ group .add_argument ('--foo' )
3252+ group .add_argument ('--bar' ,nargs = '?' )
3253+ return parser
3254+
3255+ failures = [
3256+ '--foo X --bar Y' ,
3257+ '--foo X --bar' ,
3258+ ]
3259+ successes = [
3260+ ('--foo X' ,NS (foo = 'X' ,bar = None )),
3261+ ('--bar X' ,NS (foo = None ,bar = 'X' )),
3262+ ('--bar' ,NS (foo = None ,bar = None )),
3263+ ]
3264+ successes_when_not_required = [
3265+ ('' ,NS (foo = None ,bar = None )),
3266+ ]
3267+ usage_when_required = '''\
3268+ usage: PROG [-h] (--foo FOO | --bar [BAR])
3269+ '''
3270+ usage_when_not_required = '''\
3271+ usage: PROG [-h] [--foo FOO | --bar [BAR]]
3272+ '''
3273+ help = '''\
3274+
3275+ options:
3276+ -h, --help show this help message and exit
3277+ --foo FOO
3278+ --bar [BAR]
3279+ '''
3280+
3281+
3282+ class TestMutuallyExclusiveOptionalWithDefault (MEMixin ,TestCase ):
3283+ def get_parser (self ,required = None ):
3284+ parser = ErrorRaisingArgumentParser (prog = 'PROG' )
3285+ group = parser .add_mutually_exclusive_group (required = required )
3286+ group .add_argument ('--foo' )
3287+ group .add_argument ('--bar' ,type = bool ,default = True )
3288+ return parser
3289+
3290+ failures = [
3291+ '--foo X --bar Y' ,
3292+ '--foo X --bar=' ,
3293+ ]
3294+ successes = [
3295+ ('--foo X' ,NS (foo = 'X' ,bar = True )),
3296+ ('--bar X' ,NS (foo = None ,bar = True )),
3297+ ('--bar=' ,NS (foo = None ,bar = False )),
3298+ ]
3299+ successes_when_not_required = [
3300+ ('' ,NS (foo = None ,bar = True )),
3301+ ]
3302+ usage_when_required = '''\
3303+ usage: PROG [-h] (--foo FOO | --bar BAR)
3304+ '''
3305+ usage_when_not_required = '''\
3306+ usage: PROG [-h] [--foo FOO | --bar BAR]
3307+ '''
3308+ help = '''\
3309+
3310+ options:
3311+ -h, --help show this help message and exit
3312+ --foo FOO
3313+ --bar BAR
3314+ '''
3315+
3316+
3317+ class TestMutuallyExclusivePositionalWithDefault (MEMixin ,TestCase ):
3318+ def get_parser (self ,required = None ):
3319+ parser = ErrorRaisingArgumentParser (prog = 'PROG' )
3320+ group = parser .add_mutually_exclusive_group (required = required )
3321+ group .add_argument ('--foo' )
3322+ group .add_argument ('bar' ,nargs = '?' ,type = bool ,default = True )
3323+ return parser
3324+
3325+ failures = [
3326+ '--foo X Y' ,
3327+ ]
3328+ successes = [
3329+ ('--foo X' ,NS (foo = 'X' ,bar = True )),
3330+ ('X' ,NS (foo = None ,bar = True )),
3331+ ]
3332+ successes_when_not_required = [
3333+ ('' ,NS (foo = None ,bar = True )),
3334+ ]
3335+ usage_when_required = '''\
3336+ usage: PROG [-h] (--foo FOO | bar)
3337+ '''
3338+ usage_when_not_required = '''\
3339+ usage: PROG [-h] [--foo FOO | bar]
3340+ '''
3341+ help = '''\
3342+
3343+ positional arguments:
3344+ bar
3345+
3346+ options:
3347+ -h, --help show this help message and exit
3348+ --foo FOO
3349+ '''
3350+
32423351# =================================================
32433352# Mutually exclusive group in parent parser tests
32443353# =================================================