@@ -156,8 +156,12 @@ def my_completer(self, text, line, begidx, endidx, arg_tokens)
156156 2 Another item False
157157 3 Yet another item False
158158
159- To use CompletionItems, just return them from your choices or completer
160- functions.
159+ To use CompletionItems, just return them from your choices_provider or
160+ completer functions. They can also be used as argparse choices. When a
161+ CompletionItem is created, it stores the original value (e.g. ID number) and
162+ makes it accessible through a property called orig_value. cmd2 has patched
163+ argparse so that when evaluating choices, input is compared to
164+ CompletionItem.orig_value instead of the CompletionItem instance.
161165
162166To avoid printing a ton of information to the screen at once when a user
163167presses tab, there is a maximum threshold for the number of CompletionItems
@@ -173,6 +177,12 @@ def my_completer(self, text, line, begidx, endidx, arg_tokens)
173177completion and enables nargs range parsing. See _add_argument_wrapper for
174178more details on these arguments.
175179
180+ ``argparse.ArgumentParser._check_value`` - adds support for using
181+ ``CompletionItems`` as argparse choices. When evaluating choices, input is
182+ compared to ``CompletionItem.orig_value`` instead of the ``CompletionItem``
183+ instance.
184+ See _ArgumentParser_check_value for more details.
185+
176186``argparse.ArgumentParser._get_nargs_pattern`` - adds support for nargs ranges.
177187See _get_nargs_pattern_wrapper for more details.
178188
@@ -308,17 +318,26 @@ def __new__(cls, value: object, *args: Any, **kwargs: Any) -> 'CompletionItem':
308318 return super (CompletionItem , cls ).__new__ (cls , value )
309319
310320 # noinspection PyUnusedLocal
311- def __init__ (self , value : object , desc : str = '' , * args : Any ) -> None :
321+ def __init__ (self , value : object , description : str = '' , * args : Any ) -> None :
312322 """
313323 CompletionItem Initializer
314324
315325 :param value: the value being tab completed
316- :param desc : description text to display
326+ :param description : description text to display
317327 :param args: args for str __init__
318328 :param kwargs: kwargs for str __init__
319329 """
320330 super ().__init__ (* args )
321- self .description = desc
331+ self .description = description
332+
333+ # Save the original value to support CompletionItems as argparse choices.
334+ # cmd2 has patched argparse so input is compared to this value instead of the CompletionItem instance.
335+ self .__orig_value = value
336+
337+ @property
338+ def orig_value (self ) -> Any :
339+ """Read-only property for __orig_value"""
340+ return self .__orig_value
322341
323342
324343############################################################################################################
@@ -870,7 +889,7 @@ def _add_argument_wrapper(
870889setattr (argparse ._ActionsContainer , 'add_argument' , _add_argument_wrapper )
871890
872891############################################################################################################
873- # Patch ArgumentParser._get_nargs_pattern with our wrapper to nargs ranges
892+ # Patch ArgumentParser._get_nargs_pattern with our wrapper to support nargs ranges
874893############################################################################################################
875894
876895# Save original ArgumentParser._get_nargs_pattern so we can call it in our wrapper
@@ -905,7 +924,7 @@ def _get_nargs_pattern_wrapper(self: argparse.ArgumentParser, action: argparse.A
905924
906925
907926############################################################################################################
908- # Patch ArgumentParser._match_argument with our wrapper to nargs ranges
927+ # Patch ArgumentParser._match_argument with our wrapper to support nargs ranges
909928############################################################################################################
910929# noinspection PyProtectedMember
911930orig_argument_parser_match_argument = argparse .ArgumentParser ._match_argument
@@ -977,6 +996,38 @@ def _ArgumentParser_set_ap_completer_type(self: argparse.ArgumentParser, ap_comp
977996setattr (argparse .ArgumentParser , 'set_ap_completer_type' , _ArgumentParser_set_ap_completer_type )
978997
979998
999+ ############################################################################################################
1000+ # Patch ArgumentParser._check_value to support CompletionItems as choices
1001+ ############################################################################################################
1002+ # noinspection PyPep8Naming
1003+ def _ArgumentParser_check_value (self : argparse .ArgumentParser , action : argparse .Action , value : Any ) -> None :
1004+ """
1005+ Custom override of ArgumentParser._check_value that supports CompletionItems as choices.
1006+ When evaluating choices, input is compared to CompletionItem.orig_value instead of the
1007+ CompletionItem instance.
1008+
1009+ :param self: ArgumentParser instance
1010+ :param action: the action being populated
1011+ :param value: value from command line already run through conversion function by argparse
1012+ """
1013+ # Import gettext like argparse does
1014+ from gettext import (
1015+ gettext as _ ,
1016+ )
1017+
1018+ # converted value must be one of the choices (if specified)
1019+ if action .choices is not None :
1020+ # If any choice is a CompletionItem, then use its orig_value property.
1021+ choices = [c .orig_value if isinstance (c , CompletionItem ) else c for c in action .choices ]
1022+ if value not in choices :
1023+ args = {'value' : value , 'choices' : ', ' .join (map (repr , choices ))}
1024+ msg = _ ('invalid choice: %(value)r (choose from %(choices)s)' )
1025+ raise ArgumentError (action , msg % args )
1026+
1027+
1028+ setattr (argparse .ArgumentParser , '_check_value' , _ArgumentParser_check_value )
1029+
1030+
9801031############################################################################################################
9811032# Patch argparse._SubParsersAction to add remove_parser function
9821033############################################################################################################
0 commit comments