@@ -64,7 +64,8 @@ def my_completer(text: str, line: str, begidx: int, endidx:int, extra_param: str
6464
6565
6666# imports copied from argparse to support our customized argparse functions
67- from argparse import ZERO_OR_MORE , ONE_OR_MORE , ArgumentError , _
67+ from argparse import ZERO_OR_MORE , ONE_OR_MORE , ArgumentError , _ , _get_action_name , SUPPRESS
68+
6869import re as _re
6970
7071
@@ -538,7 +539,10 @@ def _print_action_help(self, action: argparse.Action) -> None:
538539
539540 prefix = ' {0: <{width}} ' .format (prefix , width = 20 )
540541 pref_len = len (prefix )
541- help_lines = action .help .splitlines ()
542+ if action .help is not None :
543+ help_lines = action .help .splitlines ()
544+ else :
545+ help_lines = ['' ]
542546 if len (help_lines ) == 1 :
543547 print ('\n Hint:\n {}{}\n ' .format (prefix , help_lines [0 ]))
544548 else :
@@ -872,3 +876,259 @@ def _match_argument(self, action, arg_strings_pattern):
872876 'Expected between {} and {} arguments' .format (action .nargs_min , action .nargs_max ))
873877
874878 return super (ACArgumentParser , self )._match_argument (action , arg_strings_pattern )
879+
880+ def _parse_known_args (self , arg_strings , namespace ):
881+ # replace arg strings that are file references
882+ if self .fromfile_prefix_chars is not None :
883+ arg_strings = self ._read_args_from_files (arg_strings )
884+
885+ # map all mutually exclusive arguments to the other arguments
886+ # they can't occur with
887+ action_conflicts = {}
888+ for mutex_group in self ._mutually_exclusive_groups :
889+ group_actions = mutex_group ._group_actions
890+ for i , mutex_action in enumerate (mutex_group ._group_actions ):
891+ conflicts = action_conflicts .setdefault (mutex_action , [])
892+ conflicts .extend (group_actions [:i ])
893+ conflicts .extend (group_actions [i + 1 :])
894+
895+ # find all option indices, and determine the arg_string_pattern
896+ # which has an 'O' if there is an option at an index,
897+ # an 'A' if there is an argument, or a '-' if there is a '--'
898+ option_string_indices = {}
899+ arg_string_pattern_parts = []
900+ arg_strings_iter = iter (arg_strings )
901+ for i , arg_string in enumerate (arg_strings_iter ):
902+
903+ # all args after -- are non-options
904+ if arg_string == '--' :
905+ arg_string_pattern_parts .append ('-' )
906+ for arg_string in arg_strings_iter :
907+ arg_string_pattern_parts .append ('A' )
908+
909+ # otherwise, add the arg to the arg strings
910+ # and note the index if it was an option
911+ else :
912+ option_tuple = self ._parse_optional (arg_string )
913+ if option_tuple is None :
914+ pattern = 'A'
915+ else :
916+ option_string_indices [i ] = option_tuple
917+ pattern = 'O'
918+ arg_string_pattern_parts .append (pattern )
919+
920+ # join the pieces together to form the pattern
921+ arg_strings_pattern = '' .join (arg_string_pattern_parts )
922+
923+ # converts arg strings to the appropriate and then takes the action
924+ seen_actions = set ()
925+ seen_non_default_actions = set ()
926+
927+ def take_action (action , argument_strings , option_string = None ):
928+ seen_actions .add (action )
929+ argument_values = self ._get_values (action , argument_strings )
930+
931+ # error if this argument is not allowed with other previously
932+ # seen arguments, assuming that actions that use the default
933+ # value don't really count as "present"
934+ if argument_values is not action .default :
935+ seen_non_default_actions .add (action )
936+ for conflict_action in action_conflicts .get (action , []):
937+ if conflict_action in seen_non_default_actions :
938+ msg = _ ('not allowed with argument %s' )
939+ action_name = _get_action_name (conflict_action )
940+ raise ArgumentError (action , msg % action_name )
941+
942+ # take the action if we didn't receive a SUPPRESS value
943+ # (e.g. from a default)
944+ if argument_values is not SUPPRESS :
945+ action (self , namespace , argument_values , option_string )
946+
947+ # function to convert arg_strings into an optional action
948+ def consume_optional (start_index ):
949+
950+ # get the optional identified at this index
951+ option_tuple = option_string_indices [start_index ]
952+ action , option_string , explicit_arg = option_tuple
953+
954+ # identify additional optionals in the same arg string
955+ # (e.g. -xyz is the same as -x -y -z if no args are required)
956+ match_argument = self ._match_argument
957+ action_tuples = []
958+ while True :
959+
960+ # if we found no optional action, skip it
961+ if action is None :
962+ extras .append (arg_strings [start_index ])
963+ return start_index + 1
964+
965+ # if there is an explicit argument, try to match the
966+ # optional's string arguments to only this
967+ if explicit_arg is not None :
968+ arg_count = match_argument (action , 'A' )
969+
970+ # if the action is a single-dash option and takes no
971+ # arguments, try to parse more single-dash options out
972+ # of the tail of the option string
973+ chars = self .prefix_chars
974+ if arg_count == 0 and option_string [1 ] not in chars :
975+ action_tuples .append ((action , [], option_string ))
976+ char = option_string [0 ]
977+ option_string = char + explicit_arg [0 ]
978+ new_explicit_arg = explicit_arg [1 :] or None
979+ optionals_map = self ._option_string_actions
980+ if option_string in optionals_map :
981+ action = optionals_map [option_string ]
982+ explicit_arg = new_explicit_arg
983+ else :
984+ msg = _ ('ignored explicit argument %r' )
985+ raise ArgumentError (action , msg % explicit_arg )
986+
987+ # if the action expect exactly one argument, we've
988+ # successfully matched the option; exit the loop
989+ elif arg_count == 1 :
990+ stop = start_index + 1
991+ args = [explicit_arg ]
992+ action_tuples .append ((action , args , option_string ))
993+ break
994+
995+ # error if a double-dash option did not use the
996+ # explicit argument
997+ else :
998+ msg = _ ('ignored explicit argument %r' )
999+ raise ArgumentError (action , msg % explicit_arg )
1000+
1001+ # if there is no explicit argument, try to match the
1002+ # optional's string arguments with the following strings
1003+ # if successful, exit the loop
1004+ else :
1005+ start = start_index + 1
1006+ selected_patterns = arg_strings_pattern [start :]
1007+ arg_count = match_argument (action , selected_patterns )
1008+ stop = start + arg_count
1009+ args = arg_strings [start :stop ]
1010+ action_tuples .append ((action , args , option_string ))
1011+ break
1012+
1013+ # add the Optional to the list and return the index at which
1014+ # the Optional's string args stopped
1015+ assert action_tuples
1016+ for action , args , option_string in action_tuples :
1017+ take_action (action , args , option_string )
1018+ return stop
1019+
1020+ # the list of Positionals left to be parsed; this is modified
1021+ # by consume_positionals()
1022+ positionals = self ._get_positional_actions ()
1023+
1024+ # function to convert arg_strings into positional actions
1025+ def consume_positionals (start_index ):
1026+ # match as many Positionals as possible
1027+ match_partial = self ._match_arguments_partial
1028+ selected_pattern = arg_strings_pattern [start_index :]
1029+ arg_counts = match_partial (positionals , selected_pattern )
1030+
1031+ ####################################################################
1032+ # Applied mixed.patch from https://bugs.python.org/issue15112
1033+ if 'O' in arg_strings_pattern [start_index :]:
1034+ # if there is an optional after this, remove
1035+ # 'empty' positionals from the current match
1036+
1037+ while len (arg_counts ) > 1 and arg_counts [- 1 ] == 0 :
1038+ arg_counts = arg_counts [:- 1 ]
1039+ ####################################################################
1040+
1041+ # slice off the appropriate arg strings for each Positional
1042+ # and add the Positional and its args to the list
1043+ for action , arg_count in zip (positionals , arg_counts ):
1044+ args = arg_strings [start_index : start_index + arg_count ]
1045+ start_index += arg_count
1046+ take_action (action , args )
1047+
1048+ # slice off the Positionals that we just parsed and return the
1049+ # index at which the Positionals' string args stopped
1050+ positionals [:] = positionals [len (arg_counts ):]
1051+ return start_index
1052+
1053+ # consume Positionals and Optionals alternately, until we have
1054+ # passed the last option string
1055+ extras = []
1056+ start_index = 0
1057+ if option_string_indices :
1058+ max_option_string_index = max (option_string_indices )
1059+ else :
1060+ max_option_string_index = - 1
1061+ while start_index <= max_option_string_index :
1062+
1063+ # consume any Positionals preceding the next option
1064+ next_option_string_index = min ([
1065+ index
1066+ for index in option_string_indices
1067+ if index >= start_index ])
1068+ if start_index != next_option_string_index :
1069+ positionals_end_index = consume_positionals (start_index )
1070+
1071+ # only try to parse the next optional if we didn't consume
1072+ # the option string during the positionals parsing
1073+ if positionals_end_index > start_index :
1074+ start_index = positionals_end_index
1075+ continue
1076+ else :
1077+ start_index = positionals_end_index
1078+
1079+ # if we consumed all the positionals we could and we're not
1080+ # at the index of an option string, there were extra arguments
1081+ if start_index not in option_string_indices :
1082+ strings = arg_strings [start_index :next_option_string_index ]
1083+ extras .extend (strings )
1084+ start_index = next_option_string_index
1085+
1086+ # consume the next optional and any arguments for it
1087+ start_index = consume_optional (start_index )
1088+
1089+ # consume any positionals following the last Optional
1090+ stop_index = consume_positionals (start_index )
1091+
1092+ # if we didn't consume all the argument strings, there were extras
1093+ extras .extend (arg_strings [stop_index :])
1094+
1095+ # make sure all required actions were present and also convert
1096+ # action defaults which were not given as arguments
1097+ required_actions = []
1098+ for action in self ._actions :
1099+ if action not in seen_actions :
1100+ if action .required :
1101+ required_actions .append (_get_action_name (action ))
1102+ else :
1103+ # Convert action default now instead of doing it before
1104+ # parsing arguments to avoid calling convert functions
1105+ # twice (which may fail) if the argument was given, but
1106+ # only if it was defined already in the namespace
1107+ if (action .default is not None and
1108+ isinstance (action .default , str ) and
1109+ hasattr (namespace , action .dest ) and
1110+ action .default is getattr (namespace , action .dest )):
1111+ setattr (namespace , action .dest ,
1112+ self ._get_value (action , action .default ))
1113+
1114+ if required_actions :
1115+ self .error (_ ('the following arguments are required: %s' ) %
1116+ ', ' .join (required_actions ))
1117+
1118+ # make sure all required groups had one option present
1119+ for group in self ._mutually_exclusive_groups :
1120+ if group .required :
1121+ for action in group ._group_actions :
1122+ if action in seen_non_default_actions :
1123+ break
1124+
1125+ # if no actions were used, report the error
1126+ else :
1127+ names = [_get_action_name (action )
1128+ for action in group ._group_actions
1129+ if action .help is not SUPPRESS ]
1130+ msg = _ ('one of the arguments %s is required' )
1131+ self .error (msg % ' ' .join (names ))
1132+
1133+ # return the updated namespace and the extra arguments
1134+ return namespace , extras
0 commit comments