Coverage for typer/main.py: 100%

479 statements  

« prev     ^ index     » next       coverage.py v7.5.4, created at 2024-06-24 00:17 +0000

1import inspect 1hceafbdg

2import os 1hceafbdg

3import sys 1hceafbdg

4import traceback 1hceafbdg

5from datetime import datetime 1hceafbdg

6from enum import Enum 1hceafbdg

7from functools import update_wrapper 1hceafbdg

8from pathlib import Path 1hceafbdg

9from traceback import FrameSummary, StackSummary 1hceafbdg

10from types import TracebackType 1hceafbdg

11from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Type, Union 1hceafbdg

12from uuid import UUID 1hceafbdg

13 

14import click 1hceafbdg

15 

16from .completion import get_completion_inspect_parameters 1hceafbdg

17from .core import MarkupMode, TyperArgument, TyperCommand, TyperGroup, TyperOption 1hceafbdg

18from .models import ( 1hceafbdg

19 AnyType, 

20 ArgumentInfo, 

21 CommandFunctionType, 

22 CommandInfo, 

23 Default, 

24 DefaultPlaceholder, 

25 DeveloperExceptionConfig, 

26 FileBinaryRead, 

27 FileBinaryWrite, 

28 FileText, 

29 FileTextWrite, 

30 NoneType, 

31 OptionInfo, 

32 ParameterInfo, 

33 ParamMeta, 

34 Required, 

35 TyperInfo, 

36) 

37from .utils import get_params_from_function 1hceafbdg

38 

39try: 1hceafbdg

40 import rich 1hceafbdg

41 from rich.console import Console 1hceafbdg

42 from rich.traceback import Traceback 1hceafbdg

43 

44 console_stderr = Console(stderr=True) 1hceafbdg

45 

46except ImportError: # pragma: no cover 

47 rich = None # type: ignore 

48 

49_original_except_hook = sys.excepthook 1hceafbdg

50_typer_developer_exception_attr_name = "__typer_developer_exception__" 1hceafbdg

51 

52 

53def except_hook( 1ceafbdg

54 exc_type: Type[BaseException], exc_value: BaseException, tb: Optional[TracebackType] 

55) -> None: 

56 exception_config: Union[DeveloperExceptionConfig, None] = getattr( 1hceafbdg

57 exc_value, _typer_developer_exception_attr_name, None 

58 ) 

59 standard_traceback = os.getenv("_TYPER_STANDARD_TRACEBACK") 1hceafbdg

60 if ( 1cabd

61 standard_traceback 

62 or not exception_config 

63 or not exception_config.pretty_exceptions_enable 

64 ): 

65 _original_except_hook(exc_type, exc_value, tb) 1hceafbdg

66 return 1hceafbdg

67 typer_path = os.path.dirname(__file__) 1hceafbdg

68 click_path = os.path.dirname(click.__file__) 1hceafbdg

69 supress_internal_dir_names = [typer_path, click_path] 1hceafbdg

70 exc = exc_value 1hceafbdg

71 if rich: 1hceafbdg

72 rich_tb = Traceback.from_exception( 1hceafbdg

73 type(exc), 

74 exc, 

75 exc.__traceback__, 

76 show_locals=exception_config.pretty_exceptions_show_locals, 

77 suppress=supress_internal_dir_names, 

78 ) 

79 console_stderr.print(rich_tb) 1hceafbdg

80 return 1hceafbdg

81 tb_exc = traceback.TracebackException.from_exception(exc) 1hceafbdg

82 stack: List[FrameSummary] = [] 1hceafbdg

83 for frame in tb_exc.stack: 1hceafbdg

84 if any(frame.filename.startswith(path) for path in supress_internal_dir_names): 1hceafbdg

85 if not exception_config.pretty_exceptions_short: 1hceafbdg

86 # Hide the line for internal libraries, Typer and Click 

87 stack.append( 1hceafbdg

88 traceback.FrameSummary( 

89 filename=frame.filename, 

90 lineno=frame.lineno, 

91 name=frame.name, 

92 line="", 

93 ) 

94 ) 

95 else: 

96 stack.append(frame) 1hceafbdg

97 # Type ignore ref: https://github.com/python/typeshed/pull/8244 

98 final_stack_summary = StackSummary.from_list(stack) 1hceafbdg

99 tb_exc.stack = final_stack_summary 1hceafbdg

100 for line in tb_exc.format(): 1hceafbdg

101 print(line, file=sys.stderr) 1hceafbdg

102 return 1hceafbdg

103 

104 

105def get_install_completion_arguments() -> Tuple[click.Parameter, click.Parameter]: 1hceafbdg

106 install_param, show_param = get_completion_inspect_parameters() 1hceafbdg

107 click_install_param, _ = get_click_param(install_param) 1hceafbdg

108 click_show_param, _ = get_click_param(show_param) 1hceafbdg

109 return click_install_param, click_show_param 1hceafbdg

110 

111 

112class Typer: 1hceafbdg

113 def __init__( 1ceafbdg

114 self, 

115 *, 

116 name: Optional[str] = Default(None), 

117 cls: Optional[Type[TyperGroup]] = Default(None), 

118 invoke_without_command: bool = Default(False), 

119 no_args_is_help: bool = Default(False), 

120 subcommand_metavar: Optional[str] = Default(None), 

121 chain: bool = Default(False), 

122 result_callback: Optional[Callable[..., Any]] = Default(None), 

123 # Command 

124 context_settings: Optional[Dict[Any, Any]] = Default(None), 

125 callback: Optional[Callable[..., Any]] = Default(None), 

126 help: Optional[str] = Default(None), 

127 epilog: Optional[str] = Default(None), 

128 short_help: Optional[str] = Default(None), 

129 options_metavar: str = Default("[OPTIONS]"), 

130 add_help_option: bool = Default(True), 

131 hidden: bool = Default(False), 

132 deprecated: bool = Default(False), 

133 add_completion: bool = True, 

134 # Rich settings 

135 rich_markup_mode: MarkupMode = None, 

136 rich_help_panel: Union[str, None] = Default(None), 

137 pretty_exceptions_enable: bool = True, 

138 pretty_exceptions_show_locals: bool = True, 

139 pretty_exceptions_short: bool = True, 

140 ): 

