Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/argcomplete/packages/_argparse.py: 21%

179 statements  

« prev     ^ index     » next       coverage.py v7.3.1, created at 2023-09-25 06:31 +0000

1# Copyright 2012-2023, Andrey Kislyuk and argcomplete contributors. Licensed under the terms of the 

2# `Apache License, Version 2.0 <http://www.apache.org/licenses/LICENSE-2.0>`_. Distribution of the LICENSE and NOTICE 

3# files with source copies of this package and derivative works is **REQUIRED** as specified by the Apache License. 

4# See https://github.com/kislyuk/argcomplete for more info. 

5 

6# This file contains argparse introspection utilities used in the course of argcomplete execution. 

7 

8from argparse import ( 

9 ONE_OR_MORE, 

10 OPTIONAL, 

11 PARSER, 

12 REMAINDER, 

13 SUPPRESS, 

14 ZERO_OR_MORE, 

15 Action, 

16 ArgumentError, 

17 ArgumentParser, 

18 _get_action_name, 

19 _SubParsersAction, 

20) 

21from gettext import gettext 

22from typing import Dict, List, Set, Tuple 

23 

24_num_consumed_args: Dict[Action, int] = {} 

25 

26 

27def action_is_satisfied(action): 

28 '''Returns False if the parse would raise an error if no more arguments are given to this action, True otherwise.''' 

29 num_consumed_args = _num_consumed_args.get(action, 0) 

30 

31 if action.nargs in [OPTIONAL, ZERO_OR_MORE, REMAINDER]: 

32 return True 

33 if action.nargs == ONE_OR_MORE: 

34 return num_consumed_args >= 1 

35 if action.nargs == PARSER: 

36 # Not sure what this should be, but this previously always returned False 

37 # so at least this won't break anything that wasn't already broken. 

38 return False 

39 if action.nargs is None: 

40 return num_consumed_args == 1 

41 

42 assert isinstance(action.nargs, int), 'failed to handle a possible nargs value: %r' % action.nargs 

43 return num_consumed_args == action.nargs 

44 

45 

46def action_is_open(action): 

47 '''Returns True if action could consume more arguments (i.e., its pattern is open).''' 

48 num_consumed_args = _num_consumed_args.get(action, 0) 

49 

50 if action.nargs in [ZERO_OR_MORE, ONE_OR_MORE, PARSER, REMAINDER]: 

51 return True 

52 if action.nargs == OPTIONAL or action.nargs is None: 

53 return num_consumed_args == 0 

54 

55 assert isinstance(action.nargs, int), 'failed to handle a possible nargs value: %r' % action.nargs 

56 return num_consumed_args < action.nargs 

57 

58 

59def action_is_greedy(action, isoptional=False): 

60 '''Returns True if action will necessarily consume the next argument. 

61 isoptional indicates whether the argument is an optional (starts with -). 

62 ''' 

63 num_consumed_args = _num_consumed_args.get(action, 0) 

64 

65 if action.option_strings: 

66 if not isoptional and not action_is_satisfied(action): 

67 return True 

68 return action.nargs == REMAINDER 

69 else: 

70 return action.nargs == REMAINDER and num_consumed_args >= 1 

71 

72 

73class IntrospectiveArgumentParser(ArgumentParser): 

74 '''The following is a verbatim copy of ArgumentParser._parse_known_args (Python 2.7.3), 

75 except for the lines that contain the string "Added by argcomplete". 

76 ''' 

77 

78 def _parse_known_args(self, arg_strings, namespace): 

79 _num_consumed_args.clear() # Added by argcomplete 

80 self._argcomplete_namespace = namespace 

81 self.active_actions: List[Action] = [] # Added by argcomplete 

82 # replace arg strings that are file references 

83 if self.fromfile_prefix_chars is not None: 

84 arg_strings = self._read_args_from_files(arg_strings) 

85 

86 # map all mutually exclusive arguments to the other arguments 

87 # they can't occur with 

88 action_conflicts: Dict[Action, List[Action]] = {} 

89 self._action_conflicts = action_conflicts # Added by argcomplete 

90 for mutex_group in self._mutually_exclusive_groups: 

91 group_actions = mutex_group._group_actions 

92 for i, mutex_action in enumerate(mutex_group._group_actions): 

93 conflicts = action_conflicts.setdefault(mutex_action, []) 

94 conflicts.extend(group_actions[:i]) 

95 conflicts.extend(group_actions[i + 1 :]) 

96 

97 # find all option indices, and determine the arg_string_pattern 

98 # which has an 'O' if there is an option at an index, 

99 # an 'A' if there is an argument, or a '-' if there is a '--' 

100 option_string_indices = {} 

101 arg_string_pattern_parts = [] 

