Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/argcomplete/finders.py: 17%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

356 statements  

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 

6from __future__ import annotations 

7 

8import argparse 

9import os 

10import sys 

11from collections.abc import Callable, Container, Mapping 

12from typing import Literal, TextIO 

13 

14from . import io as _io 

15from .completers import BaseCompleter, ChoicesCompleter, FilesCompleter, SuppressCompleter 

16from .io import debug, mute_stderr 

17from .lexers import split_line 

18from .packages._argparse import IntrospectiveArgumentParser, action_is_greedy, action_is_open, action_is_satisfied 

19 

20safe_actions = { 

21 argparse._StoreAction, 

22 argparse._StoreConstAction, 

23 argparse._StoreTrueAction, 

24 argparse._StoreFalseAction, 

25 argparse._AppendAction, 

26 argparse._AppendConstAction, 

27 argparse._CountAction, 

28} 

29 

30 

31def default_validator(completion: str, prefix: str) -> bool: 

32 return completion.startswith(prefix) 

33 

34 

35class CompletionFinder: 

36 """ 

37 Inherit from this class if you wish to override any of the stages below. Otherwise, use 

38 ``argcomplete.autocomplete()`` directly (it's a convenience instance of this class). It has the same signature as 

39 :meth:`CompletionFinder.__call__()`. 

40 """ 

41 

42 _parser: argparse.ArgumentParser | None 

43 _formatter: argparse.HelpFormatter | None 

44 always_complete_options: bool | Literal["long", "short"] 

45 exclude: Container[str] | None 

46 validator: Callable[[str, str], bool] 

47 print_suppressed: bool 

48 completing: bool 

49 _display_completions: dict[str, str] 

50 default_completer: BaseCompleter 

51 append_space: bool 

52 

53 active_parsers: list[argparse.ArgumentParser] 

54 visited_positionals: list[argparse.Action] 

55 

56 def __init__( 

57 self, 

58 argument_parser: argparse.ArgumentParser | None = None, 

59 always_complete_options: bool | Literal["long", "short"] = True, 

60 exclude: Container[str] | None = None, 

61 validator: Callable[[str, str], bool] | None = None, 

62 print_suppressed: bool = False, 

63 default_completer: BaseCompleter = FilesCompleter(), 

64 append_space: bool | None = None, 

65 ) -> None: 

66 self._parser = argument_parser # type: ignore[assignment] 

67 self._formatter = None 

68 self.always_complete_options = always_complete_options 

69 self.exclude = exclude 

70 if validator is None: 

71 validator = default_validator 

72 self.validator = validator 

73 self.print_suppressed = print_suppressed 

74 self.completing = False 

75 self._display_completions = {} 

76 self.default_completer = default_completer 

77 if append_space is None: 

78 append_space = os.environ.get("_ARGCOMPLETE_SUPPRESS_SPACE") != "1" 

79 self.append_space = append_space 

80 

81 def __call__( 

82 self, 

83 argument_parser: argparse.ArgumentParser, 

84 always_complete_options: bool | str = True, 

85 exit_method: Callable = os._exit, 

86 output_stream: TextIO | None = None, 

87 exclude: Container[str] | None = None, 

88 validator: Callable[[str, str], bool] | None = None, 

89 print_suppressed: bool = False, 

90 append_space: bool | None = None, 

91 default_completer: BaseCompleter = FilesCompleter(), 

92 ) -> None: 

93 """ 

94 :param argument_parser: The argument parser to autocomplete on 

95 :param always_complete_options: 

96 Controls the autocompletion of option strings if an option string opening character (normally ``-``) has not 

97 been entered. If ``True`` (default), both short (``-x``) and long (``--x``) option strings will be 

98 suggested. If ``False``, no option strings will be suggested. If ``long``, long options and short options 

99 with no long variant will be suggested. If ``short``, short options and long options with no short variant 

100 will be suggested. 

101 :param exit_method: 

102 Method used to stop the program after printing completions. Defaults to :meth:`os._exit`. If you want to 

103 perform a normal exit that calls exit handlers, use :meth:`sys.exit`. 

104 :param exclude: List of strings representing options to be omitted from autocompletion 

105 :param validator: 

106 Function to filter all completions through before returning (called with two string arguments, completion 

107 and prefix; return value is evaluated as a boolean) 

108 :param print_suppressed: 

109 Whether or not to autocomplete options that have the ``help=argparse.SUPPRESS`` keyword argument set. 

110 :param append_space: 

111 Whether to append a space to unique matches. The default is ``True``. 

112 

113 .. note:: 

114 If you are not subclassing CompletionFinder to override its behaviors, 

115 use :meth:`argcomplete.autocomplete()` directly. It has the same signature as this method. 

116 

117 Produces tab completions for ``argument_parser``. See module docs for more info. 

118 

119 Argcomplete only executes actions if their class is known not to have side effects. Custom action classes can be 

120 added to argcomplete.safe_actions, if their values are wanted in the ``parsed_args`` completer argument, or 

121 their execution is otherwise desirable. 

122 """ 

