@@ -105,7 +105,7 @@ def do_pos_and_flag(self, args: argparse.Namespace) -> None:
105105 ############################################################################################################
106106 STR_METAVAR = "HEADLESS"
107107 TUPLE_METAVAR = ('arg1' , 'others' )
108- CUSTOM_TABLE_HEADER = ("Custom Header " ,)
108+ DESCRIPTION_TABLE_HEADER = ("Description " ,)
109109
110110 # tuples (for sake of immutability) used in our tests (there is a mix of sorted and unsorted on purpose)
111111 non_negative_num_choices = (1 , 2 , 3 , 0.5 , 22 )
@@ -140,45 +140,82 @@ def completion_item_method(self) -> list[CompletionItem]:
140140 choices_parser = Cmd2ArgumentParser ()
141141
142142 # Flag args for choices command. Include string and non-string arg types.
143- choices_parser .add_argument ("-l" , "--list" , help = "a flag populated with a choices list" , choices = static_choices_list )
144143 choices_parser .add_argument (
145- "-p" , "--provider" , help = "a flag populated with a choices provider" , choices_provider = choices_provider
144+ "-l" ,
145+ "--list" ,
146+ help = "a flag populated with a choices list" ,
147+ choices = static_choices_list ,
146148 )
147149 choices_parser .add_argument (
148- "--table_header" ,
149- help = 'this arg has a table header' ,
150+ "-p" ,
151+ "--provider" ,
152+ help = "a flag populated with a choices provider" ,
153+ choices_provider = choices_provider ,
154+ )
155+ choices_parser .add_argument (
156+ "--no_metavar" ,
157+ help = 'this arg has no metavar' ,
150158 choices_provider = completion_item_method ,
151- table_header = CUSTOM_TABLE_HEADER ,
159+ table_header = DESCRIPTION_TABLE_HEADER ,
152160 )
153161 choices_parser .add_argument (
154- "--no_header " ,
155- help = 'this arg has no table header ' ,
162+ "--str_metavar " ,
163+ help = 'this arg has str for a metavar ' ,
156164 choices_provider = completion_item_method ,
157165 metavar = STR_METAVAR ,
166+ table_header = DESCRIPTION_TABLE_HEADER ,
158167 )
159168 choices_parser .add_argument (
160169 '-t' ,
161170 "--tuple_metavar" ,
162171 help = 'this arg has tuple for a metavar' ,
163- choices_provider = completion_item_method ,
164172 metavar = TUPLE_METAVAR ,
165173 nargs = argparse .ONE_OR_MORE ,
174+ choices_provider = completion_item_method ,
175+ table_header = DESCRIPTION_TABLE_HEADER ,
176+ )
177+ choices_parser .add_argument (
178+ '-n' ,
179+ '--num' ,
180+ type = int ,
181+ help = 'a flag with an int type' ,
182+ choices = num_choices ,
183+ )
184+ choices_parser .add_argument (
185+ '--completion_items' ,
186+ help = 'choices are CompletionItems' ,
187+ choices = completion_item_choices ,
188+ table_header = DESCRIPTION_TABLE_HEADER ,
166189 )
167- choices_parser .add_argument ('-n' , '--num' , type = int , help = 'a flag with an int type' , choices = num_choices )
168- choices_parser .add_argument ('--completion_items' , help = 'choices are CompletionItems' , choices = completion_item_choices )
169190 choices_parser .add_argument (
170- '--num_completion_items' , help = 'choices are numerical CompletionItems' , choices = num_completion_items
191+ '--num_completion_items' ,
192+ help = 'choices are numerical CompletionItems' ,
193+ choices = num_completion_items ,
194+ table_header = DESCRIPTION_TABLE_HEADER ,
171195 )
172196
173197 # Positional args for choices command
174- choices_parser .add_argument ("list_pos" , help = "a positional populated with a choices list" , choices = static_choices_list )
175198 choices_parser .add_argument (
176- "method_pos" , help = "a positional populated with a choices provider" , choices_provider = choices_provider
199+ "list_pos" ,
200+ help = "a positional populated with a choices list" ,
201+ choices = static_choices_list ,
202+ )
203+ choices_parser .add_argument (
204+ "method_pos" ,
205+ help = "a positional populated with a choices provider" ,
206+ choices_provider = choices_provider ,
177207 )
178208 choices_parser .add_argument (
179- 'non_negative_num' , type = int , help = 'a positional with non-negative numerical choices' , choices = non_negative_num_choices
209+ 'non_negative_num' ,
210+ type = int ,
211+ help = 'a positional with non-negative numerical choices' ,
212+ choices = non_negative_num_choices ,
213+ )
214+ choices_parser .add_argument (
215+ 'empty_choices' ,
216+ help = 'a positional with empty choices' ,
217+ choices = [],
180218 )
181- choices_parser .add_argument ('empty_choices' , help = 'a positional with empty choices' , choices = [])
182219
183220 @with_argparser (choices_parser )
184221 def do_choices (self , args : argparse .Namespace ) -> None :
@@ -854,20 +891,20 @@ def test_unfinished_flag_error(ac_app, command_and_args, text, is_error) -> None
854891 assert is_error == all (x in completions .error for x in ["Error: argument" , "expected" ])
855892
856893
857- def test_completion_table_arg_header (ac_app ) -> None :
894+ def test_completion_table_metavar (ac_app ) -> None :
858895 # Test when metavar is None
859896 text = ''
860- line = f'choices --table_header { text } '
897+ line = f'choices --no_metavar { text } '
861898 endidx = len (line )
862899 begidx = endidx - len (text )
863900
864901 completions = ac_app .complete (text , line , begidx , endidx )
865902 assert completions .table is not None
866- assert completions .table .columns [0 ].header == "TABLE_HEADER "
903+ assert completions .table .columns [0 ].header == "NO_METAVAR "
867904
868905 # Test when metavar is a string
869906 text = ''
870- line = f'choices --no_header { text } '
907+ line = f'choices --str_metavar { text } '
871908 endidx = len (line )
872909 begidx = endidx - len (text )
873910
@@ -908,32 +945,6 @@ def test_completion_table_arg_header(ac_app) -> None:
908945 assert completions .table .columns [0 ].header == ac_app .TUPLE_METAVAR [1 ].upper ()
909946
910947
911- def test_completion_table_header (ac_app ) -> None :
912- from cmd2 .argparse_completer import (
913- DEFAULT_TABLE_HEADER ,
914- )
915-
916- # This argument provided a table header
917- text = ''
918- line = f'choices --table_header { text } '
919- endidx = len (line )
920- begidx = endidx - len (text )
921-
922- completions = ac_app .complete (text , line , begidx , endidx )
923- assert completions .table is not None
924- assert ac_app .CUSTOM_TABLE_HEADER [0 ] == completions .table .columns [1 ].header
925-
926- # This argument did not provide a table header, so it should be DEFAULT_TABLE_HEADER
927- text = ''
928- line = f'choices --no_header { text } '
929- endidx = len (line )
930- begidx = endidx - len (text )
931-
932- completions = ac_app .complete (text , line , begidx , endidx )
933- assert completions .table is not None
934- assert DEFAULT_TABLE_HEADER [0 ] == completions .table .columns [1 ].header
935-
936-
937948@pytest .mark .parametrize (
938949 ('command_and_args' , 'text' , 'has_hint' ),
939950 [
@@ -1165,6 +1176,94 @@ def test_display_meta(ac_app, subcommand, flag, display_meta) -> None:
11651176 assert completions [0 ].display_meta == display_meta
11661177
11671178
1179+ def test_validate_table_data_no_table () -> None :
1180+ action = argparse .Action (option_strings = ['-f' ], dest = 'foo' )
1181+ action .set_table_header (None )
1182+ arg_state = argparse_completer ._ArgumentState (action )
1183+ completions = Completions (
1184+ [
1185+ CompletionItem ('item1' ),
1186+ CompletionItem ('item2' ),
1187+ ]
1188+ )
1189+
1190+ # This should not raise an exception
1191+ argparse_completer .ArgparseCompleter ._validate_table_data (arg_state , completions )
1192+
1193+
1194+ def test_validate_table_data_missing_header () -> None :
1195+ action = argparse .Action (option_strings = ['-f' ], dest = 'foo' )
1196+ action .set_table_header (None )
1197+ arg_state = argparse_completer ._ArgumentState (action )
1198+
1199+ completions = Completions (
1200+ [
1201+ CompletionItem ('item1' , table_row = ['data1' ]),
1202+ CompletionItem ('item2' , table_row = ['data2' ]),
1203+ ]
1204+ )
1205+
1206+ with pytest .raises (
1207+ ValueError ,
1208+ match = "Argument 'foo' has CompletionItems with table_row, but no table_header was defined" ,
1209+ ):
1210+ argparse_completer .ArgparseCompleter ._validate_table_data (arg_state , completions )
1211+
1212+
1213+ def test_validate_table_data_missing_row_data () -> None :
1214+ action = argparse .Action (option_strings = ['-f' ], dest = 'foo' )
1215+ action .set_table_header (['Col1' ])
1216+ arg_state = argparse_completer ._ArgumentState (action )
1217+
1218+ completions = Completions (
1219+ [
1220+ CompletionItem ('item1' , table_row = ['data1' ]),
1221+ CompletionItem ('item2' ), # Missing table_row
1222+ ]
1223+ )
1224+
1225+ with pytest .raises (
1226+ ValueError ,
1227+ match = "Argument 'foo' has table_header defined, but the CompletionItem for 'item2' is missing table_row" ,
1228+ ):
1229+ argparse_completer .ArgparseCompleter ._validate_table_data (arg_state , completions )
1230+
1231+
1232+ def test_validate_table_row_data_length_mismatch () -> None :
1233+ action = argparse .Action (option_strings = ['-f' ], dest = 'foo' )
1234+ action .set_table_header (['Col1' , 'Col2' ])
1235+ arg_state = argparse_completer ._ArgumentState (action )
1236+
1237+ completions = Completions (
1238+ [
1239+ CompletionItem ('item1' , table_row = ['data1a' , 'data1b' ]),
1240+ CompletionItem ('item2' , table_row = ['only_one' ]),
1241+ ]
1242+ )
1243+
1244+ with pytest .raises (
1245+ ValueError ,
1246+ match = r"Argument 'foo': table_row length \(1\) does not match table_header length \(2\) for item 'item2'." ,
1247+ ):
1248+ argparse_completer .ArgparseCompleter ._validate_table_data (arg_state , completions )
1249+
1250+
1251+ def test_validate_table_data_valid () -> None :
1252+ action = argparse .Action (option_strings = ['-f' ], dest = 'foo' )
1253+ action .get_table_header = lambda : ['Col1' , 'Col2' ]
1254+ arg_state = argparse_completer ._ArgumentState (action )
1255+
1256+ completions = Completions (
1257+ [
1258+ CompletionItem ('item1' , table_row = ['data1a' , 'data1b' ]),
1259+ CompletionItem ('item2' , table_row = ['data2a' , 'data2b' ]),
1260+ ]
1261+ )
1262+
1263+ # This should not raise an exception
1264+ argparse_completer .ArgparseCompleter ._validate_table_data (arg_state , completions )
1265+
1266+
11681267# Custom ArgparseCompleter-based class
11691268class CustomCompleter (argparse_completer .ArgparseCompleter ):
11701269 def _complete_flags (self , text : str , line : str , begidx : int , endidx : int , matched_flags : list [str ]) -> list [str ]:
0 commit comments