Coverage for typer / core.py: 100%

554 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-11-09 00:15 +0000

1import errno 1afcdbeg

2import inspect 1afcdbeg

3import os 1afcdbeg

4import sys 1afcdbeg

5from collections.abc import Callable, Mapping, MutableMapping, Sequence 1afcdbeg

6from difflib import get_close_matches 1afcdbeg

7from enum import Enum 1afcdbeg

8from gettext import gettext as _ 1afcdbeg

9from typing import ( 1afcdbeg

10 Any, 

11 TextIO, 

12 Union, 

13 cast, 

14) 

15 

16from . import _click 1afcdbeg

17from ._click import types 1afcdbeg

18from ._click.parser import _OptionParser 1afcdbeg

19from ._click.shell_completion import CompletionItem 1afcdbeg

20from ._typing import Literal 1afcdbeg

21from .exceptions import Abort, Exit 1afcdbeg

22from .utils import parse_boolean_env_var 1afcdbeg

23 

24MarkupMode = Literal["markdown", "rich", None] 1afcdbeg

25MARKUP_MODE_KEY = "TYPER_RICH_MARKUP_MODE" 1afcdbeg

26 

27HAS_RICH = parse_boolean_env_var(os.getenv("TYPER_USE_RICH"), default=True) 1afcdbeg

28 

29if HAS_RICH: 1afcdbeg

30 DEFAULT_MARKUP_MODE: MarkupMode = "rich" 1afcdbeg

31else: 

32 DEFAULT_MARKUP_MODE = None 1afcdbeg

33 

34 

35# Copy from _click.parser._split_opt 

36def _split_opt(opt: str) -> tuple[str, str]: 1afcdbeg

37 first = opt[:1] 1afcdbeg

38 if first.isalnum(): 1afcdbeg

39 return "", opt 1afcdbeg

40 if opt[1:2] == first: 1afcdbeg

41 return opt[:2], opt[2:] 1afcdbeg

42 return first, opt[1:] 1afcdbeg

43 

44 

45def _typer_param_setup_autocompletion_compat( 1afcdbeg

46 self: _click.Parameter, 

47 *, 

48 autocompletion: Callable[ 

49 [_click.Context, list[str], str], list[tuple[str, str] | str] 

50 ] 

51 | None = None, 

52) -> None: 

53 if self._custom_shell_complete is not None: 1afcdbeg

54 import warnings 1afcdbeg

55 

56 warnings.warn( 1afcdbeg

57 "In Typer, only the parameter 'autocompletion' is supported. " 

58 "The support for 'shell_complete' is deprecated and will be removed in upcoming versions. ", 

59 DeprecationWarning, 

60 stacklevel=2, 

61 ) 

62 

63 if autocompletion is not None: 1afcdbeg

64 

65 def compat_autocompletion( 1afcdbeg

66 ctx: _click.Context, param: _click.core.Parameter, incomplete: str 

67 ) -> list[CompletionItem]: 

68 out = [] 1afcdbeg

69 

70 for c in autocompletion(ctx, [], incomplete): 1afcdbeg

71 if isinstance(c, tuple): 1afcdbeg

72 use_completion = CompletionItem(c[0], help=c[1]) 1afcdbeg

73 else: 

74 assert isinstance(c, str) 1afcdbeg

75 use_completion = CompletionItem(c) 1afcdbeg

76 

77 if use_completion.value.startswith(incomplete): 1afcdbeg

78 out.append(use_completion) 1afcdbeg

79 

80 return out 1afcdbeg

81 

82 self._custom_shell_complete = compat_autocompletion 1afcdbeg

83 

84 

85def _get_default_string( 1afcdbeg

86 obj: Union["TyperArgument", "TyperOption"], 

87 *, 

88 ctx: _click.Context, 

89 show_default_is_str: bool, 

90 default_value: list[Any] | tuple[Any, ...] | str | Callable[..., Any] | Any, 

91) -> str: 

92 # Extracted from _click.core.Option.get_help_record() to be reused by 

93 # rich_utils avoiding RegEx hacks 

94 if show_default_is_str: 1afcdbeg

95 default_string = f"({obj.show_default})" 1afcdbeg

96 elif isinstance(default_value, (list, tuple)): 1afcdbeg

97 parts = [ 1afcdbeg

98 _get_default_string( 

99 obj, ctx=ctx, show_default_is_str=show_default_is_str, default_value=d 

100 ) 

101 for d in default_value 

102 ] 

103 if all(not part for part in parts): 1afcdbeg

104 default_string = "" 1afcdbeg

105 else: 

106 default_string = ", ".join(parts) 1afcdbeg

107 elif isinstance(default_value, Enum): 1afcdbeg

108 default_string = str(default_value.value) 1afcdbeg

109 elif inspect.isfunction(default_value): 1afcdbeg

110 default_string = _("(dynamic)") 1afcdbeg

111 elif isinstance(obj, TyperOption) and obj.is_bool_flag and obj.secondary_opts: 1afcdbeg

112 # For boolean flags that have distinct True/False opts, 

113 # use the opt without prefix instead of the value. 

114 # Typer override, original commented 

115 # default_string = _click.parser.split_opt( 

116 # (self.opts if self.default else self.secondary_opts)[0] 

117 # )[1] 

118 if obj.default: 1afcdbeg

119 if obj.opts: 1afcdbeg

120 default_string = _split_opt(obj.opts[0])[1] 1afcdbeg

121 else: 

122 default_string = str(default_value) 1afcdbeg

123 else: 

124 default_string = _split_opt(obj.secondary_opts[0])[1] 1afcdbeg

125 # Typer override end 

126 elif ( 1ab

127 isinstance(obj, TyperOption) 

128 and obj.is_bool_flag 

129 and not obj.secondary_opts 

130 and not default_value 

131 ): 

132 default_string = "" 1afcdbeg

133 elif default_value is None: 1afcdbeg

134 default_string = "" 1afcdbeg

135 else: 

136 default_string = str(default_value) 1afcdbeg

137 return default_string 1afcdbeg

138 

139 

140def _extract_default_help_str( 1afcdbeg

141 obj: Union["TyperArgument", "TyperOption"], *, ctx: _click.Context 

142) -> Any | Callable[[], Any] | None: 

143 # Extracted from _click.core.Option.get_help_record() to be reused by 

144 # rich_utils avoiding RegEx hacks 

145 # Temporarily enable resilient parsing to avoid type casting 

146 # failing for the default. Might be possible to extend this to 

147 # help formatting in general. 

148 resilient = ctx.resilient_parsing 1afcdbeg

149 ctx.resilient_parsing = True 1afcdbeg

150 

151 try: 1afcdbeg

152 default_value = obj.get_default(ctx, call=False) 1afcdbeg

153 finally: 