141 self._add_completion = add_completion 1hceafbdg

142 self.rich_markup_mode: MarkupMode = rich_markup_mode 1hceafbdg

143 self.rich_help_panel = rich_help_panel 1hceafbdg

144 self.pretty_exceptions_enable = pretty_exceptions_enable 1hceafbdg

145 self.pretty_exceptions_show_locals = pretty_exceptions_show_locals 1hceafbdg

146 self.pretty_exceptions_short = pretty_exceptions_short 1hceafbdg

147 self.info = TyperInfo( 1hceafbdg

148 name=name, 

149 cls=cls, 

150 invoke_without_command=invoke_without_command, 

151 no_args_is_help=no_args_is_help, 

152 subcommand_metavar=subcommand_metavar, 

153 chain=chain, 

154 result_callback=result_callback, 

155 context_settings=context_settings, 

156 callback=callback, 

157 help=help, 

158 epilog=epilog, 

159 short_help=short_help, 

160 options_metavar=options_metavar, 

161 add_help_option=add_help_option, 

162 hidden=hidden, 

163 deprecated=deprecated, 

164 ) 

165 self.registered_groups: List[TyperInfo] = [] 1hceafbdg

166 self.registered_commands: List[CommandInfo] = [] 1hceafbdg

167 self.registered_callback: Optional[TyperInfo] = None 1hceafbdg

168 

169 def callback( 1ceafbdg

170 self, 

171 name: Optional[str] = Default(None), 

172 *, 

173 cls: Optional[Type[TyperGroup]] = Default(None), 

174 invoke_without_command: bool = Default(False), 

175 no_args_is_help: bool = Default(False), 

176 subcommand_metavar: Optional[str] = Default(None), 

177 chain: bool = Default(False), 

178 result_callback: Optional[Callable[..., Any]] = Default(None), 

179 # Command 

180 context_settings: Optional[Dict[Any, Any]] = Default(None), 

181 help: Optional[str] = Default(None), 

182 epilog: Optional[str] = Default(None), 

183 short_help: Optional[str] = Default(None), 

184 options_metavar: str = Default("[OPTIONS]"), 

185 add_help_option: bool = Default(True), 

186 hidden: bool = Default(False), 

187 deprecated: bool = Default(False), 

188 # Rich settings 

189 rich_help_panel: Union[str, None] = Default(None), 

190 ) -> Callable[[CommandFunctionType], CommandFunctionType]: 

191 def decorator(f: CommandFunctionType) -> CommandFunctionType: 1hceafbdg

192 self.registered_callback = TyperInfo( 1hceafbdg

193 name=name, 

194 cls=cls, 

195 invoke_without_command=invoke_without_command, 

196 no_args_is_help=no_args_is_help, 

197 subcommand_metavar=subcommand_metavar, 

198 chain=chain, 

199 result_callback=result_callback, 

200 context_settings=context_settings, 

201 callback=f, 

202 help=help, 

203 epilog=epilog, 

204 short_help=short_help, 

205 options_metavar=options_metavar, 

206 add_help_option=add_help_option, 

207 hidden=hidden, 

208 deprecated=deprecated, 

209 rich_help_panel=rich_help_panel, 

210 ) 

211 return f 1hceafbdg

212 

213 return decorator 1hceafbdg

214 

215 def command( 1ceafbdg

216 self, 

217 name: Optional[str] = None, 

218 *, 

219 cls: Optional[Type[TyperCommand]] = None, 

220 context_settings: Optional[Dict[Any, Any]] = None, 

221 help: Optional[str] = None, 

222 epilog: Optional[str] = None, 

223 short_help: Optional[str] = None, 

224 options_metavar: str = "[OPTIONS]", 

225 add_help_option: bool = True, 

226 no_args_is_help: bool = False, 

227 hidden: bool = False, 

228 deprecated: bool = False, 

229 # Rich settings 

230 rich_help_panel: Union[str, None] = Default(None), 

231 ) -> Callable[[CommandFunctionType], CommandFunctionType]: 

232 if cls is None: 1hceafbdg

233 cls = TyperCommand 1hceafbdg

234 

235 def decorator(f: CommandFunctionType) -> CommandFunctionType: 1hceafbdg

236 self.registered_commands.append( 1hceafbdg

237 CommandInfo( 

238 name=name, 

239 cls=cls, 

240 context_settings=context_settings, 

241 callback=f, 

242 help=help, 

243 epilog=epilog, 

244 short_help=short_help, 

245 options_metavar=options_metavar, 

246 add_help_option=add_help_option, 

247 no_args_is_help=no_args_is_help, 

248 hidden=hidden, 

249 deprecated=deprecated, 

250 # Rich settings 

251 rich_help_panel=rich_help_panel, 

252 ) 

253 ) 

254 return f 1hceafbdg

255 

256 return decorator 1hceafbdg

257 

258 def add_typer( 1ceafbdg

259 self, 

260 typer_instance: "Typer", 

261 *, 

262 name: Optional[str] = Default(None), 

263 cls: Optional[Type[TyperGroup]] = Default(None), 

264 invoke_without_command: bool = Default(False), 

265 no_args_is_help: bool = Default(False), 

266 subcommand_metavar: Optional[str] = Default(None), 

267 chain: bool = Default(False), 

268 result_callback: Optional[Callable[..., Any]] = Default(None), 

269 # Command 

270 context_settings: Optional[Dict[Any, Any]] = Default(None), 

271 callback: Optional[Callable[..., Any]] = Default(None), 

272 help: Optional[str] = Default(None), 

273 epilog: Optional[str] = Default(None), 

274 short_help: Optional[str] = Default(None), 

275 options_metavar: str = Default("[OPTIONS]"), 

276 add_help_option: bool = Default(True), 

277 hidden: bool = Default(False), 

278 deprecated: bool = Default(False), 

279 # Rich settings 

280 rich_help_panel: Union[str, None] = Default(None), 

281 ) -> None: 