102 arg_strings_iter = iter(arg_strings) 

103 for i, arg_string in enumerate(arg_strings_iter): 

104 # all args after -- are non-options 

105 if arg_string == '--': 

106 arg_string_pattern_parts.append('-') 

107 for arg_string in arg_strings_iter: 

108 arg_string_pattern_parts.append('A') 

109 

110 # otherwise, add the arg to the arg strings 

111 # and note the index if it was an option 

112 else: 

113 option_tuple = self._parse_optional(arg_string) 

114 if option_tuple is None: 

115 pattern = 'A' 

116 else: 

117 option_string_indices[i] = option_tuple 

118 pattern = 'O' 

119 arg_string_pattern_parts.append(pattern) 

120 

121 # join the pieces together to form the pattern 

122 arg_strings_pattern = ''.join(arg_string_pattern_parts) 

123 

124 # converts arg strings to the appropriate and then takes the action 

125 seen_actions: Set[Action] = set() 

126 seen_non_default_actions: Set[Action] = set() 

127 self._seen_non_default_actions = seen_non_default_actions # Added by argcomplete 

128 

129 def take_action(action, argument_strings, option_string=None): 

130 seen_actions.add(action) 

131 argument_values = self._get_values(action, argument_strings) 

132 

133 # error if this argument is not allowed with other previously 

134 # seen arguments, assuming that actions that use the default 

135 # value don't really count as "present" 

136 if argument_values is not action.default: 

137 seen_non_default_actions.add(action) 

138 for conflict_action in action_conflicts.get(action, []): 

139 if conflict_action in seen_non_default_actions: 

140 msg = gettext('not allowed with argument %s') 

141 action_name = _get_action_name(conflict_action) 

142 raise ArgumentError(action, msg % action_name) 

143 

144 # take the action if we didn't receive a SUPPRESS value 

145 # (e.g. from a default) 

146 if argument_values is not SUPPRESS or isinstance(action, _SubParsersAction): 

147 try: 

148 action(self, namespace, argument_values, option_string) 

149 except BaseException: 

150 # Begin added by argcomplete 

151 # When a subparser action is taken and fails due to incomplete arguments, it does not merge the 

152 # contents of its parsed namespace into the parent namespace. Do that here to allow completers to 

153 # access the partially parsed arguments for the subparser. 

154 if isinstance(action, _SubParsersAction): 

155 subnamespace = action._name_parser_map[argument_values[0]]._argcomplete_namespace 

156 for key, value in vars(subnamespace).items(): 

157 setattr(namespace, key, value) 

158 # End added by argcomplete 

159 raise 

160 

161 # function to convert arg_strings into an optional action 

162 def consume_optional(start_index): 

163 # get the optional identified at this index 

164 option_tuple = option_string_indices[start_index] 

165 action, option_string, explicit_arg = option_tuple 

166 

167 # identify additional optionals in the same arg string 

168 # (e.g. -xyz is the same as -x -y -z if no args are required) 

169 match_argument = self._match_argument 

170 action_tuples: List[Tuple[Action, List[str], str]] = [] 

171 while True: 

172 # if we found no optional action, skip it 

173 if action is None: 

174 extras.append(arg_strings[start_index]) 

175 return start_index + 1 

176 

177 # if there is an explicit argument, try to match the 

178 # optional's string arguments to only this 

179 if explicit_arg is not None: 

180 arg_count = match_argument(action, 'A') 

181 

182 # if the action is a single-dash option and takes no 

183 # arguments, try to parse more single-dash options out 

184 # of the tail of the option string 

185 chars = self.prefix_chars 

186 if arg_count == 0 and option_string[1] not in chars: 

187 action_tuples.append((action, [], option_string)) 

188 char = option_string[0] 

189 option_string = char + explicit_arg[0] 

190 new_explicit_arg = explicit_arg[1:] or None 

191 optionals_map = self._option_string_actions 

192 if option_string in optionals_map: 

193 action = optionals_map[option_string] 

194 explicit_arg = new_explicit_arg 

195 else: 

196 msg = gettext('ignored explicit argument %r') 

197 raise ArgumentError(action, msg % explicit_arg) 

198 

199 # if the action expect exactly one argument, we've 

200 # successfully matched the option; exit the loop 

201 elif arg_count == 1: 

202 stop = start_index + 1 

203 args = [explicit_arg] 

204 action_tuples.append((action, args, option_string)) 

205 break 

206 

207 # error if a double-dash option did not use the 

208 # explicit argument 

209 else: 

210 msg = gettext('ignored explicit argument %r') 

211 raise ArgumentError(action, msg % explicit_arg) 

212 

213 # if there is no explicit argument, try to match the 