154 ctx.resilient_parsing = resilient 1afcdbeg

155 return default_value 1afcdbeg

156 

157 

158def _main( 1afcdbeg

159 self: _click.Command, 

160 *, 

161 args: Sequence[str] | None = None, 

162 prog_name: str | None = None, 

163 complete_var: str | None = None, 

164 standalone_mode: bool = True, 

165 windows_expand_args: bool = True, 

166 rich_markup_mode: MarkupMode = DEFAULT_MARKUP_MODE, 

167 **extra: Any, 

168) -> Any: 

169 # Originally copied from _click.main(), adopted to handle custom rich exceptions etc 

170 if args is None: 1afcdbeg

171 args = sys.argv[1:] 1afcdbeg

172 

173 if os.name == "nt" and windows_expand_args: # pragma: no cover 1afcdbeg

174 args = _click.utils._expand_args(args) 1cd

175 else: 

176 args = list(args) 1afcdbeg

177 

178 if prog_name is None: 1afcdbeg

179 prog_name = _click.utils._detect_program_name() 1afcdbeg

180 

181 # Process shell completion requests and exit early. 

182 self._main_shell_completion(extra, prog_name, complete_var) 1afcdbeg

183 

184 try: 1afcdbeg

185 try: 1afcdbeg

186 with self.make_context(prog_name, args, **extra) as ctx: 1afcdbeg

187 rv = self.invoke(ctx) 1afcdbeg

188 if not standalone_mode: 1afcdbeg

189 return rv 1afcdbeg

190 # it's not safe to `ctx.exit(rv)` here! 

191 # note that `rv` may actually contain data like "1" which 

192 # has obvious effects 

193 # more subtle case: `rv=[None, None]` can come out of 

194 # chained commands which all returned `None` -- so it's not 

195 # even always obvious that `rv` indicates success/failure 

196 # by its truthiness/falsiness 

197 ctx.exit() 1afcdbeg

198 except EOFError as e: 1afcdbeg

199 _click.echo(file=sys.stderr) 1afcdbeg

200 raise Abort() from e 1afcdbeg

201 except KeyboardInterrupt as e: 1afcdbeg

202 raise Exit(130) from e 1afcdbeg

203 except _click.exceptions.ClickException as e: 1afcdbeg

204 if not standalone_mode: 1afcdbeg

205 raise 1afcdbeg

206 if HAS_RICH and rich_markup_mode is not None: 1afcdbeg

207 from . import rich_utils 1afcdbeg

208 

209 rich_utils.rich_format_error(e) 1afcdbeg

210 else: 

211 e.show() 1afcdbeg

212 sys.exit(e.exit_code) 1afcdbeg

213 except OSError as e: 1afcdbeg

214 if e.errno == errno.EPIPE: 1afcdbeg

215 sys.stdout = cast(TextIO, _click.utils.PacifyFlushWrapper(sys.stdout)) 1afcdbeg

216 sys.stderr = cast(TextIO, _click.utils.PacifyFlushWrapper(sys.stderr)) 1afcdbeg

217 sys.exit(1) 1afcdbeg

218 else: 

219 raise 1afcdbeg

220 except Exit as e: 1afcdbeg

221 if standalone_mode: 1afcdbeg

222 sys.exit(e.exit_code) 1afcdbeg

223 else: 

224 # in non-standalone mode, return the exit code 

225 # note that this is only reached if `self.invoke` above raises 

226 # an Exit explicitly -- thus bypassing the check there which 

227 # would return its result 

228 # the results of non-standalone execution may therefore be 

229 # somewhat ambiguous: if there are codepaths which lead to 

230 # `ctx.exit(1)` and to `return 1`, the caller won't be able to 

231 # tell the difference between the two 

232 return e.exit_code 1afcdbeg

233 except Abort: 1afcdbeg

234 if not standalone_mode: 1afcdbeg

235 raise 1afcdbeg

236 if HAS_RICH and rich_markup_mode is not None: 1afcdbeg

237 from . import rich_utils 1afcdbeg

238 

239 rich_utils.rich_abort_error() 1afcdbeg

240 else: 

241 _click.echo(_("Aborted!"), file=sys.stderr) 1afcdbeg

242 sys.exit(1) 1afcdbeg

243 

244 

245class TyperArgument(_click.core.Parameter): 1afcdbeg

246 param_type_name = "argument" 1afcdbeg

247 

248 def __init__( 1afcdbeg

249 self, 

250 *, 

251 # Parameter 

252 param_decls: list[str], 

253 type: Any | None = None, 

254 required: bool = False, 

255 default: Any | None = None, 

256 callback: Callable[..., Any] | None = None, 

257 nargs: int | None = None, 

258 metavar: str | None = None, 

259 expose_value: bool = True, 

260 is_eager: bool = False, 

261 envvar: str | list[str] | None = None, 

262 # Note that shell_complete is not fully supported and will be removed in future versions 

263 # TODO: Remove shell_complete in a future version (after 0.16.0) 

264 shell_complete: Callable[ 

265 [_click.Context, _click.Parameter, str], 

266 list[CompletionItem] | list[str], 

267 ] 

268 | None = None, 

269 autocompletion: Callable[..., Any] | None = None, 

270 # TyperArgument 

271 show_default: bool | str = True, 

272 show_choices: bool = True, 

273 show_envvar: bool = True, 

274 help: str | None = None, 

275 hidden: bool = False, 

276 # Rich settings 

277 rich_help_panel: str | None = None, 

278 ): 

279 self.help = help 1afcdbeg

280 self.show_default = show_default 1afcdbeg

281 self.show_choices = show_choices 1afcdbeg

282 self.show_envvar = show_envvar 1afcdbeg

283 self.hidden = hidden 1afcdbeg

284 self.rich_help_panel = rich_help_panel 1afcdbeg

285 

286 super().__init__( 1afcdbeg

287 param_decls=param_decls, 

288 type=type, 

289 required=required, 

290 default=default, 

291 callback=callback, 

292 nargs=nargs, 

293 metavar=metavar, 

294 expose_value=expose_value, 

295 is_eager=is_eager, 

296 envvar=envvar, 

297 shell_complete=shell_complete, 

298 ) 

299 _typer_param_setup_autocompletion_compat(self, autocompletion=autocompletion) 1afcdbeg

300 

301 @property 1afcdbeg

302 def human_readable_name(self) -> str: 1afcdbeg

303 if self.metavar is not None: 1afcdbeg

304 return self.metavar 1afcdbeg

305 assert self.name is not None, "self.name or self.metavar should be set" 1afcdbeg

306 return self.name 1afcdbeg

307 

308 def _get_default_string( 1afcdbeg

309 self, 

310 *, 

311 ctx: _click.Context, 

312 show_default_is_str: bool, 

313 default_value: list[Any] | tuple[Any, ...] | str | Callable[..., Any] | Any, 

314 ) -> str: 