282 self.registered_groups.append( 1hceafbdg

283 TyperInfo( 

284 typer_instance, 

285 name=name, 

286 cls=cls, 

287 invoke_without_command=invoke_without_command, 

288 no_args_is_help=no_args_is_help, 

289 subcommand_metavar=subcommand_metavar, 

290 chain=chain, 

291 result_callback=result_callback, 

292 context_settings=context_settings, 

293 callback=callback, 

294 help=help, 

295 epilog=epilog, 

296 short_help=short_help, 

297 options_metavar=options_metavar, 

298 add_help_option=add_help_option, 

299 hidden=hidden, 

300 deprecated=deprecated, 

301 rich_help_panel=rich_help_panel, 

302 ) 

303 ) 

304 

305 def __call__(self, *args: Any, **kwargs: Any) -> Any: 1hceafbdg

306 if sys.excepthook != except_hook: 1hceafbdg

307 sys.excepthook = except_hook 1hceafbdg

308 try: 1hceafbdg

309 return get_command(self)(*args, **kwargs) 1hceafbdg

310 except Exception as e: 1hceafbdg

311 # Set a custom attribute to tell the hook to show nice exceptions for user 

312 # code. An alternative/first implementation was a custom exception with 

313 # raise custom_exc from e 

314 # but that means the last error shown is the custom exception, not the 

315 # actual error. This trick improves developer experience by showing the 

316 # actual error last. 

317 setattr( 1hceafbdg

318 e, 

319 _typer_developer_exception_attr_name, 

320 DeveloperExceptionConfig( 

321 pretty_exceptions_enable=self.pretty_exceptions_enable, 

322 pretty_exceptions_show_locals=self.pretty_exceptions_show_locals, 

323 pretty_exceptions_short=self.pretty_exceptions_short, 

324 ), 

325 ) 

326 raise e 1hceafbdg

327 

328 

329def get_group(typer_instance: Typer) -> TyperGroup: 1hceafbdg

330 group = get_group_from_info( 1hceafbdg

331 TyperInfo(typer_instance), 

332 pretty_exceptions_short=typer_instance.pretty_exceptions_short, 

333 rich_markup_mode=typer_instance.rich_markup_mode, 

334 ) 

335 return group 1hceafbdg

336 

337 

338def get_command(typer_instance: Typer) -> click.Command: 1hceafbdg

339 if typer_instance._add_completion: 1hceafbdg

340 click_install_param, click_show_param = get_install_completion_arguments() 1hceafbdg

341 if ( 1cabd

342 typer_instance.registered_callback 

343 or typer_instance.info.callback 

344 or typer_instance.registered_groups 

345 or len(typer_instance.registered_commands) > 1 

346 ): 

347 # Create a Group 

348 click_command: click.Command = get_group(typer_instance) 1hceafbdg

349 if typer_instance._add_completion: 1hceafbdg

350 click_command.params.append(click_install_param) 1hceafbdg

351 click_command.params.append(click_show_param) 1hceafbdg

352 return click_command 1hceafbdg

353 elif len(typer_instance.registered_commands) == 1: 1hceafbdg

354 # Create a single Command 

355 single_command = typer_instance.registered_commands[0] 1hceafbdg

356 

357 if not single_command.context_settings and not isinstance( 1hceafbdg

358 typer_instance.info.context_settings, DefaultPlaceholder 

359 ): 

360 single_command.context_settings = typer_instance.info.context_settings 1hceafbdg

361 

362 click_command = get_command_from_info( 1hceafbdg

363 single_command, 

364 pretty_exceptions_short=typer_instance.pretty_exceptions_short, 

365 rich_markup_mode=typer_instance.rich_markup_mode, 

366 ) 

367 if typer_instance._add_completion: 1hceafbdg

368 click_command.params.append(click_install_param) 1hceafbdg

369 click_command.params.append(click_show_param) 1hceafbdg

370 return click_command 1hceafbdg

371 raise RuntimeError( 

372 "Could not get a command for this Typer instance" 

373 ) # pragma: no cover 

374 

375 

376def get_group_name(typer_info: TyperInfo) -> Optional[str]: 1hceafbdg

377 if typer_info.callback: 1hceafbdg

378 # Priority 1: Callback passed in app.add_typer() 

379 return get_command_name(typer_info.callback.__name__) 1hceafbdg

380 if typer_info.typer_instance: 1hceafbdg

381 registered_callback = typer_info.typer_instance.registered_callback 1hceafbdg

382 if registered_callback: 1hceafbdg

383 if registered_callback.callback: 1hceafbdg

384 # Priority 2: Callback passed in @subapp.callback() 

385 return get_command_name(registered_callback.callback.__name__) 1hceafbdg

386 if typer_info.typer_instance.info.callback: 1hceafbdg

387 return get_command_name(typer_info.typer_instance.info.callback.__name__) 1hceafbdg

388 return None 1hceafbdg

389 

390 

391def solve_typer_info_help(typer_info: TyperInfo) -> str: 1hceafbdg

392 # Priority 1: Explicit value was set in app.add_typer() 

393 if not isinstance(typer_info.help, DefaultPlaceholder): 1hceafbdg

394 return inspect.cleandoc(typer_info.help or "") 1hceafbdg

395 # Priority 2: Explicit value was set in sub_app.callback() 

396 try: 1hceafbdg

397 callback_help = typer_info.typer_instance.registered_callback.help 1hceafbdg

398 if not isinstance(callback_help, DefaultPlaceholder): 1hceafbdg

399 return inspect.cleandoc(callback_help or "") 1hceafbdg

400 except AttributeError: 1hceafbdg

401 pass 1hceafbdg

402 # Priority 3: Explicit value was set in sub_app = typer.Typer() 

403 try: 1hceafbdg

404 instance_help = typer_info.typer_instance.info.help 1hceafbdg

405 if not isinstance(instance_help, DefaultPlaceholder): 1hceafbdg

406 return inspect.cleandoc(instance_help or "") 1hceafbdg

407 except AttributeError: 1hceafbdg

408 pass 1hceafbdg

409 # Priority 4: Implicit inference from callback docstring in app.add_typer() 

410 if typer_info.callback: 1hceafbdg