123 self.__init__( # type: ignore 

124 argument_parser, 

125 always_complete_options=always_complete_options, 

126 exclude=exclude, 

127 validator=validator, 

128 print_suppressed=print_suppressed, 

129 append_space=append_space, 

130 default_completer=default_completer, 

131 ) 

132 

133 if "_ARGCOMPLETE" not in os.environ: 

134 # not an argument completion invocation 

135 return 

136 

137 self._init_debug_stream() 

138 

139 if output_stream is None: 

140 filename = os.environ.get("_ARGCOMPLETE_STDOUT_FILENAME") 

141 if filename is not None: 

142 debug(f"Using output file {filename}") 

143 output_stream = open(filename, "w") 

144 

145 if output_stream is None: 

146 try: 

147 output_stream = os.fdopen(8, "w") 

148 except Exception: 

149 debug("Unable to open fd 8 for writing, quitting") 

150 exit_method(1) 

151 

152 assert output_stream is not None 

153 

154 ifs = os.environ.get("_ARGCOMPLETE_IFS", "\013") 

155 if len(ifs) != 1: 

156 debug(f"Invalid value for IFS, quitting [{ifs}]") 

157 exit_method(1) 

158 

159 dfs = os.environ.get("_ARGCOMPLETE_DFS") 

160 if dfs and len(dfs) != 1: 

161 debug(f"Invalid value for DFS, quitting [{dfs}]") 

162 exit_method(1) 

163 

164 comp_line = os.environ["COMP_LINE"] 

165 comp_point = int(os.environ["COMP_POINT"]) 

166 

167 cword_prequote, cword_prefix, cword_suffix, comp_words, last_wordbreak_pos = split_line(comp_line, comp_point) 

168 

169 # _ARGCOMPLETE is set by the shell script to tell us where comp_words 

170 # should start, based on what we're completing. 

171 # 1: <script> [args] 

172 # 2: python <script> [args] 

173 # 3: python -m <module> [args] 

174 start = int(os.environ["_ARGCOMPLETE"]) - 1 

175 comp_words = comp_words[start:] 

176 

177 assert self._parser is not None 

178 if cword_prefix and cword_prefix[0] in self._parser.prefix_chars and "=" in cword_prefix: 

179 # Special case for when the current word is "--optional=PARTIAL_VALUE". Give the optional to the parser. 

180 comp_words.append(cword_prefix.split("=", 1)[0]) 

181 

182 debug( 

183 f"\nLINE: {comp_line!r}", 

184 f"\nPOINT: {comp_point!r}", 

185 f"\nPREQUOTE: {cword_prequote!r}", 

186 f"\nPREFIX: {cword_prefix!r}", 

187 f"\nSUFFIX: {cword_suffix!r}", 

188 "\nWORDS:", 

189 comp_words, 

190 ) 

191 

192 completions = self._get_completions(comp_words, cword_prefix, cword_prequote, last_wordbreak_pos) 

193 

194 if dfs: 

195 display_completions = { 

196 key: value.replace(ifs, " ") if value else "" for key, value in self._display_completions.items() 

197 } 

198 completions = [dfs.join((key, display_completions.get(key) or "")) for key in completions] 

199 

200 if os.environ.get("_ARGCOMPLETE_SHELL") == "zsh": 

201 completions = [f"{c}:{self._display_completions.get(c)}" for c in completions] 

202 

203 debug("\nReturning completions:", completions) 

204 output_stream.write(ifs.join(completions)) 

205 output_stream.flush() 

206 _io.debug_stream.flush() 

207 exit_method(0) 

208 

209 def _init_debug_stream(self): 

210 """Initialize debug output stream 

211 

212 By default, writes to file descriptor 9, or stderr if that fails. 

213 This can be overridden by derived classes, for example to avoid 

214 clashes with file descriptors being used elsewhere (such as in pytest). 

215 """ 

216 try: 