315 return _get_default_string( 1afcdbeg

316 self, 

317 ctx=ctx, 

318 show_default_is_str=show_default_is_str, 

319 default_value=default_value, 

320 ) 

321 

322 def _extract_default_help_str( 1afcdbeg

323 self, *, ctx: _click.Context 

324 ) -> Any | Callable[[], Any] | None: 

325 return _extract_default_help_str(self, ctx=ctx) 1afcdbeg

326 

327 def get_help_record(self, ctx: _click.Context) -> tuple[str, str] | None: 1afcdbeg

328 # Modified version of _click.core.Option.get_help_record() 

329 # to support Arguments 

330 if self.hidden: 1afcdbeg

331 return None 1afcdbeg

332 name = self.make_metavar(ctx=ctx) 1afcdbeg

333 help = self.help or "" 1afcdbeg

334 extra = [] 1afcdbeg

335 if self.show_envvar: 1afcdbeg

336 envvar = self.envvar 1afcdbeg

337 # allow_from_autoenv is currently not supported in Typer for CLI Arguments 

338 if envvar is not None: 1afcdbeg

339 var_str = ( 1afcdbeg

340 ", ".join(str(d) for d in envvar) 

341 if isinstance(envvar, (list, tuple)) 

342 else envvar 

343 ) 

344 extra.append(f"env var: {var_str}") 1afcdbeg

345 

346 # Typer override: 

347 # Extracted to _extract_default_help_str() to allow re-using it in rich_utils 

348 default_value = self._extract_default_help_str(ctx=ctx) 1afcdbeg

349 # Typer override end 

350 

351 show_default_is_str = isinstance(self.show_default, str) 1afcdbeg

352 

353 if show_default_is_str or ( 1afcdbeg

354 default_value is not None and (self.show_default or ctx.show_default) 

355 ): 

356 # Typer override: 

357 # Extracted to _get_default_string() to allow re-using it in rich_utils 

358 default_string = self._get_default_string( 1afcdbeg

359 ctx=ctx, 

360 show_default_is_str=show_default_is_str, 

361 default_value=default_value, 

362 ) 

363 # Typer override end 

364 if default_string: 1afcdbeg

365 extra.append(_("default: {default}").format(default=default_string)) 1afcdbeg

366 if self.required: 1afcdbeg

367 extra.append(_("required")) 1afcdbeg

368 if extra: 1afcdbeg

369 extra_str = "; ".join(extra) 1afcdbeg

370 extra_str = f"[{extra_str}]" 1afcdbeg

371 rich_markup_mode = None 1afcdbeg

372 if hasattr(ctx, "obj") and isinstance(ctx.obj, dict): 1afcdbeg

373 rich_markup_mode = ctx.obj.get(MARKUP_MODE_KEY, None) 1afcdbeg

374 if HAS_RICH and rich_markup_mode == "rich": 1afcdbeg

375 # This is needed for when we want to export to HTML 

376 from . import rich_utils 1afcdbeg

377 

378 extra_str = rich_utils.escape_before_html_export(extra_str) 1afcdbeg

379 

380 help = f"{help} {extra_str}" if help else f"{extra_str}" 1afcdbeg

381 return name, help 1afcdbeg

382 

383 def make_metavar(self, ctx: _click.Context, *, usage: bool = False) -> str: 1afcdbeg

384 # Modified version of _click.core.Argument.make_metavar() 

385 # to include Argument name 

386 if self.metavar is not None: 1afcdbeg

387 var = self.metavar 1afcdbeg

388 if var.startswith("[") or not usage: 1afcdbeg

389 return var 1afcdbeg

390 if not self.required: 1afcdbeg

391 return f"[{var}]" 1afcdbeg

392 return f"{{{var}}}" 1afcdbeg

393 var = self.name or "" 1afcdbeg

394 if usage and not self.required: 1afcdbeg

395 var = f"[{var}]" 1afcdbeg

396 elif usage and self.required: 1afcdbeg

397 var = f"{{{var}}}" 1afcdbeg

398 type_var = self.type.get_metavar(self, ctx=ctx) 1afcdbeg

399 if type_var: 1afcdbeg

400 var += f":{type_var}" 1afcdbeg

401 if self.nargs != 1: 1afcdbeg

402 var += "..." 1afcdbeg

403 return var 1afcdbeg

404 

405 def value_is_missing(self, value: Any) -> bool: 1afcdbeg

406 return _value_is_missing(self, value) 1afcdbeg

407 

408 def _parse_decls( 1afcdbeg

409 self, decls: Sequence[str], expose_value: bool 

410 ) -> tuple[str | None, list[str], list[str]]: 

411 if not decls: 1afcdbeg

412 if not expose_value: 1afcdbeg

413 return None, [], [] 1afcdbeg

414 raise TypeError("Argument is marked as exposed, but does not have a name.") 1afcdbeg

415 if len(decls) == 1: 1afcdbeg

416 name = arg = decls[0] 1afcdbeg

417 name = name.replace("-", "_") 1afcdbeg

418 else: 

419 raise TypeError( 1afcdbeg

420 "Arguments take exactly one parameter declaration, got" 

421 f" {len(decls)}: {decls}." 

422 ) 

423 return name, [arg], [] 1afcdbeg

424 

425 def get_usage_pieces(self, ctx: _click.Context) -> list[str]: 1afcdbeg

426 return [self.make_metavar(ctx, usage=True)] 1afcdbeg

427 

428 def get_error_hint(self, ctx: _click.Context) -> str: 1afcdbeg

429 return f"'{self.human_readable_name}'" 1afcdbeg

430 

431 def add_to_parser(self, parser: _OptionParser, ctx: _click.Context) -> None: 1afcdbeg

432 parser.add_argument(dest=self.name, nargs=self.nargs, obj=self) 1afcdbeg

433 

434 

435class TyperOption(_click.Parameter): 1afcdbeg

436 param_type_name = "option" 1afcdbeg

437 

438 _depr_flag_value: bool | None 1acbe

439 