411 doc = inspect.getdoc(typer_info.callback) 1hceafbdg

412 if doc: 1hceafbdg

413 return doc 1hceafbdg

414 # Priority 5: Implicit inference from callback docstring in @app.callback() 

415 try: 1hceafbdg

416 callback = typer_info.typer_instance.registered_callback.callback 1hceafbdg

417 if not isinstance(callback, DefaultPlaceholder): 1hceafbdg

418 doc = inspect.getdoc(callback or "") 1hceafbdg

419 if doc: 1hceafbdg

420 return doc 1hceafbdg

421 except AttributeError: 1hceafbdg

422 pass 1hceafbdg

423 # Priority 6: Implicit inference from callback docstring in typer.Typer() 

424 try: 1hceafbdg

425 instance_callback = typer_info.typer_instance.info.callback 1hceafbdg

426 if not isinstance(instance_callback, DefaultPlaceholder): 1hceafbdg

427 doc = inspect.getdoc(instance_callback) 1hceafbdg

428 if doc: 1hceafbdg

429 return doc 1hceafbdg

430 except AttributeError: 1hceafbdg

431 pass 1hceafbdg

432 # Value not set, use the default 

433 return typer_info.help.value 1hceafbdg

434 

435 

436def solve_typer_info_defaults(typer_info: TyperInfo) -> TyperInfo: 1hceafbdg

437 values: Dict[str, Any] = {} 1hceafbdg

438 name = None 1hceafbdg

439 for name, value in typer_info.__dict__.items(): 1hceafbdg

440 # Priority 1: Value was set in app.add_typer() 

441 if not isinstance(value, DefaultPlaceholder): 1hceafbdg

442 values[name] = value 1hceafbdg

443 continue 1hceafbdg

444 # Priority 2: Value was set in @subapp.callback() 

445 try: 1hceafbdg

446 callback_value = getattr( 1hceafbdg

447 typer_info.typer_instance.registered_callback, # type: ignore 

448 name, 

449 ) 

450 if not isinstance(callback_value, DefaultPlaceholder): 1hceafbdg

451 values[name] = callback_value 1hceafbdg

452 continue 1hceafbdg

453 except AttributeError: 1hceafbdg

454 pass 1hceafbdg

455 # Priority 3: Value set in subapp = typer.Typer() 

456 try: 1hceafbdg

457 instance_value = getattr( 1hceafbdg

458 typer_info.typer_instance.info, # type: ignore 

459 name, 

460 ) 

461 if not isinstance(instance_value, DefaultPlaceholder): 1hceafbdg

462 values[name] = instance_value 1hceafbdg

463 continue 1hceafbdg

464 except AttributeError: 1hceafbdg

465 pass 1hceafbdg

466 # Value not set, use the default 

467 values[name] = value.value 1hceafbdg

468 if values["name"] is None: 1hceafbdg

469 values["name"] = get_group_name(typer_info) 1hceafbdg

470 values["help"] = solve_typer_info_help(typer_info) 1hceafbdg

471 return TyperInfo(**values) 1hceafbdg

472 

473 

474def get_group_from_info( 1ceafbdg

475 group_info: TyperInfo, 

476 *, 

477 pretty_exceptions_short: bool, 

478 rich_markup_mode: MarkupMode, 

479) -> TyperGroup: 

480 assert ( 1cabd

481 group_info.typer_instance 

482 ), "A Typer instance is needed to generate a Click Group" 

483 commands: Dict[str, click.Command] = {} 1hceafbdg

484 for command_info in group_info.typer_instance.registered_commands: 1hceafbdg

485 command = get_command_from_info( 1hceafbdg

486 command_info=command_info, 

487 pretty_exceptions_short=pretty_exceptions_short, 

488 rich_markup_mode=rich_markup_mode, 

489 ) 

490 if command.name: 1hceafbdg

491 commands[command.name] = command 1hceafbdg

492 for sub_group_info in group_info.typer_instance.registered_groups: 1hceafbdg

493 sub_group = get_group_from_info( 1hceafbdg

494 sub_group_info, 

495 pretty_exceptions_short=pretty_exceptions_short, 

496 rich_markup_mode=rich_markup_mode, 

497 ) 

498 if sub_group.name: 1hceafbdg

499 commands[sub_group.name] = sub_group 1hceafbdg

500 solved_info = solve_typer_info_defaults(group_info) 1hceafbdg

501 ( 1ceafbdg

502 params, 

503 convertors, 

504 context_param_name, 

505 ) = get_params_convertors_ctx_param_name_from_function(solved_info.callback) 

506 cls = solved_info.cls or TyperGroup 1hceafbdg

507 assert issubclass(cls, TyperGroup) 1hceafbdg

508 group = cls( 1hceafbdg

509 name=solved_info.name or "", 

510 commands=commands, 

511 invoke_without_command=solved_info.invoke_without_command, 

512 no_args_is_help=solved_info.no_args_is_help, 

513 subcommand_metavar=solved_info.subcommand_metavar, 

514 chain=solved_info.chain, 

515 result_callback=solved_info.result_callback, 

516 context_settings=solved_info.context_settings, 

517 callback=get_callback( 

518 callback=solved_info.callback, 

519 params=params, 

520 convertors=convertors, 

521 context_param_name=context_param_name, 

522 pretty_exceptions_short=pretty_exceptions_short, 

523 ), 

524 params=params, 

525 help=solved_info.help, 

526 epilog=solved_info.epilog, 

527 short_help=solved_info.short_help, 

528 options_metavar=solved_info.options_metavar, 

529 add_help_option=solved_info.add_help_option, 

530 hidden=solved_info.hidden, 

531 deprecated=solved_info.deprecated, 

532 rich_markup_mode=rich_markup_mode, 

533 # Rich settings 

534 rich_help_panel=solved_info.rich_help_panel, 

535 ) 

536 return group 1hceafbdg

537 

538 

539def get_command_name(name: str) -> str: 1hceafbdg

540 return name.lower().replace("_", "-") 1hceafbdg

541 

542 

543def get_params_convertors_ctx_param_name_from_function( 1ceafbdg

544 callback: Optional[Callable[..., Any]], 

545) -> Tuple[List[Union[click.Argument, click.Option]], Dict[str, Any], Optional[str]]: 