217 _io.debug_stream = os.fdopen(9, "w") 

218 except Exception: 

219 _io.debug_stream = sys.stderr 

220 debug() 

221 

222 def _get_completions(self, comp_words, cword_prefix, cword_prequote, last_wordbreak_pos): 

223 active_parsers = self._patch_argument_parser() 

224 

225 parsed_args = argparse.Namespace() 

226 self.completing = True 

227 

228 try: 

229 debug("invoking parser with", comp_words[1:]) 

230 with mute_stderr(): 

231 assert self._parser is not None 

232 a = self._parser.parse_known_args(comp_words[1:], namespace=parsed_args) 

233 debug("parsed args:", a) 

234 except BaseException as e: 

235 debug("\nexception", type(e), str(e), "while parsing args") 

236 

237 self.completing = False 

238 

239 if "--" in comp_words: 

240 self.always_complete_options = False 

241 

242 completions = self.collect_completions(active_parsers, parsed_args, cword_prefix) 

243 completions = self.filter_completions(completions) 

244 completions = self.quote_completions(completions, cword_prequote, last_wordbreak_pos) 

245 return completions 

246 

247 def _patch_argument_parser(self): 

248 """ 

249 Since argparse doesn't support much introspection, we monkey-patch it to replace the parse_known_args method and 

250 all actions with hooks that tell us which action was last taken or about to be taken, and let us have the parser 

251 figure out which subparsers need to be activated (then recursively monkey-patch those). 

252 We save all active ArgumentParsers to extract all their possible option names later. 

253 """ 

254 self.active_parsers = [] 

255 self.visited_positionals = [] 

256 

257 completer = self 

258 

259 def patch(parser): 

260 completer.visited_positionals.append(parser) 

261 completer.active_parsers.append(parser) 

262 

263 if isinstance(parser, IntrospectiveArgumentParser): 

264 return 

265 

266 classname = "MonkeyPatchedIntrospectiveArgumentParser" 

267 

268 parser.__class__ = type(classname, (IntrospectiveArgumentParser, parser.__class__), {}) 

269 

270 for action in parser._actions: 

271 if hasattr(action, "_orig_class"): 

272 continue 

273 

274 # TODO: accomplish this with super 

275 class IntrospectAction(action.__class__): # type: ignore 

276 def __call__(self, parser, namespace, values, option_string=None): 

277 debug("Action stub called on", self) 

278 debug("\targs:", parser, namespace, values, option_string) 

279 debug("\torig class:", self._orig_class) 

280 debug("\torig callable:", self._orig_callable) 

281 

282 if not completer.completing: 

283 self._orig_callable(parser, namespace, values, option_string=option_string) 

284 elif issubclass(self._orig_class, argparse._SubParsersAction): 

285 debug("orig class is a subparsers action: patching and running it") 

286 patch(self._name_parser_map[values[0]]) 

287 self._orig_callable(parser, namespace, values, option_string=option_string) 

288 elif self._orig_class in safe_actions: 

289 if not self.option_strings: 

290 completer.visited_positionals.append(self) 

291 

292 self._orig_callable(parser, namespace, values, option_string=option_string) 

293 

294 action._orig_class = action.__class__ 

295 action._orig_callable = action.__call__ 

296 action.__class__ = IntrospectAction 

297 

298 patch(self._parser) 

299 

300 debug("Active parsers:", self.active_parsers) 

301 debug("Visited positionals:", self.visited_positionals) 

302 

303 return self.active_parsers 

304 

305 def _get_action_help(self, action): 

306 if action.help is None: 

307 return "" 

308 if "%" not in action.help: 

309 return action.help 

310 formatters = self._formatter 

311 if formatters is None: 

312 assert self._parser is not None 

313 self._formatter = formatters = self._parser.formatter_class(prog=self._parser.prog) 

314 return formatters._expand_help(action) 

315 

316 def _get_subparser_completions(self, parser, cword_prefix): 

317 aliases_by_parser: dict[argparse.ArgumentParser, list[str]] = {} 

318 for key in parser.choices: 

319 p = parser.choices[key] 

320 aliases_by_parser.setdefault(p, []).append(key) 

321 

322 for action in parser._get_subactions(): 

323 for alias in aliases_by_parser[parser.choices[action.dest]]: 

324 if alias.startswith(cword_prefix): 

325 self._display_completions[alias] = self._get_action_help(action) 

326 

327 completions = [subcmd for subcmd in parser.choices if subcmd.startswith(cword_prefix)] 

328 return completions 