440 def __init__( 1afcdbeg

441 self, 

442 *, 

443 # Parameter 

444 param_decls: list[str], 

445 type: types.ParamType | Any | None = None, 

446 required: bool = False, 

447 default: Any | None = None, 

448 callback: Callable[..., Any] | None = None, 

449 nargs: int | None = None, 

450 metavar: str | None = None, 

451 expose_value: bool = True, 

452 is_eager: bool = False, 

453 envvar: str | list[str] | None = None, 

454 # Note that shell_complete is not fully supported and will be removed in future versions 

455 # TODO: Remove shell_complete in a future version (after 0.16.0) 

456 shell_complete: Callable[ 

457 [_click.Context, _click.Parameter, str], 

458 list[CompletionItem] | list[str], 

459 ] 

460 | None = None, 

461 autocompletion: Callable[..., Any] | None = None, 

462 # Option 

463 show_default: bool | str = False, 

464 prompt: bool | str = False, 

465 confirmation_prompt: bool | str = False, 

466 prompt_required: bool = True, 

467 hide_input: bool = False, 

468 is_flag: bool | None = None, 

469 multiple: bool = False, 

470 count: bool = False, 

471 allow_from_autoenv: bool = True, 

472 help: str | None = None, 

473 hidden: bool = False, 

474 show_choices: bool = True, 

475 show_envvar: bool = False, 

476 # Rich settings 

477 rich_help_panel: str | None = None, 

478 ): 

479 if help: 1afcdbeg

480 help = inspect.cleandoc(help) 1afcdbeg

481 

482 super().__init__( 1afcdbeg

483 param_decls, 

484 type=type, 

485 multiple=multiple, 

486 required=required, 

487 default=default, 

488 callback=callback, 

489 nargs=nargs, 

490 metavar=metavar, 

491 expose_value=expose_value, 

492 is_eager=is_eager, 

493 envvar=envvar, 

494 shell_complete=shell_complete, 

495 ) 

496 

497 if prompt is True: 1afcdbeg

498 if self.name is None: 1afcdbeg

499 raise TypeError("'name' is required with 'prompt=True'.") 1afcdbeg

500 

501 prompt_text: str | None = self.name.replace("_", " ").capitalize() 1afcdbeg

502 elif prompt is False: 1afcdbeg

503 prompt_text = None 1afcdbeg

504 else: 

505 prompt_text = prompt 1afcdbeg

506 

507 self.prompt = prompt_text 1afcdbeg

508 self.confirmation_prompt = confirmation_prompt 1afcdbeg

509 self.prompt_required = prompt_required 1afcdbeg

510 self.hide_input = hide_input 1afcdbeg

511 self.hidden = hidden 1afcdbeg

512 

513 # TODO: revisit all of this flag stuff 

514 if is_flag and type is None: 1afcdbeg

515 self.type: types.ParamType = types.BoolParamType() 1afcdbeg

516 

517 self.is_flag: bool = bool(is_flag) 1afcdbeg

518 self.is_bool_flag: bool = bool( 1afcdbeg

519 is_flag and isinstance(self.type, types.BoolParamType) 

520 ) 

521 

522 if self.is_flag: 1afcdbeg

523 self._depr_flag_value = True 1afcdbeg

524 else: 

525 self._depr_flag_value = None 1afcdbeg

526 

527 # Counting. TODO: test or remove? Not currently in coverage. 

528 self.count = count 1afcdbeg

529 if count and type is None: 1afcdbeg

530 self.type = types.IntRange(min=0) 1afcdbeg

531 

532 self.allow_from_autoenv = allow_from_autoenv 1afcdbeg

533 self.help = help 1afcdbeg

534 self.show_default = show_default 1afcdbeg

535 self.show_choices = show_choices 1afcdbeg

536 self.show_envvar = show_envvar 1afcdbeg

537 

538 _typer_param_setup_autocompletion_compat(self, autocompletion=autocompletion) 1afcdbeg

539 self.rich_help_panel = rich_help_panel 1afcdbeg

540 

541 def get_error_hint(self, ctx: _click.Context) -> str: 1afcdbeg

542 result = super().get_error_hint(ctx) 1afcdbeg

543 if self.show_envvar and self.envvar is not None: 1afcdbeg

544 result += f" (env var: '{self.envvar}')" 1afcdbeg

545 return result 1afcdbeg

546 

547 def _parse_decls( 1afcdbeg

548 self, decls: Sequence[str], expose_value: bool 

549 ) -> tuple[str | None, list[str], list[str]]: 

550 opts = [] 1afcdbeg

551 secondary_opts = [] 1afcdbeg

552 name = None 1afcdbeg

553 possible_names = [] 1afcdbeg

554 

555 for decl in decls: 1afcdbeg

556 if decl.isidentifier(): 1afcdbeg

557 if name is not None: 1afcdbeg

558 raise TypeError(f"Name '{name}' defined twice") 1afcdbeg

559 name = decl 1afcdbeg

560 else: 

561 split_char = ";" if decl[:1] == "/" else "/" 1afcdbeg

562 if split_char in decl: 1afcdbeg

563 first, second = decl.split(split_char, 1) 1afcdbeg

564 first = first.rstrip() 1afcdbeg

565 if first: 1afcdbeg

566 possible_names.append(_split_opt(first)) 1afcdbeg

567 opts.append(first) 1afcdbeg

568 second = second.lstrip() 1afcdbeg

569 if second: 1afcdbeg

570 secondary_opts.append(second.lstrip()) 1afcdbeg

571 if first == second: 1afcdbeg

572 raise ValueError( 1afcdbeg

573 f"Boolean option {decl!r} cannot use the" 

574 " same flag for true/false." 

575 ) 

576 else: 

577 possible_names.append(_split_opt(decl)) 1afcdbeg

578 opts.append(decl) 1afcdbeg

579 

580 if name is None and possible_names: 1afcdbeg

581 possible_names.sort(key=lambda x: -len(x[0])) # group long options first 1afcdbeg

582 name = possible_names[0][1].replace("-", "_") 1afcdbeg

583 if not name.isidentifier(): 1afcdbeg

584 name = None 1afcdbeg

585 

586 return name, opts, secondary_opts 1afcdbeg

587 

588 def add_to_parser(self, parser: _OptionParser, ctx: _click.Context) -> None: 1afcdbeg

589 if self.multiple: 1afcdbeg

590 action = "append" 1afcdbeg

591 elif self.count: 1afcdbeg

592 action = "count" 1afcdbeg

593 else: 

594 action = "store" 1afcdbeg

595 

596 if self.is_flag: 1afcdbeg

597 action = f"{action}_const" 1afcdbeg

598 

599 if self.is_bool_flag and self.secondary_opts: 1afcdbeg

600 parser.add_option( 1afcdbeg

601 obj=self, opts=self.opts, dest=self.name, action=action, const=True 

602 ) 

603 parser.add_option( 1afcdbeg

604 obj=self, 

605 opts=self.secondary_opts, 

606 dest=self.name, 

607 action=action, 

608 const=False, 

609 ) 

610 else: 

611 parser.add_option( 1afcdbeg

612 obj=self, 

613 opts=self.opts, 

614 dest=self.name, 

615 action=action, 

616 const=self._depr_flag_value, 

617 ) 

618 else: 

619 parser.add_option( 1afcdbeg

620 obj=self, 

621 opts=self.opts, 

622 dest=self.name, 

623 action=action, 

624 nargs=self.nargs, 

625 ) 