546 params = [] 1hceafbdg

547 convertors = {} 1hceafbdg

548 context_param_name = None 1hceafbdg

549 if callback: 1hceafbdg

550 parameters = get_params_from_function(callback) 1hceafbdg

551 for param_name, param in parameters.items(): 1hceafbdg

552 if lenient_issubclass(param.annotation, click.Context): 1hceafbdg

553 context_param_name = param_name 1hceafbdg

554 continue 1hceafbdg

555 click_param, convertor = get_click_param(param) 1hceafbdg

556 if convertor: 1hceafbdg

557 convertors[param_name] = convertor 1hceafbdg

558 params.append(click_param) 1hceafbdg

559 return params, convertors, context_param_name 1hceafbdg

560 

561 

562def get_command_from_info( 1ceafbdg

563 command_info: CommandInfo, 

564 *, 

565 pretty_exceptions_short: bool, 

566 rich_markup_mode: MarkupMode, 

567) -> click.Command: 

568 assert command_info.callback, "A command must have a callback function" 1hceafbdg

569 name = command_info.name or get_command_name(command_info.callback.__name__) 1hceafbdg

570 use_help = command_info.help 1hceafbdg

571 if use_help is None: 1hceafbdg

572 use_help = inspect.getdoc(command_info.callback) 1hceafbdg

573 else: 

574 use_help = inspect.cleandoc(use_help) 1hceafbdg

575 ( 1ceafbdg

576 params, 

577 convertors, 

578 context_param_name, 

579 ) = get_params_convertors_ctx_param_name_from_function(command_info.callback) 

580 cls = command_info.cls or TyperCommand 1hceafbdg

581 command = cls( 1hceafbdg

582 name=name, 

583 context_settings=command_info.context_settings, 

584 callback=get_callback( 

585 callback=command_info.callback, 

586 params=params, 

587 convertors=convertors, 

588 context_param_name=context_param_name, 

589 pretty_exceptions_short=pretty_exceptions_short, 

590 ), 

591 params=params, # type: ignore 

592 help=use_help, 

593 epilog=command_info.epilog, 

594 short_help=command_info.short_help, 

595 options_metavar=command_info.options_metavar, 

596 add_help_option=command_info.add_help_option, 

597 no_args_is_help=command_info.no_args_is_help, 

598 hidden=command_info.hidden, 

599 deprecated=command_info.deprecated, 

600 rich_markup_mode=rich_markup_mode, 

601 # Rich settings 

602 rich_help_panel=command_info.rich_help_panel, 

603 ) 

604 return command 1hceafbdg

605 

606 

607def determine_type_convertor(type_: Any) -> Optional[Callable[[Any], Any]]: 1hceafbdg

608 convertor: Optional[Callable[[Any], Any]] = None 1hceafbdg

609 if lenient_issubclass(type_, Path): 1hceafbdg

610 convertor = param_path_convertor 1hceafbdg

611 if lenient_issubclass(type_, Enum): 1hceafbdg

612 convertor = generate_enum_convertor(type_) 1hceafbdg

613 return convertor 1hceafbdg

614 

615 

616def param_path_convertor(value: Optional[str] = None) -> Optional[Path]: 1hceafbdg

617 if value is not None: 1hceafbdg

618 return Path(value) 1hceafbdg

619 return None 1hceafbdg

620 

621 

622def generate_enum_convertor(enum: Type[Enum]) -> Callable[[Any], Any]: 1hceafbdg

623 val_map = {str(val.value): val for val in enum} 1hceafbdg

624 

625 def convertor(value: Any) -> Any: 1hceafbdg

626 if value is not None: 1hceafbdg

627 val = str(value) 1hceafbdg

628 if val in val_map: 1hceafbdg

629 key = val_map[val] 1hceafbdg

630 return enum(key) 1hceafbdg

631 

632 return convertor 1hceafbdg

633 

634 

635def generate_list_convertor( 1ceafbdg

636 convertor: Optional[Callable[[Any], Any]], default_value: Optional[Any] 

637) -> Callable[[Sequence[Any]], Optional[List[Any]]]: 

638 def internal_convertor(value: Sequence[Any]) -> Optional[List[Any]]: 1hceafbdg

639 if default_value is None and len(value) == 0: 1hceafbdg

640 return None 1hceafbdg

641 return [convertor(v) if convertor else v for v in value] 1hceafbdg

642 

643 return internal_convertor 1hceafbdg

644 

645 

646def generate_tuple_convertor( 1ceafbdg

647 types: Sequence[Any], 

648) -> Callable[[Optional[Tuple[Any, ...]]], Optional[Tuple[Any, ...]]]: 

649 convertors = [determine_type_convertor(type_) for type_ in types] 1hceafbdg

650 

651 def internal_convertor( 1ceafbdg

652 param_args: Optional[Tuple[Any, ...]], 

653 ) -> Optional[Tuple[Any, ...]]: 

654 if param_args is None: 1hceafbdg

655 return None 1hceafbdg

656 return tuple( 1hceafbdg

657 convertor(arg) if convertor else arg 

658 for (convertor, arg) in zip(convertors, param_args) 

659 ) 

660 

661 return internal_convertor 1hceafbdg

662 

663 

664def get_callback( 1ceafbdg

665 *, 

666 callback: Optional[Callable[..., Any]] = None, 

667 params: Sequence[click.Parameter] = [], 

668 convertors: Optional[Dict[str, Callable[[str], Any]]] = None, 

669 context_param_name: Optional[str] = None, 

670 pretty_exceptions_short: bool, 

671) -> Optional[Callable[..., Any]]: 

672 use_convertors = convertors or {} 1hceafbdg

673 if not callback: 1hceafbdg

674 return None 1hceafbdg

675 parameters = get_params_from_function(callback) 1hceafbdg

676 use_params: Dict[str, Any] = {} 1hceafbdg

677 for param_name in parameters: 1hceafbdg

678 use_params[param_name] = None 1hceafbdg

679 for param in params: 1hceafbdg

680 if param.name: 1hceafbdg