329 

330 def _include_options(self, action, cword_prefix): 

331 if len(cword_prefix) > 0 or self.always_complete_options is True: 

332 return [opt for opt in action.option_strings if opt.startswith(cword_prefix)] 

333 long_opts = [opt for opt in action.option_strings if len(opt) > 2] 

334 short_opts = [opt for opt in action.option_strings if len(opt) <= 2] 

335 if self.always_complete_options == "long": 

336 return long_opts if long_opts else short_opts 

337 elif self.always_complete_options == "short": 

338 return short_opts if short_opts else long_opts 

339 return [] 

340 

341 def _get_option_completions(self, parser, cword_prefix): 

342 for action in parser._actions: 

343 if action.option_strings: 

344 for option_string in action.option_strings: 

345 if option_string.startswith(cword_prefix): 

346 self._display_completions[option_string] = self._get_action_help(action) 

347 

348 option_completions = [] 

349 for action in parser._actions: 

350 if not self.print_suppressed: 

351 completer = getattr(action, "completer", None) 

352 if isinstance(completer, SuppressCompleter) and completer.suppress(): 

353 continue 

354 if action.help == argparse.SUPPRESS: 

355 continue 

356 if not self._action_allowed(action, parser): 

357 continue 

358 if not isinstance(action, argparse._SubParsersAction): 

359 option_completions += self._include_options(action, cword_prefix) 

360 return option_completions 

361 

362 @staticmethod 

363 def _action_allowed(action, parser): 

364 # Logic adapted from take_action in ArgumentParser._parse_known_args 

365 # (members are saved by vendor._argparse.IntrospectiveArgumentParser) 

366 for conflict_action in parser._action_conflicts.get(action, []): 

367 if conflict_action in parser._seen_non_default_actions: 

368 return False 

369 return True 

370 

371 def _complete_active_option(self, parser, next_positional, cword_prefix, parsed_args, completions): 

372 debug(f"Active actions (L={len(parser.active_actions)}): {parser.active_actions}") 

373 

374 isoptional = cword_prefix and cword_prefix[0] in parser.prefix_chars 

375 optional_prefix = "" 

376 greedy_actions = [x for x in parser.active_actions if action_is_greedy(x, isoptional)] 

377 if greedy_actions: 

378 assert len(greedy_actions) == 1, "expect at most 1 greedy action" 

379 # This means the action will fail to parse if the word under the cursor is not given 

380 # to it, so give it exclusive control over completions (flush previous completions) 

381 debug("Resetting completions because", greedy_actions[0], "must consume the next argument") 

382 self._display_completions = {} 

383 completions = [] 

384 elif isoptional: 

385 if "=" in cword_prefix: 

386 # Special case for when the current word is "--optional=PARTIAL_VALUE". 

387 # The completer runs on PARTIAL_VALUE. The prefix is added back to the completions 

388 # (and chopped back off later in quote_completions() by the COMP_WORDBREAKS logic). 

389 optional_prefix, _, cword_prefix = cword_prefix.partition("=") 

390 else: 

391 # Only run completers if current word does not start with - (is not an optional) 

392 return completions 

393 

394 complete_remaining_positionals = False 

395 # Use the single greedy action (if there is one) or all active actions. 

396 for active_action in greedy_actions or parser.active_actions: 

397 if not active_action.option_strings: # action is a positional 

398 if action_is_open(active_action): 

399 # Any positional arguments after this may slide down into this action 

400 # if more arguments are added (since the user may not be done yet), 

401 # so it is extremely difficult to tell which completers to run. 

402 # Running all remaining completers will probably show more than the user wants 

403 # but it also guarantees we won't miss anything. 

404 complete_remaining_positionals = True 

405 if not complete_remaining_positionals: 

406 if action_is_satisfied(active_action) and not action_is_open(active_action): 

407 debug("Skipping", active_action) 

408 continue 

409 

410 debug("Activating completion for", active_action, active_action._orig_class) 

411 # completer = getattr(active_action, "completer", DefaultCompleter()) 

412 completer = getattr(active_action, "completer", None) 

413 

414 if completer is None: 

415 if active_action.choices is not None and not isinstance(active_action, argparse._SubParsersAction): 

416 completer = ChoicesCompleter(active_action.choices) 

417 elif not isinstance(active_action, argparse._SubParsersAction): 

418 completer = self.default_completer 

419 

420 if completer: 

421 if isinstance(completer, SuppressCompleter) and completer.suppress(): 

422 continue 