626 

627 def prompt_for_value(self, ctx: _click.Context) -> Any: 1afcdbeg

628 """This is an alternative flow that can be activated in the full 

629 value processing if a value does not exist. It will prompt the 

630 user until a valid value exists and then returns the processed 

631 value as result. 

632 """ 

633 assert self.prompt is not None 1afcdbeg

634 

635 # Calculate the default before prompting anything to lock in the value before 

636 # attempting any user interaction. 

637 default = self.get_default(ctx) 1afcdbeg

638 

639 # A boolean flag can use a simplified [y/n] confirmation prompt. 

640 if self.is_bool_flag: 1afcdbeg

641 # Nothing prevent you to declare an option that is simultaneously: 

642 # 1) auto-detected as a boolean flag, 

643 # 2) allowed to prompt, and 

644 # 3) still declare a non-boolean default. 

645 # This forced casting into a boolean is necessary to align any non-boolean 

646 # default to the prompt, which is going to be a [y/n]-style confirmation 

647 # because the option is still a boolean flag. That way, instead of [y/n], 

648 # we get [Y/n] or [y/N] depending on the truthy value of the default. 

649 # Refs: https://github.com/pallets/click/pull/3030#discussion_r2289180249 

650 if default is not None: 1afcdbeg

651 default = bool(default) 1afcdbeg

652 return _click.termui.confirm(self.prompt, default) 1afcdbeg

653 

654 # If show_default is set to True/False, provide this to `prompt` as well. For 

655 # non-bool values of `show_default`, we use `prompt`'s default behavior 

656 prompt_kwargs: Any = {} 1afcdbeg

657 if isinstance(self.show_default, bool): 1afcdbeg

658 prompt_kwargs["show_default"] = self.show_default 1afcdbeg

659 

660 return _click.termui.prompt( 1afcdbeg

661 self.prompt, 

662 # Use ``None`` to inform the prompt() function to reiterate until a valid 

663 # value is provided by the user if we have no default. 

664 default=default, 

665 type=self.type, 

666 hide_input=self.hide_input, 

667 show_choices=self.show_choices, 

668 confirmation_prompt=self.confirmation_prompt, 

669 value_proc=lambda x: self.process_value(ctx, x), 

670 **prompt_kwargs, 

671 ) 

672 

673 def value_from_envvar(self, ctx: _click.Context) -> Any: 1afcdbeg

674 # TODO: clean up 

675 rv = self.resolve_envvar_value(ctx) 1afcdbeg

676 

677 # Absent environment variable or an empty string is interpreted as unset. 

678 if rv is None: 1afcdbeg

679 return None 1afcdbeg

680 

681 if self.nargs != 1 or self.multiple: 1afcdbeg

682 return self.type.split_envvar_value(rv) 1afcdbeg

683 

684 return rv 1afcdbeg

685 

686 def resolve_envvar_value(self, ctx: _click.Context) -> str | None: 1afcdbeg

687 rv = super().resolve_envvar_value(ctx) 1afcdbeg

688 

689 if rv is not None: 1afcdbeg

690 return rv 1afcdbeg

691 

692 if ( 1ab

693 self.allow_from_autoenv 

694 and ctx.auto_envvar_prefix is not None 

695 and self.name is not None 

696 ): 

697 envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}" 1afcdbeg

698 rv = os.environ.get(envvar) 1afcdbeg

699 

700 if rv: 1afcdbeg

701 return rv 1afcdbeg

702 

703 return None 1afcdbeg

704 

705 def consume_value( 1afcdbeg

706 self, ctx: _click.Context, opts: Mapping[str, _click.Parameter] 

707 ) -> tuple[Any, _click.core.ParameterSource]: 

708 """For `Option`, the value can be collected from an interactive prompt 

709 if the option is a flag that needs a value (and the `prompt` property is 

710 set). 

711 

712 Additionally, this method handles flag option that are activated without a 

713 value, in which case the `flag_value` is returned. 

714 """ 

715 value, source = super().consume_value(ctx, opts) 1afcdbeg

716 

717 # The value wasn't set, or used the param's default, prompt for one to the user 

718 # if prompting is enabled. 

719 if ( 

720 source in {None, _click.core.ParameterSource.DEFAULT} 

721 and self.prompt is not None 

722 and (self.required or self.prompt_required) 

723 and not ctx.resilient_parsing 

724 ): 

725 value = self.prompt_for_value(ctx) 1afcdbeg

726 source = _click.core.ParameterSource.PROMPT 1afcdbeg

727 

728 return value, source 1afcdbeg

729 

730 def _get_default_string( 1afcdbeg

731 self, 

732 *, 

733 ctx: _click.Context, 

734 show_default_is_str: bool, 

735 default_value: list[Any] | tuple[Any, ...] | str | Callable[..., Any] | Any, 

736 ) -> str: 

737 return _get_default_string( 1afcdbeg

738 self, 

739 ctx=ctx, 

740 show_default_is_str=show_default_is_str, 

741 default_value=default_value, 

742 ) 

743 

744 def _extract_default_help_str( 1afcdbeg

745 self, *, ctx: _click.Context 

746 ) -> Any | Callable[[], Any] | None: 

747 return _extract_default_help_str(self, ctx=ctx) 1afcdbeg

748 

749 def make_metavar(self, ctx: _click.Context) -> str: 1afcdbeg

750 return super().make_metavar(ctx=ctx) 1afcdbeg

751 

752 def get_help_record(self, ctx: _click.Context) -> tuple[str, str] | None: 1afcdbeg

753 # Duplicate all of Click's logic only to modify a single line, to allow boolean 

754 # flags with only names for False values as it's currently supported by Typer 

755 # Ref: https://typer.tiangolo.com/tutorial/parameter-types/bool/#only-names-for-false 

756 if self.hidden: 1afcdbeg

757 return None 1afcdbeg

758 

759 any_prefix_is_slash = False 1afcdbeg

760 

761 def _write_opts(opts: Sequence[str]) -> str: 1afcdbeg

762 nonlocal any_prefix_is_slash 

763 

764 rv, any_slashes = _click.formatting.join_options(opts) 1afcdbeg

765 

766 if any_slashes: 1afcdbeg

767 any_prefix_is_slash = True 1afcdbeg

768 

769 if not self.is_flag and not self.count: 1afcdbeg

770 rv += f" {self.make_metavar(ctx=ctx)}" 1afcdbeg

771 

772 return rv 1afcdbeg

773 

774 rv = [_write_opts(self.opts)] 1afcdbeg

775 

776 if self.secondary_opts: 1afcdbeg

777 rv.append(_write_opts(self.secondary_opts)) 1afcdbeg

778 

779 help = self.help or "" 1afcdbeg

