@@ -67,12 +67,12 @@ def __init__(self, *args, **kwargs):
67
67
68
68
# Add subcommands to music
69
69
music_subparsers = music_parser .add_subparsers ()
70
- music_create_parser = music_subparsers .add_parser ('create' , help = 'Create music' )
70
+ music_create_parser = music_subparsers .add_parser ('create' , help = 'create music' )
71
71
72
72
# Add subcommands to music -> create
73
73
music_create_subparsers = music_create_parser .add_subparsers ()
74
- music_create_jazz_parser = music_create_subparsers .add_parser ('jazz' , help = 'Create jazz' )
75
- music_create_rock_parser = music_create_subparsers .add_parser ('rock' , help = 'Create rocks' )
74
+ music_create_jazz_parser = music_create_subparsers .add_parser ('jazz' , help = 'create jazz' )
75
+ music_create_rock_parser = music_create_subparsers .add_parser ('rock' , help = 'create rocks' )
76
76
77
77
@with_argparser (music_parser )
78
78
def do_music (self , args : argparse .Namespace ) -> None :
@@ -84,10 +84,10 @@ def do_music(self, args: argparse.Namespace) -> None:
84
84
85
85
# Uses default flag prefix value (-)
86
86
flag_parser = Cmd2ArgumentParser ()
87
- flag_parser .add_argument ('-n' , '--normal_flag' , help = 'A normal flag' , action = 'store_true' )
88
- flag_parser .add_argument ('-a' , '--append_flag' , help = 'Append flag' , action = 'append' )
89
- flag_parser .add_argument ('-o' , '--append_const_flag' , help = 'Append const flag' , action = 'append_const' , const = True )
90
- flag_parser .add_argument ('-c' , '--count_flag' , help = 'Count flag' , action = 'count' )
87
+ flag_parser .add_argument ('-n' , '--normal_flag' , help = 'a normal flag' , action = 'store_true' )
88
+ flag_parser .add_argument ('-a' , '--append_flag' , help = 'append flag' , action = 'append' )
89
+ flag_parser .add_argument ('-o' , '--append_const_flag' , help = 'append const flag' , action = 'append_const' , const = True )
90
+ flag_parser .add_argument ('-c' , '--count_flag' , help = 'count flag' , action = 'count' )
91
91
flag_parser .add_argument ('-s' , '--suppressed_flag' , help = argparse .SUPPRESS , action = 'store_true' )
92
92
flag_parser .add_argument ('-r' , '--remainder_flag' , nargs = argparse .REMAINDER , help = 'a remainder flag' )
93
93
@@ -97,7 +97,7 @@ def do_flag(self, args: argparse.Namespace) -> None:
97
97
98
98
# Uses non-default flag prefix value (+)
99
99
plus_flag_parser = Cmd2ArgumentParser (prefix_chars = '+' )
100
- plus_flag_parser .add_argument ('+n' , '++normal_flag' , help = 'A normal flag' , action = 'store_true' )
100
+ plus_flag_parser .add_argument ('+n' , '++normal_flag' , help = 'a normal flag' , action = 'store_true' )
101
101
102
102
@with_argparser (plus_flag_parser )
103
103
def do_plus_flag (self , args : argparse .Namespace ) -> None :
@@ -251,6 +251,22 @@ def do_raise_completion_error(self, args: argparse.Namespace) -> None:
251
251
def do_arg_tokens (self , args : argparse .Namespace ) -> None :
252
252
pass
253
253
254
+ ############################################################################################################
255
+ # Begin code related to mutually exclusive groups
256
+ ############################################################################################################
257
+ mutex_parser = Cmd2ArgumentParser ()
258
+
259
+ mutex_group = mutex_parser .add_mutually_exclusive_group (required = True )
260
+ mutex_group .add_argument ('optional_pos' , help = 'the optional positional' , nargs = argparse .OPTIONAL )
261
+ mutex_group .add_argument ('-f' , '--flag' , help = 'the flag arg' )
262
+ mutex_group .add_argument ('-o' , '--other_flag' , help = 'the other flag arg' )
263
+
264
+ mutex_parser .add_argument ('last_arg' , help = 'the last arg' )
265
+
266
+ @with_argparser (mutex_parser )
267
+ def do_mutex (self , args : argparse .Namespace ) -> None :
268
+ pass
269
+
254
270
255
271
@pytest .fixture
256
272
def ac_app ():
@@ -271,8 +287,16 @@ def test_help(ac_app, command):
271
287
assert out1 == out2
272
288
273
289
290
+ def test_bad_subcommand_help (ac_app ):
291
+ # These should give the same output because the second one isn't using a
292
+ # real subcommand, so help will be called on the music command instead.
293
+ out1 , err1 = run_cmd (ac_app , 'help music' )
294
+ out2 , err2 = run_cmd (ac_app , 'help music fake' )
295
+ assert out1 == out2
296
+
297
+
274
298
@pytest .mark .parametrize ('command, text, completions' , [
275
- ('' , 'mu ' , ['music ' ]),
299
+ ('' , 'mus ' , ['music ' ]),
276
300
('music' , 'cre' , ['create ' ]),
277
301
('music' , 'creab' , []),
278
302
('music create' , '' , ['jazz' , 'rock' ]),
@@ -770,6 +794,45 @@ def test_arg_tokens(ac_app, command_and_args, completions):
770
794
assert ac_app .completion_matches == sorted (completions , key = ac_app .default_sort_key )
771
795
772
796
797
+ @pytest .mark .parametrize ('command_and_args, text, output_contains, first_match' , [
798
+ # Group isn't done. Hint will show for optional positional and no completions returned
799
+ ('mutex' , '' , 'the optional positional' , None ),
800
+
801
+ # Group isn't done. Flag name will still complete.
802
+ ('mutex' , '--fl' , '' , '--flag ' ),
803
+
804
+ # Group isn't done. Flag hint will show.
805
+ ('mutex --flag' , '' , 'the flag arg' , None ),
806
+
807
+ # Group finished by optional positional. No flag name will complete.
808
+ ('mutex pos_val' , '--fl' , '' , None ),
809
+
810
+ # Group finished by optional positional. Error will display trying to complete the flag's value.
811
+ ('mutex pos_val --flag' , '' , 'f/--flag: not allowed with argument optional_pos' , None ),
812
+
813
+ # Group finished by --flag. Optional positional will be skipped and last_arg will show its hint.
814
+ ('mutex --flag flag_val' , '' , 'the last arg' , None ),
815
+
816
+ # Group finished by --flag. Other flag won't complete.
817
+ ('mutex --flag flag_val' , '--oth' , '' , None ),
818
+
819
+ # Group finished by --flag. Error will display trying to complete other flag's value.
820
+ ('mutex --flag flag_val --other' , '' , '-o/--other_flag: not allowed with argument -f/--flag' , None ),
821
+
822
+ # Group finished by --flag. That same flag can be used again so it's hint will show.
823
+ ('mutex --flag flag_val --flag' , '' , 'the flag arg' , None )
824
+ ])
825
+ def test_complete_mutex_group (ac_app , command_and_args , text , output_contains , first_match , capsys ):
826
+ line = '{} {}' .format (command_and_args , text )
827
+ endidx = len (line )
828
+ begidx = endidx - len (text )
829
+
830
+ assert first_match == complete_tester (text , line , begidx , endidx , ac_app )
831
+
832
+ out , err = capsys .readouterr ()
833
+ assert output_contains in out
834
+
835
+
773
836
def test_single_prefix_char ():
774
837
from cmd2 .argparse_completer import _single_prefix_char
775
838
parser = Cmd2ArgumentParser (prefix_chars = '-+' )
0 commit comments