423 

424 if callable(completer): 

425 completer_output = completer( 

426 prefix=cword_prefix, action=active_action, parser=parser, parsed_args=parsed_args 

427 ) 

428 if isinstance(completer_output, Mapping): 

429 for completion, description in completer_output.items(): 

430 if self.validator(completion, cword_prefix): 

431 completions.append(completion) 

432 self._display_completions[completion] = description 

433 else: 

434 for completion in completer_output: 

435 if self.validator(completion, cword_prefix): 

436 completions.append(completion) 

437 if isinstance(completer, ChoicesCompleter): 

438 self._display_completions[completion] = self._get_action_help(active_action) 

439 else: 

440 self._display_completions[completion] = "" 

441 else: 

442 debug("Completer is not callable, trying the readline completer protocol instead") 

443 for i in range(9999): 

444 next_completion = completer.complete(cword_prefix, i) # type: ignore 

445 if next_completion is None: 

446 break 

447 if self.validator(next_completion, cword_prefix): 

448 self._display_completions[next_completion] = "" 

449 completions.append(next_completion) 

450 if optional_prefix: 

451 completions = [optional_prefix + "=" + completion for completion in completions] 

452 debug("Completions:", completions) 

453 return completions 

454 

455 def collect_completions( 

456 self, active_parsers: list[argparse.ArgumentParser], parsed_args: argparse.Namespace, cword_prefix: str 

457 ) -> list[str]: 

458 """ 

459 Visits the active parsers and their actions, executes their completers or introspects them to collect their 

460 option strings. Returns the resulting completions as a list of strings. 

461 

462 This method is exposed for overriding in subclasses; there is no need to use it directly. 

463 """ 

464 completions: list[str] = [] 

465 

466 debug("all active parsers:", active_parsers) 

467 active_parser = active_parsers[-1] 

468 debug("active_parser:", active_parser) 

469 if self.always_complete_options or (len(cword_prefix) > 0 and cword_prefix[0] in active_parser.prefix_chars): 

470 completions += self._get_option_completions(active_parser, cword_prefix) 

471 debug("optional options:", completions) 

472 

473 next_positional = self._get_next_positional() 

474 debug("next_positional:", next_positional) 

475 

476 if isinstance(next_positional, argparse._SubParsersAction): 

477 completions += self._get_subparser_completions(next_positional, cword_prefix) 

478 

479 completions = self._complete_active_option( 

480 active_parser, next_positional, cword_prefix, parsed_args, completions 

481 ) 

482 debug("active options:", completions) 

483 debug("display completions:", self._display_completions) 

484 

485 return completions 

486 

487 def _get_next_positional(self): 

488 """ 

489 Get the next positional action if it exists. 

490 """ 

491 active_parser = self.active_parsers[-1] 

492 last_positional = self.visited_positionals[-1] 

493 

494 all_positionals = active_parser._get_positional_actions() 

495 if not all_positionals: 

496 return None 

497 

498 if active_parser == last_positional: 

499 return all_positionals[0] 

500 

501 i = 0 

502 for i in range(len(all_positionals)): 

503 if all_positionals[i] == last_positional: 

504 break 

505 

506 if i + 1 < len(all_positionals): 

507 return all_positionals[i + 1] 

508 

509 return None 

510 

511 def filter_completions(self, completions: list[str]) -> list[str]: 

512 """ 

513 De-duplicates completions and excludes those specified by ``exclude``. 

514 Returns the filtered completions as a list. 

515 

516 This method is exposed for overriding in subclasses; there is no need to use it directly. 

517 """ 

518 filtered_completions = [] 

519 for completion in completions: 

520 if self.exclude is not None and completion in self.exclude: 

521 continue 

522 if completion not in filtered_completions: 

523 filtered_completions.append(completion) 

524 return filtered_completions 

525 

526 def quote_completions( 

527 self, completions: list[str], cword_prequote: str, last_wordbreak_pos: int | None 

528 ) -> list[str]: 

529 """ 

530 If the word under the cursor started with a quote (as indicated by a nonempty ``cword_prequote``), escapes 

531 occurrences of that quote character in the completions, and adds the quote to the beginning of each completion. 

532 Otherwise, escapes all characters that bash splits words on (``COMP_WORDBREAKS``), and removes portions of 

533 completions before the first colon if (``COMP_WORDBREAKS``) contains a colon. 

534 

535 If there is only one completion, and it doesn't end with a **continuation character** (``/``, ``:``, or ``=``), 

536 adds a space after the completion. 

537 

538 This method is exposed for overriding in subclasses; there is no need to use it directly. 

539 """ 