780 extra = [] 1afcdbeg

781 

782 if self.show_envvar: 1afcdbeg

783 envvar = self.envvar 1afcdbeg

784 

785 if envvar is None: 1afcdbeg

786 if ( 1ab

787 self.allow_from_autoenv 

788 and ctx.auto_envvar_prefix is not None 

789 and self.name is not None 

790 ): 

791 envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}" 1afcdbeg

792 

793 if envvar is not None: 1afcdbeg

794 var_str = ( 1afcdbeg

795 envvar 

796 if isinstance(envvar, str) 

797 else ", ".join(str(d) for d in envvar) 

798 ) 

799 extra.append(_("env var: {var}").format(var=var_str)) 1afcdbeg

800 

801 # Typer override: 

802 # Extracted to _extract_default() to allow re-using it in rich_utils 

803 default_value = self._extract_default_help_str(ctx=ctx) 1afcdbeg

804 # Typer override end 

805 

806 show_default_is_str = isinstance(self.show_default, str) 1afcdbeg

807 

808 if show_default_is_str or ( 1afcdbeg

809 default_value is not None and (self.show_default or ctx.show_default) 

810 ): 

811 # Typer override: 

812 # Extracted to _get_default_string() to allow re-using it in rich_utils 

813 default_string = self._get_default_string( 1afcdbeg

814 ctx=ctx, 

815 show_default_is_str=show_default_is_str, 

816 default_value=default_value, 

817 ) 

818 # Typer override end 

819 if default_string: 1afcdbeg

820 extra.append(_("default: {default}").format(default=default_string)) 1afcdbeg

821 

822 if isinstance(self.type, types._NumberRangeBase): 1afcdbeg

823 range_str = self.type._describe_range() 1afcdbeg

824 

825 if range_str: 1afcdbeg

826 extra.append(range_str) 1afcdbeg

827 

828 if self.required: 1afcdbeg

829 extra.append(_("required")) 1afcdbeg

830 

831 if extra: 1afcdbeg

832 extra_str = "; ".join(extra) 1afcdbeg

833 extra_str = f"[{extra_str}]" 1afcdbeg

834 rich_markup_mode = None 1afcdbeg

835 if hasattr(ctx, "obj") and isinstance(ctx.obj, dict): 1afcdbeg

836 rich_markup_mode = ctx.obj.get(MARKUP_MODE_KEY, None) 1afcdbeg

837 if HAS_RICH and rich_markup_mode == "rich": 1afcdbeg

838 # This is needed for when we want to export to HTML 

839 from . import rich_utils 1afcdbeg

840 

841 extra_str = rich_utils.escape_before_html_export(extra_str) 1afcdbeg

842 

843 help = f"{help} {extra_str}" if help else f"{extra_str}" 1afcdbeg

844 

845 return ("; " if any_prefix_is_slash else " / ").join(rv), help 1afcdbeg

846 

847 def value_is_missing(self, value: Any) -> bool: 1afcdbeg

848 return _value_is_missing(self, value) 1afcdbeg

849 

850 

851def _value_is_missing(param: _click.Parameter, value: Any) -> bool: 1afcdbeg

852 if value is None: 1afcdbeg

853 return True 1afcdbeg

854 

855 if (param.nargs != 1 or param.multiple) and value == (): 1afcdbeg

856 return True # pragma: no cover 

857 

858 return False 1afcdbeg

859 

860 

861def _typer_format_options( 1afcdbeg

862 self: _click.core.Command, *, ctx: _click.Context, formatter: _click.HelpFormatter 

863) -> None: 

864 args = [] 1afcdbeg

865 opts = [] 1afcdbeg

866 for param in self.get_params(ctx): 1afcdbeg

867 rv = param.get_help_record(ctx) 1afcdbeg

868 if rv is not None: 1afcdbeg

869 if param.param_type_name == "argument": 1afcdbeg

870 args.append(rv) 1afcdbeg

871 elif param.param_type_name == "option": 1afcdbeg

872 opts.append(rv) 1afcdbeg

873 

874 if args: 1afcdbeg

875 with formatter.section(_("Arguments")): 1afcdbeg

876 formatter.write_dl(args) 1afcdbeg

877 if opts: 1afcdbeg

878 with formatter.section(_("Options")): 1afcdbeg

879 formatter.write_dl(opts) 1afcdbeg

880 

881 

882def _typer_main_shell_completion( 1afcdbeg

883 self: _click.core.Command, 

884 *, 

885 ctx_args: MutableMapping[str, Any], 

886 prog_name: str, 

887 complete_var: str | None = None, 

888) -> None: 

889 if complete_var is None: 1afcdbeg

890 complete_var = f"_{prog_name}_COMPLETE".replace("-", "_").upper() 1afcdbeg

891 

892 instruction = os.environ.get(complete_var) 1afcdbeg

893 

894 if not instruction: 1afcdbeg

895 return 1afcdbeg

896 

897 from .completion import shell_complete 1afcdbeg

898 

899 rv = shell_complete(self, ctx_args, prog_name, complete_var, instruction) 1afcdbeg

900 sys.exit(rv) 1afcdbeg

901 

902 

903class TyperCommand(_click.core.Command): 1afcdbeg

904 def __init__( 1afcdbeg

905 self, 

906 name: str | None, 

907 *, 

908 context_settings: dict[str, Any] | None = None, 

909 callback: Callable[..., Any] | None = None, 

910 params: list[_click.Parameter] | None = None, 

911 help: str | None = None, 

912 epilog: str | None = None, 

913 short_help: str | None = None, 

914 options_metavar: str | None = "[OPTIONS]", 

915 add_help_option: bool = True, 

916 no_args_is_help: bool = False, 

917 hidden: bool = False, 

918 deprecated: bool = False, 

919 # Rich settings 

920 rich_markup_mode: MarkupMode = DEFAULT_MARKUP_MODE, 

921 rich_help_panel: str | None = None, 

922 ) -> None: 

923 super().__init__( 1afcdbeg

924 name=name, 

925 context_settings=context_settings, 

926 callback=callback, 

927 params=params, 

928 help=help, 

929 epilog=epilog, 

930 short_help=short_help, 

931 options_metavar=options_metavar, 

932 add_help_option=add_help_option, 

933 no_args_is_help=no_args_is_help, 

934 hidden=hidden, 

935 deprecated=deprecated, 

936 ) 

937 self.rich_markup_mode: MarkupMode = rich_markup_mode 1afcdbeg

938 self.rich_help_panel = rich_help_panel 1afcdbeg

939 

940 def format_options( 1afcdbeg

941 self, ctx: _click.Context, formatter: _click.HelpFormatter 

942 ) -> None: 

943 _typer_format_options(self, ctx=ctx, formatter=formatter) 1afcdbeg

944 