681 use_params[param.name] = param.default 1hceafbdg

682 

683 def wrapper(**kwargs: Any) -> Any: 1hceafbdg

684 _rich_traceback_guard = pretty_exceptions_short # noqa: F841 1hceafbdg

685 for k, v in kwargs.items(): 1hceafbdg

686 if k in use_convertors: 1hceafbdg

687 use_params[k] = use_convertors[k](v) 1hceafbdg

688 else: 

689 use_params[k] = v 1hceafbdg

690 if context_param_name: 1hceafbdg

691 use_params[context_param_name] = click.get_current_context() 1hceafbdg

692 return callback(**use_params) 1hceafbdg

693 

694 update_wrapper(wrapper, callback) 1hceafbdg

695 return wrapper 1hceafbdg

696 

697 

698def get_click_type( 1ceafbdg

699 *, annotation: Any, parameter_info: ParameterInfo 

700) -> click.ParamType: 

701 if parameter_info.click_type is not None: 1hceafbdg

702 return parameter_info.click_type 1hceafbdg

703 

704 elif parameter_info.parser is not None: 1hceafbdg

705 return click.types.FuncParamType(parameter_info.parser) 1hceafbdg

706 

707 elif annotation == str: 1hceafbdg

708 return click.STRING 1hceafbdg

709 elif annotation == int: 1hceafbdg

710 if parameter_info.min is not None or parameter_info.max is not None: 1hceafbdg

711 min_ = None 1hceafbdg

712 max_ = None 1hceafbdg

713 if parameter_info.min is not None: 1hceafbdg

714 min_ = int(parameter_info.min) 1hceafbdg

715 if parameter_info.max is not None: 1hceafbdg

716 max_ = int(parameter_info.max) 1hceafbdg

717 return click.IntRange(min=min_, max=max_, clamp=parameter_info.clamp) 1hceafbdg

718 else: 

719 return click.INT 1hceafbdg

720 elif annotation == float: 1hceafbdg

721 if parameter_info.min is not None or parameter_info.max is not None: 1hceafbdg

722 return click.FloatRange( 1hceafbdg

723 min=parameter_info.min, 

724 max=parameter_info.max, 

725 clamp=parameter_info.clamp, 

726 ) 

727 else: 

728 return click.FLOAT 1hceafbdg

729 elif annotation == bool: 1hceafbdg

730 return click.BOOL 1hceafbdg

731 elif annotation == UUID: 1hceafbdg

732 return click.UUID 1hceafbdg

733 elif annotation == datetime: 1hceafbdg

734 return click.DateTime(formats=parameter_info.formats) 1hceafbdg

735 elif ( 1ab

736 annotation == Path 

737 or parameter_info.allow_dash 

738 or parameter_info.path_type 

739 or parameter_info.resolve_path 

740 ): 

741 return click.Path( 1hceafbdg

742 exists=parameter_info.exists, 

743 file_okay=parameter_info.file_okay, 

744 dir_okay=parameter_info.dir_okay, 

745 writable=parameter_info.writable, 

746 readable=parameter_info.readable, 

747 resolve_path=parameter_info.resolve_path, 

748 allow_dash=parameter_info.allow_dash, 

749 path_type=parameter_info.path_type, 

750 ) 

751 elif lenient_issubclass(annotation, FileTextWrite): 1hceafbdg

752 return click.File( 1hceafbdg

753 mode=parameter_info.mode or "w", 

754 encoding=parameter_info.encoding, 

755 errors=parameter_info.errors, 

756 lazy=parameter_info.lazy, 

757 atomic=parameter_info.atomic, 

758 ) 

759 elif lenient_issubclass(annotation, FileText): 1hceafbdg

760 return click.File( 1hceafbdg

761 mode=parameter_info.mode or "r", 

762 encoding=parameter_info.encoding, 

763 errors=parameter_info.errors, 

764 lazy=parameter_info.lazy, 

765 atomic=parameter_info.atomic, 

766 ) 

767 elif lenient_issubclass(annotation, FileBinaryRead): 1hceafbdg

768 return click.File( 1hceafbdg

769 mode=parameter_info.mode or "rb", 

770 encoding=parameter_info.encoding, 

771 errors=parameter_info.errors, 

772 lazy=parameter_info.lazy, 

773 atomic=parameter_info.atomic, 

774 ) 

775 elif lenient_issubclass(annotation, FileBinaryWrite): 1hceafbdg

776 return click.File( 1hceafbdg

777 mode=parameter_info.mode or "wb", 

778 encoding=parameter_info.encoding, 

779 errors=parameter_info.errors, 

780 lazy=parameter_info.lazy, 

781 atomic=parameter_info.atomic, 

782 ) 

783 elif lenient_issubclass(annotation, Enum): 1hceafbdg

784 return click.Choice( 1hceafbdg

785 [item.value for item in annotation], 

786 case_sensitive=parameter_info.case_sensitive, 

787 ) 

788 raise RuntimeError(f"Type not yet supported: {annotation}") # pragma: no cover 

789 

790 

791def lenient_issubclass( 1ceafbdg

792 cls: Any, class_or_tuple: Union[AnyType, Tuple[AnyType, ...]] 

793) -> bool: 

794 return isinstance(cls, type) and issubclass(cls, class_or_tuple) 1hceafbdg

795 

796 

797def get_click_param( 1ceafbdg

798 param: ParamMeta, 

799) -> Tuple[Union[click.Argument, click.Option], Any]: 

800 # First, find out what will be: 

801 # * ParamInfo (ArgumentInfo or OptionInfo) 

802 # * default_value 

803 # * required 

804 default_value = None 1hceafbdg

805 required = False 1hceafbdg

806 if isinstance(param.default, ParameterInfo): 1hceafbdg

807 parameter_info = param.default 1hceafbdg

808 if parameter_info.default == Required: 1hceafbdg

809 required = True 1hceafbdg

810 else: 

811 default_value = parameter_info.default 1hceafbdg

812 elif param.default == Required or param.default == param.empty: 1hceafbdg

813 required = True 1hceafbdg

814 parameter_info = ArgumentInfo() 1hceafbdg

815 else: 