540 special_chars = "\\" 

541 # If the word under the cursor was quoted, escape the quote char. 

542 # Otherwise, escape all special characters and specially handle all COMP_WORDBREAKS chars. 

543 if cword_prequote == "": 

544 # Bash mangles completions which contain characters in COMP_WORDBREAKS. 

545 # This workaround has the same effect as __ltrim_colon_completions in bash_completion 

546 # (extended to characters other than the colon). 

547 if last_wordbreak_pos is not None: 

548 completions = [c[last_wordbreak_pos + 1 :] for c in completions] 

549 special_chars += "();<>|&!`$*?[]{} \t\n\"'" 

550 elif cword_prequote == '"': 

551 special_chars += '"`$!' 

552 

553 if os.environ.get("_ARGCOMPLETE_SHELL") in ("tcsh", "fish"): 

554 # tcsh and fish escapes special characters itself. 

555 special_chars = "" 

556 elif cword_prequote == "'": 

557 # Nothing can be escaped in single quotes, so we need to close 

558 # the string, escape the single quote, then open a new string. 

559 special_chars = "" 

560 completions = [c.replace("'", r"'\''") for c in completions] 

561 

562 # PowerShell uses ` as escape character. 

563 if os.environ.get("_ARGCOMPLETE_SHELL") == "powershell": 

564 escape_char = '`' 

565 special_chars = special_chars.replace('`', '') 

566 else: 

567 escape_char = "\\" 

568 if os.environ.get("_ARGCOMPLETE_SHELL") == "zsh": 

569 # zsh uses colon as a separator between a completion and its description. 

570 special_chars += ":" 

571 

572 escaped_completions = [] 

573 for completion in completions: 

574 escaped_completion = completion 

575 for char in special_chars: 

576 escaped_completion = escaped_completion.replace(char, escape_char + char) 

577 escaped_completions.append(escaped_completion) 

578 if completion in self._display_completions: 

579 self._display_completions[escaped_completion] = self._display_completions[completion] 

580 

581 if self.append_space: 

582 # Similar functionality in bash was previously turned off by supplying the "-o nospace" option to complete. 

583 # Now it is conditionally disabled using "compopt -o nospace" if the match ends in a continuation character. 

584 # This code is retained for environments where this isn't done natively. 

585 continuation_chars = "=/:" 

586 if len(escaped_completions) == 1 and escaped_completions[0][-1] not in continuation_chars: 

587 if cword_prequote == "": 

588 escaped_completions[0] += " " 

589 

590 return escaped_completions 

591 

592 def rl_complete(self, text: str, state: int) -> str | None: 

593 """ 

594 Alternate entry point for using the argcomplete completer in a readline-based REPL. See also 

595 `rlcompleter <https://docs.python.org/3/library/rlcompleter.html#completer-objects>`_. 

596 Usage: 

597 

598 .. code-block:: python 

599 

600 import argcomplete, argparse, readline 

601 parser = argparse.ArgumentParser() 

602 ... 

603 completer = argcomplete.CompletionFinder(parser) 

604 readline.set_completer_delims("") 

605 readline.set_completer(completer.rl_complete) 

606 readline.parse_and_bind("tab: complete") 

607 result = input("prompt> ") 

608 """ 

609 if state == 0: 

610 cword_prequote, cword_prefix, _cword_suffix, comp_words, first_colon_pos = split_line(text) 

611 comp_words.insert(0, sys.argv[0]) 

612 matches = self._get_completions(comp_words, cword_prefix, cword_prequote, first_colon_pos) 

613 self._rl_matches = [text + match[len(cword_prefix) :] for match in matches] 

614 

615 if state < len(self._rl_matches): 

616 return self._rl_matches[state] 

617 else: 

618 return None 

619 

620 def get_display_completions(self) -> dict[str, str]: 

621 """ 

622 This function returns a mapping of completions to their help strings for displaying to the user. 

623 """ 

624 return self._display_completions 

625 

626 

627class ExclusiveCompletionFinder(CompletionFinder): 

628 @staticmethod 

629 def _action_allowed(action, parser): 

630 if not CompletionFinder._action_allowed(action, parser): 

631 return False 

632 

633 append_classes = (argparse._AppendAction, argparse._AppendConstAction) 

634 if action._orig_class in append_classes: 

635 return True 

636 

637 return action not in parser._seen_non_default_actions