945 def _main_shell_completion( 1afcdbeg

946 self, 

947 ctx_args: MutableMapping[str, Any], 

948 prog_name: str, 

949 complete_var: str | None = None, 

950 ) -> None: 

951 _typer_main_shell_completion( 1afcdbeg

952 self, ctx_args=ctx_args, prog_name=prog_name, complete_var=complete_var 

953 ) 

954 

955 def main( 1afcdbeg

956 self, 

957 args: Sequence[str] | None = None, 

958 prog_name: str | None = None, 

959 complete_var: str | None = None, 

960 standalone_mode: bool = True, 

961 windows_expand_args: bool = True, 

962 **extra: Any, 

963 ) -> Any: 

964 return _main( 1afcdbeg

965 self, 

966 args=args, 

967 prog_name=prog_name, 

968 complete_var=complete_var, 

969 standalone_mode=standalone_mode, 

970 windows_expand_args=windows_expand_args, 

971 rich_markup_mode=self.rich_markup_mode, 

972 **extra, 

973 ) 

974 

975 def format_help(self, ctx: _click.Context, formatter: _click.HelpFormatter) -> None: 1afcdbeg

976 if not HAS_RICH or self.rich_markup_mode is None: 1afcdbeg

977 if not hasattr(ctx, "obj") or ctx.obj is None: 1afcdbeg

978 ctx.ensure_object(dict) 1afcdbeg

979 if isinstance(ctx.obj, dict): 1afcdbeg

980 ctx.obj[MARKUP_MODE_KEY] = self.rich_markup_mode 1afcdbeg

981 return super().format_help(ctx, formatter) 1afcdbeg

982 from . import rich_utils 1afcdbeg

983 

984 return rich_utils.rich_format_help( 1afcdbeg

985 obj=self, 

986 ctx=ctx, 

987 markup_mode=self.rich_markup_mode, 

988 ) 

989 

990 

991class TyperGroup(_click.Command): 1afcdbeg

992 allow_extra_args = True 1afcdbeg

993 allow_interspersed_args = False 1afcdbeg

994 command_class: type[_click.Command] | None = None 1afcdbeg

995 group_class: type["TyperGroup"] | type[type] | None = None 1afcdbeg

996 

997 def __init__( 1afcdbeg

998 self, 

999 *, 

1000 name: str | None = None, 

1001 commands: dict[str, _click.Command] | Sequence[_click.Command] | None = None, 

1002 # Rich settings 

1003 rich_markup_mode: MarkupMode = DEFAULT_MARKUP_MODE, 

1004 rich_help_panel: str | None = None, 

1005 suggest_commands: bool = True, 

1006 # Click settings 

1007 invoke_without_command: bool = False, 

1008 no_args_is_help: bool = False, 

1009 subcommand_metavar: str | None = None, 

1010 result_callback: Callable[..., Any] | None = None, 

1011 **attrs: Any, 

1012 ) -> None: 

1013 super().__init__(name=name, **attrs) 1afcdbeg

1014 self.rich_markup_mode: MarkupMode = rich_markup_mode 1afcdbeg

1015 self.rich_help_panel = rich_help_panel 1afcdbeg

1016 self.suggest_commands = suggest_commands 1afcdbeg

1017 

1018 # copied from Click's init 

1019 if commands is None: 1afcdbeg

1020 commands = {} 1afcdbeg

1021 elif isinstance(commands, Sequence): 1afcdbeg

1022 commands = { 1afcdbeg

1023 c.name: c 

1024 for c in commands 

1025 if isinstance(c, _click.Command) and c.name is not None 

1026 } 

1027 

1028 self.commands = cast(MutableMapping[str, _click.Command], commands) 1afcdbeg

1029 self.no_args_is_help = no_args_is_help 1afcdbeg

1030 self.invoke_without_command = invoke_without_command 1afcdbeg

1031 

1032 if subcommand_metavar is None: 1afcdbeg

1033 subcommand_metavar = "COMMAND [ARGS]..." 1afcdbeg

1034 

1035 self.subcommand_metavar = subcommand_metavar 1afcdbeg

1036 self._result_callback = result_callback 1afcdbeg

1037 

1038 def add_command(self, cmd: _click.Command, name: str | None = None) -> None: 1afcdbeg

1039 name = name or cmd.name 1afcdbeg

1040 if name is None: 1afcdbeg

1041 raise TypeError("Command has no name.") 1afcdbeg

1042 self.commands[name] = cmd 1afcdbeg

1043 

1044 def get_command(self, ctx: _click.Context, cmd_name: str) -> _click.Command | None: 1afcdbeg

1045 return self.commands.get(cmd_name) 1afcdbeg

1046 

1047 def collect_usage_pieces(self, ctx: _click.Context) -> list[str]: 1afcdbeg

1048 rv = super().collect_usage_pieces(ctx) 1afcdbeg

1049 rv.append(self.subcommand_metavar) 1afcdbeg

1050 return rv 1afcdbeg

1051 

1052 def format_commands( 1afcdbeg

1053 self, ctx: _click.Context, formatter: _click.HelpFormatter 

1054 ) -> None: 

1055 commands = [] 1afcdbeg

1056 for subcommand in self.list_commands(ctx): 1afcdbeg

1057 cmd = self.get_command(ctx, subcommand) 1afcdbeg

1058 if cmd is None or cmd.hidden: 1afcdbeg

1059 continue 1afcdbeg

1060 

1061 commands.append((subcommand, cmd)) 1afcdbeg

1062 

1063 # allow for 3 times the default spacing 

1064 if len(commands): 1afcdbeg

1065 limit = formatter.width - 6 - max(len(cmd[0]) for cmd in commands) 1afcdbeg

1066 

1067 rows = [] 1afcdbeg

1068 for subcommand, cmd in commands: 1afcdbeg

1069 assert cmd is not None 1afcdbeg

1070 help = cmd.get_short_help_str(limit) 1afcdbeg

1071 rows.append((subcommand, help)) 1afcdbeg

1072 

1073 if rows: 1afcdbeg

1074 with formatter.section(_("Commands")): 1afcdbeg

1075 formatter.write_dl(rows) 1afcdbeg

1076 

1077 def parse_args(self, ctx: _click.Context, args: list[str]) -> list[str]: 1afcdbeg

1078 if not args and self.no_args_is_help and not ctx.resilient_parsing: 1afcdbeg

1079 raise _click.exceptions.NoArgsIsHelpError(ctx) 1afcdbeg

1080 

1081 rest = super().parse_args(ctx, args) 1afcdbeg

1082 

1083 if rest: 1afcdbeg

1084 ctx._protected_args, ctx.args = rest[:1], rest[1:] 1afcdbeg

1085 

1086 return ctx.args 1afcdbeg

1087 