214 # optional's string arguments with the following strings 

215 # if successful, exit the loop 

216 else: 

217 start = start_index + 1 

218 selected_patterns = arg_strings_pattern[start:] 

219 self.active_actions = [action] # Added by argcomplete 

220 _num_consumed_args[action] = 0 # Added by argcomplete 

221 arg_count = match_argument(action, selected_patterns) 

222 stop = start + arg_count 

223 args = arg_strings[start:stop] 

224 

225 # Begin added by argcomplete 

226 # If the pattern is not open (e.g. no + at the end), remove the action from active actions (since 

227 # it wouldn't be able to consume any more args) 

228 _num_consumed_args[action] = len(args) 

229 if not action_is_open(action): 

230 self.active_actions.remove(action) 

231 # End added by argcomplete 

232 

233 action_tuples.append((action, args, option_string)) 

234 break 

235 

236 # add the Optional to the list and return the index at which 

237 # the Optional's string args stopped 

238 assert action_tuples 

239 for action, args, option_string in action_tuples: 

240 take_action(action, args, option_string) 

241 return stop 

242 

243 # the list of Positionals left to be parsed; this is modified 

244 # by consume_positionals() 

245 positionals = self._get_positional_actions() 

246 

247 # function to convert arg_strings into positional actions 

248 def consume_positionals(start_index): 

249 # match as many Positionals as possible 

250 match_partial = self._match_arguments_partial 

251 selected_pattern = arg_strings_pattern[start_index:] 

252 arg_counts = match_partial(positionals, selected_pattern) 

253 

254 # slice off the appropriate arg strings for each Positional 

255 # and add the Positional and its args to the list 

256 for action, arg_count in zip(positionals, arg_counts): # Added by argcomplete 

257 self.active_actions.append(action) # Added by argcomplete 

258 for action, arg_count in zip(positionals, arg_counts): 

259 args = arg_strings[start_index : start_index + arg_count] 

260 start_index += arg_count 

261 _num_consumed_args[action] = len(args) # Added by argcomplete 

262 take_action(action, args) 

263 

264 # slice off the Positionals that we just parsed and return the 

265 # index at which the Positionals' string args stopped 

266 positionals[:] = positionals[len(arg_counts) :] 

267 return start_index 

268 

269 # consume Positionals and Optionals alternately, until we have 

270 # passed the last option string 

271 extras = [] 

272 start_index = 0 

273 if option_string_indices: 

274 max_option_string_index = max(option_string_indices) 

275 else: 

276 max_option_string_index = -1 

277 while start_index <= max_option_string_index: 

278 # consume any Positionals preceding the next option 

279 next_option_string_index = min([index for index in option_string_indices if index >= start_index]) 

280 if start_index != next_option_string_index: 

281 positionals_end_index = consume_positionals(start_index) 

282 

283 # only try to parse the next optional if we didn't consume 

284 # the option string during the positionals parsing 

285 if positionals_end_index > start_index: 

286 start_index = positionals_end_index 

287 continue 

288 else: 

289 start_index = positionals_end_index 

290 

291 # if we consumed all the positionals we could and we're not 

292 # at the index of an option string, there were extra arguments 

293 if start_index not in option_string_indices: 

294 strings = arg_strings[start_index:next_option_string_index] 

295 extras.extend(strings) 

296 start_index = next_option_string_index 

297 

298 # consume the next optional and any arguments for it 

299 start_index = consume_optional(start_index) 

300 

301 # consume any positionals following the last Optional 

302 stop_index = consume_positionals(start_index) 

303 

304 # if we didn't consume all the argument strings, there were extras 

305 extras.extend(arg_strings[stop_index:]) 

306 

307 # if we didn't use all the Positional objects, there were too few 

308 # arg strings supplied. 

309 

310 if positionals: 

311 self.active_actions.append(positionals[0]) # Added by argcomplete 

312 self.error(gettext('too few arguments')) 

313 

314 # make sure all required actions were present 

315 for action in self._actions: 

316 if action.required: 

317 if action not in seen_actions: 

318 name = _get_action_name(action) 

319 self.error(gettext('argument %s is required') % name) 

320 

321 # make sure all required groups had one option present 

322 for group in self._mutually_exclusive_groups: 

323 if group.required: 

324 for action in group._group_actions: 

325 if action in seen_non_default_actions: 

326 break 

327 

328 # if no actions were used, report the error 

329 else: 

330 names = [ 

331 str(_get_action_name(action)) for action in group._group_actions if action.help is not SUPPRESS 

332 ] 

333 msg = gettext('one of the arguments %s is required') 

334 self.error(msg % ' '.join(names)) 

335 

336 # return the updated namespace and the extra arguments 

337 return namespace, extras