816 default_value = param.default 1hceafbdg

817 parameter_info = OptionInfo() 1hceafbdg

818 annotation: Any = Any 1hceafbdg

819 if not param.annotation == param.empty: 1hceafbdg

820 annotation = param.annotation 1hceafbdg

821 else: 

822 annotation = str 1hceafbdg

823 main_type = annotation 1hceafbdg

824 is_list = False 1hceafbdg

825 is_tuple = False 1hceafbdg

826 parameter_type: Any = None 1hceafbdg

827 is_flag = None 1hceafbdg

828 origin = getattr(main_type, "__origin__", None) 1hceafbdg

829 if origin is not None: 1hceafbdg

830 # Handle Optional[SomeType] 

831 if origin is Union: 1hceafbdg

832 types = [] 1hceafbdg

833 for type_ in main_type.__args__: 1hceafbdg

834 if type_ is NoneType: 1hceafbdg

835 continue 1hceafbdg

836 types.append(type_) 1hceafbdg

837 assert len(types) == 1, "Typer Currently doesn't support Union types" 1hceafbdg

838 main_type = types[0] 1hceafbdg

839 origin = getattr(main_type, "__origin__", None) 1hceafbdg

840 # Handle Tuples and Lists 

841 if lenient_issubclass(origin, List): 1hceafbdg

842 main_type = main_type.__args__[0] 1hceafbdg

843 assert not getattr( 1hceafbdg

844 main_type, "__origin__", None 

845 ), "List types with complex sub-types are not currently supported" 

846 is_list = True 1hceafbdg

847 elif lenient_issubclass(origin, Tuple): # type: ignore 1hceafbdg

848 types = [] 1hceafbdg

849 for type_ in main_type.__args__: 1hceafbdg

850 assert not getattr( 1hceafbdg

851 type_, "__origin__", None 

852 ), "Tuple types with complex sub-types are not currently supported" 

853 types.append( 1hceafbdg

854 get_click_type(annotation=type_, parameter_info=parameter_info) 

855 ) 

856 parameter_type = tuple(types) 1hceafbdg

857 is_tuple = True 1hceafbdg

858 if parameter_type is None: 1hceafbdg

859 parameter_type = get_click_type( 1hceafbdg

860 annotation=main_type, parameter_info=parameter_info 

861 ) 

862 convertor = determine_type_convertor(main_type) 1hceafbdg

863 if is_list: 1hceafbdg

864 convertor = generate_list_convertor( 1hceafbdg

865 convertor=convertor, default_value=default_value 

866 ) 

867 if is_tuple: 1hceafbdg

868 convertor = generate_tuple_convertor(main_type.__args__) 1hceafbdg

869 if isinstance(parameter_info, OptionInfo): 1hceafbdg

870 if main_type is bool and parameter_info.is_flag is not False: 1hceafbdg

871 is_flag = True 1hceafbdg

872 # Click doesn't accept a flag of type bool, only None, and then it sets it 

873 # to bool internally 

874 parameter_type = None 1hceafbdg

875 default_option_name = get_command_name(param.name) 1hceafbdg

876 if is_flag: 1hceafbdg

877 default_option_declaration = ( 1ceafbdg

878 f"--{default_option_name}/--no-{default_option_name}" 

879 ) 

880 else: 

881 default_option_declaration = f"--{default_option_name}" 1hceafbdg

882 param_decls = [param.name] 1hceafbdg

883 if parameter_info.param_decls: 1hceafbdg

884 param_decls.extend(parameter_info.param_decls) 1hceafbdg

885 else: 

886 param_decls.append(default_option_declaration) 1hceafbdg

887 return ( 1ceafbdg

888 TyperOption( 

889 # Option 

890 param_decls=param_decls, 

891 show_default=parameter_info.show_default, 

892 prompt=parameter_info.prompt, 

893 confirmation_prompt=parameter_info.confirmation_prompt, 

894 prompt_required=parameter_info.prompt_required, 

895 hide_input=parameter_info.hide_input, 

896 is_flag=is_flag, 

897 flag_value=parameter_info.flag_value, 

898 multiple=is_list, 

899 count=parameter_info.count, 

900 allow_from_autoenv=parameter_info.allow_from_autoenv, 

901 type=parameter_type, 

902 help=parameter_info.help, 

903 hidden=parameter_info.hidden, 

904 show_choices=parameter_info.show_choices, 

905 show_envvar=parameter_info.show_envvar, 

906 # Parameter 

907 required=required, 

908 default=default_value, 

909 callback=get_param_callback( 

910 callback=parameter_info.callback, convertor=convertor 

911 ), 

912 metavar=parameter_info.metavar, 

913 expose_value=parameter_info.expose_value, 

914 is_eager=parameter_info.is_eager, 

915 envvar=parameter_info.envvar, 

916 shell_complete=parameter_info.shell_complete, 

917 autocompletion=get_param_completion(parameter_info.autocompletion), 

918 # Rich settings 

919 rich_help_panel=parameter_info.rich_help_panel, 

920 ), 

921 convertor, 

922 ) 

923 elif isinstance(parameter_info, ArgumentInfo): 1hceafbdg

924 param_decls = [param.name] 1hceafbdg

925 nargs = None 1hceafbdg

926 if is_list: 1hceafbdg

927 nargs = -1 1hceafbdg

928 return ( 1ceafbdg

929 TyperArgument( 

930 # Argument 

931 param_decls=param_decls, 

932 type=parameter_type, 

933 required=required, 

934 nargs=nargs, 

935 # TyperArgument 

936 show_default=parameter_info.show_default, 

937 show_choices=parameter_info.show_choices, 

938 show_envvar=parameter_info.show_envvar, 

939 help=parameter_info.help, 

940 hidden=parameter_info.hidden, 

941 # Parameter 

942 default=default_value, 

943 callback=get_param_callback( 

944 callback=parameter_info.callback, convertor=convertor 

945 ), 

946 metavar=parameter_info.metavar, 

947 expose_value=parameter_info.expose_value, 

948 is_eager=parameter_info.is_eager, 

949 envvar=parameter_info.envvar, 

950 autocompletion=get_param_completion(parameter_info.autocompletion), 

951 # Rich settings 

952 rich_help_panel=parameter_info.rich_help_panel, 

953 ), 

954 convertor, 

955 ) 