1088 def invoke(self, ctx: _click.Context) -> Any: 1afcdbeg

1089 def _process_result(value: Any) -> Any: 1afcdbeg

1090 if self._result_callback is not None: 1afcdbeg

1091 value = ctx.invoke(self._result_callback, value, **ctx.params) 1afcdbeg

1092 return value 1afcdbeg

1093 

1094 if not ctx._protected_args: 1afcdbeg

1095 if self.invoke_without_command: 1afcdbeg

1096 # No subcommand was invoked, so the result callback is 

1097 # invoked with the group return value for regular 

1098 # groups, or an empty list for chained groups. 

1099 with ctx: 1afcdbeg

1100 rv = super().invoke(ctx) 1afcdbeg

1101 # return _process_result([] if self.chain else rv) 

1102 return _process_result(rv) 1afcdbeg

1103 ctx.fail(_("Missing command.")) 1afcdbeg

1104 

1105 # Fetch args back out 

1106 args = [*ctx._protected_args, *ctx.args] 1afcdbeg

1107 ctx.args = [] 1afcdbeg

1108 ctx._protected_args = [] 1afcdbeg

1109 

1110 # Make sure the context is entered so we do not clean up 

1111 # resources until the result processor has worked. 

1112 with ctx: 1afcdbeg

1113 cmd_name, cmd, args = self.resolve_command(ctx, args) 1afcdbeg

1114 assert cmd is not None 1afcdbeg

1115 ctx.invoked_subcommand = cmd_name 1afcdbeg

1116 super().invoke(ctx) 1afcdbeg

1117 sub_ctx = cmd.make_context(cmd_name, args, parent=ctx) 1afcdbeg

1118 with sub_ctx: 1afcdbeg

1119 return _process_result(sub_ctx.command.invoke(sub_ctx)) 1afcdbeg

1120 

1121 def shell_complete( 1afcdbeg

1122 self, ctx: _click.Context, incomplete: str 

1123 ) -> list[CompletionItem]: 

1124 """Return a list of completions for the incomplete value. Looks 

1125 at the names of options, subcommands, and chained 

1126 multi-commands. 

1127 """ 

1128 

1129 results = [ 1afcdbeg

1130 CompletionItem(name, help=command.get_short_help_str()) 

1131 for name, command in _click.core._complete_visible_commands(ctx, incomplete) 

1132 ] 

1133 results.extend(super().shell_complete(ctx, incomplete)) 1afcdbeg

1134 return results 1afcdbeg

1135 

1136 def format_options( 1afcdbeg

1137 self, ctx: _click.Context, formatter: _click.HelpFormatter 

1138 ) -> None: 

1139 _typer_format_options(self, ctx=ctx, formatter=formatter) 1afcdbeg

1140 self.format_commands(ctx, formatter) 1afcdbeg

1141 

1142 def _main_shell_completion( 1afcdbeg

1143 self, 

1144 ctx_args: MutableMapping[str, Any], 

1145 prog_name: str, 

1146 complete_var: str | None = None, 

1147 ) -> None: 

1148 _typer_main_shell_completion( 1afcdbeg

1149 self, ctx_args=ctx_args, prog_name=prog_name, complete_var=complete_var 

1150 ) 

1151 

1152 def _click_resolve_command( 1afcdbeg

1153 self, ctx: _click.Context, args: list[str] 

1154 ) -> tuple[str | None, _click.Command | None, list[str]]: 

1155 cmd_name = args[0] 1afcdbeg

1156 original_cmd_name = cmd_name 1afcdbeg

1157 

1158 # Get the command 

1159 cmd = self.get_command(ctx, cmd_name) 1afcdbeg

1160 

1161 if cmd is None and ctx.token_normalize_func is not None: 1afcdbeg

1162 cmd_name = ctx.token_normalize_func(cmd_name) 1afcdbeg

1163 cmd = self.get_command(ctx, cmd_name) 1afcdbeg

1164 

1165 if cmd is None and not ctx.resilient_parsing: 1afcdbeg

1166 if _split_opt(cmd_name)[0]: 1afcdbeg

1167 self.parse_args(ctx, args) 1afcdbeg

1168 ctx.fail(_("No such command {name!r}.").format(name=original_cmd_name)) 1afcdbeg

1169 return cmd_name if cmd else None, cmd, args[1:] 1afcdbeg

1170 

1171 def resolve_command( 1afcdbeg

1172 self, ctx: _click.Context, args: list[str] 

1173 ) -> tuple[str | None, _click.Command | None, list[str]]: 

1174 try: 1afcdbeg

1175 return self._click_resolve_command(ctx, args) 1afcdbeg

1176 except _click.exceptions.UsageError as e: 1afcdbeg

1177 if self.suggest_commands: 1afcdbeg

1178 available_commands = list(self.commands.keys()) 1afcdbeg

1179 if available_commands and args: 1afcdbeg

1180 typo = args[0] 1afcdbeg

1181 matches = get_close_matches(typo, available_commands) 1afcdbeg

1182 if matches: 1afcdbeg

1183 suggestions = ", ".join(f"{m!r}" for m in matches) 1afcdbeg

1184 message = e.message.rstrip(".") 1afcdbeg

1185 e.message = f"{message}. Did you mean {suggestions}?" 1afcdbeg

1186 raise 1afcdbeg

1187 

1188 def main( 1afcdbeg

1189 self, 

1190 args: Sequence[str] | None = None, 

1191 prog_name: str | None = None, 

1192 complete_var: str | None = None, 

1193 standalone_mode: bool = True, 

1194 windows_expand_args: bool = True, 

1195 **extra: Any, 

1196 ) -> Any: 

1197 return _main( 1afcdbeg

1198 self, 

1199 args=args, 

1200 prog_name=prog_name, 

1201 complete_var=complete_var, 

1202 standalone_mode=standalone_mode, 

1203 windows_expand_args=windows_expand_args, 

1204 rich_markup_mode=self.rich_markup_mode, 

1205 **extra, 

1206 ) 

1207 

1208 def format_help(self, ctx: _click.Context, formatter: _click.HelpFormatter) -> None: 1afcdbeg

1209 if not HAS_RICH or self.rich_markup_mode is None: 1afcdbeg

1210 return super().format_help(ctx, formatter) 1afcdbeg

1211 from . import rich_utils 1afcdbeg

1212 

1213 return rich_utils.rich_format_help( 1afcdbeg

1214 obj=self, 

1215 ctx=ctx, 

1216 markup_mode=self.rich_markup_mode, 

1217 ) 

1218 

1219 def list_commands(self, ctx: _click.Context) -> list[str]: 1afcdbeg

1220 """Returns a list of subcommand names, maintaining the original order of creation (cf Issue #933)""" 

1221 return [n for n, c in self.commands.items()] 1afcdbeg