956 raise AssertionError("A click.Parameter should be returned") # pragma: no cover 

957 

958 

959def get_param_callback( 1ceafbdg

960 *, 

961 callback: Optional[Callable[..., Any]] = None, 

962 convertor: Optional[Callable[..., Any]] = None, 

963) -> Optional[Callable[..., Any]]: 

964 if not callback: 1hceafbdg

965 return None 1hceafbdg

966 parameters = get_params_from_function(callback) 1hceafbdg

967 ctx_name = None 1hceafbdg

968 click_param_name = None 1hceafbdg

969 value_name = None 1hceafbdg

970 untyped_names: List[str] = [] 1hceafbdg

971 for param_name, param_sig in parameters.items(): 1hceafbdg

972 if lenient_issubclass(param_sig.annotation, click.Context): 1hceafbdg

973 ctx_name = param_name 1hceafbdg

974 elif lenient_issubclass(param_sig.annotation, click.Parameter): 1hceafbdg

975 click_param_name = param_name 1hceafbdg

976 else: 

977 untyped_names.append(param_name) 1hceafbdg

978 # Extract value param name first 

979 if untyped_names: 1hceafbdg

980 value_name = untyped_names.pop() 1hceafbdg

981 # If context and Click param were not typed (old/Click callback style) extract them 

982 if untyped_names: 1hceafbdg

983 if ctx_name is None: 1hceafbdg

984 ctx_name = untyped_names.pop(0) 1hceafbdg

985 if click_param_name is None: 1hceafbdg

986 if untyped_names: 1hceafbdg

987 click_param_name = untyped_names.pop(0) 1hceafbdg

988 if untyped_names: 1hceafbdg

989 raise click.ClickException( 1hceafbdg

990 "Too many CLI parameter callback function parameters" 

991 ) 

992 

993 def wrapper(ctx: click.Context, param: click.Parameter, value: Any) -> Any: 1hceafbdg

994 use_params: Dict[str, Any] = {} 1hceafbdg

995 if ctx_name: 1hceafbdg

996 use_params[ctx_name] = ctx 1hceafbdg

997 if click_param_name: 1hceafbdg

998 use_params[click_param_name] = param 1hceafbdg

999 if value_name: 1hceafbdg

1000 if convertor: 1hceafbdg

1001 use_value = convertor(value) 1hceafbdg

1002 else: 

1003 use_value = value 1hceafbdg

1004 use_params[value_name] = use_value 1hceafbdg

1005 return callback(**use_params) 1hceafbdg

1006 

1007 update_wrapper(wrapper, callback) 1hceafbdg

1008 return wrapper 1hceafbdg

1009 

1010 

1011def get_param_completion( 1ceafbdg

1012 callback: Optional[Callable[..., Any]] = None, 

1013) -> Optional[Callable[..., Any]]: 

1014 if not callback: 1hceafbdg

1015 return None 1hceafbdg

1016 parameters = get_params_from_function(callback) 1hceafbdg

1017 ctx_name = None 1hceafbdg

1018 args_name = None 1hceafbdg

1019 incomplete_name = None 1hceafbdg

1020 unassigned_params = list(parameters.values()) 1hceafbdg

1021 for param_sig in unassigned_params[:]: 1hceafbdg

1022 origin = getattr(param_sig.annotation, "__origin__", None) 1hceafbdg

1023 if lenient_issubclass(param_sig.annotation, click.Context): 1hceafbdg

1024 ctx_name = param_sig.name 1hceafbdg

1025 unassigned_params.remove(param_sig) 1hceafbdg

1026 elif lenient_issubclass(origin, List): 1hceafbdg

1027 args_name = param_sig.name 1hceafbdg

1028 unassigned_params.remove(param_sig) 1hceafbdg

1029 elif lenient_issubclass(param_sig.annotation, str): 1hceafbdg

1030 incomplete_name = param_sig.name 1hceafbdg

1031 unassigned_params.remove(param_sig) 1hceafbdg

1032 # If there are still unassigned parameters (not typed), extract by name 

1033 for param_sig in unassigned_params[:]: 1hceafbdg

1034 if ctx_name is None and param_sig.name == "ctx": 1hceafbdg

1035 ctx_name = param_sig.name 1hceafbdg

1036 unassigned_params.remove(param_sig) 1hceafbdg

1037 elif args_name is None and param_sig.name == "args": 1hceafbdg

1038 args_name = param_sig.name 1hceafbdg

1039 unassigned_params.remove(param_sig) 1hceafbdg

1040 elif incomplete_name is None and param_sig.name == "incomplete": 1hceafbdg

1041 incomplete_name = param_sig.name 1hceafbdg

1042 unassigned_params.remove(param_sig) 1hceafbdg

1043 # Extract value param name first 

1044 if unassigned_params: 1hceafbdg

1045 show_params = " ".join([param.name for param in unassigned_params]) 1hceafbdg

1046 raise click.ClickException( 1hceafbdg

1047 f"Invalid autocompletion callback parameters: {show_params}" 

1048 ) 

1049 

1050 def wrapper(ctx: click.Context, args: List[str], incomplete: Optional[str]) -> Any: 1hceafbdg

1051 use_params: Dict[str, Any] = {} 1hceafbdg

1052 if ctx_name: 1hceafbdg

1053 use_params[ctx_name] = ctx 1hceafbdg

1054 if args_name: 1hceafbdg

1055 use_params[args_name] = args 1hceafbdg

1056 if incomplete_name: 1hceafbdg

1057 use_params[incomplete_name] = incomplete 1hceafbdg

1058 return callback(**use_params) 1hceafbdg

1059 

1060 update_wrapper(wrapper, callback) 1hceafbdg

1061 return wrapper 1hceafbdg

1062 

1063 

1064def run(function: Callable[..., Any]) -> None: 1hceafbdg

1065 app = Typer(add_completion=False) 1hceafbdg

1066 app.command()(function) 1hceafbdg

1067 app() 1hceafbdg