typer.main
1import inspect 2import os 3import sys 4import traceback 5from datetime import datetime 6from enum import Enum 7from functools import update_wrapper 8from pathlib import Path 9from traceback import FrameSummary, StackSummary 10from types import TracebackType 11from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Type, Union 12from uuid import UUID 13 14import click 15 16from ._typing import get_args, get_origin, is_union 17from .completion import get_completion_inspect_parameters 18from .core import ( 19 DEFAULT_MARKUP_MODE, 20 MarkupMode, 21 TyperArgument, 22 TyperCommand, 23 TyperGroup, 24 TyperOption, 25) 26from .models import ( 27 AnyType, 28 ArgumentInfo, 29 CommandFunctionType, 30 CommandInfo, 31 Default, 32 DefaultPlaceholder, 33 DeveloperExceptionConfig, 34 FileBinaryRead, 35 FileBinaryWrite, 36 FileText, 37 FileTextWrite, 38 NoneType, 39 OptionInfo, 40 ParameterInfo, 41 ParamMeta, 42 Required, 43 TyperInfo, 44) 45from .utils import get_params_from_function 46 47try: 48 import rich 49 from rich.traceback import Traceback 50 51 from . import rich_utils 52 53 console_stderr = rich_utils._get_rich_console(stderr=True) 54 55except ImportError: # pragma: no cover 56 rich = None # type: ignore 57 58_original_except_hook = sys.excepthook 59_typer_developer_exception_attr_name = "__typer_developer_exception__" 60 61 62def except_hook( 63 exc_type: Type[BaseException], exc_value: BaseException, tb: Optional[TracebackType] 64) -> None: 65 exception_config: Union[DeveloperExceptionConfig, None] = getattr( 66 exc_value, _typer_developer_exception_attr_name, None 67 ) 68 standard_traceback = os.getenv("_TYPER_STANDARD_TRACEBACK") 69 if ( 70 standard_traceback 71 or not exception_config 72 or not exception_config.pretty_exceptions_enable 73 ): 74 _original_except_hook(exc_type, exc_value, tb) 75 return 76 typer_path = os.path.dirname(__file__) 77 click_path = os.path.dirname(click.__file__) 78 supress_internal_dir_names = [typer_path, click_path] 79 exc = exc_value 80 if rich: 81 from .rich_utils import MAX_WIDTH 82 83 rich_tb = Traceback.from_exception( 84 type(exc), 85 exc, 86 exc.__traceback__, 87 show_locals=exception_config.pretty_exceptions_show_locals, 88 suppress=supress_internal_dir_names, 89 width=MAX_WIDTH, 90 ) 91 console_stderr.print(rich_tb) 92 return 93 tb_exc = traceback.TracebackException.from_exception(exc) 94 stack: List[FrameSummary] = [] 95 for frame in tb_exc.stack: 96 if any(frame.filename.startswith(path) for path in supress_internal_dir_names): 97 if not exception_config.pretty_exceptions_short: 98 # Hide the line for internal libraries, Typer and Click 99 stack.append( 100 traceback.FrameSummary( 101 filename=frame.filename, 102 lineno=frame.lineno, 103 name=frame.name, 104 line="", 105 ) 106 ) 107 else: 108 stack.append(frame) 109 # Type ignore ref: https://github.com/python/typeshed/pull/8244 110 final_stack_summary = StackSummary.from_list(stack) 111 tb_exc.stack = final_stack_summary 112 for line in tb_exc.format(): 113 print(line, file=sys.stderr) 114 return 115 116 117def get_install_completion_arguments() -> Tuple[click.Parameter, click.Parameter]: 118 install_param, show_param = get_completion_inspect_parameters() 119 click_install_param, _ = get_click_param(install_param) 120 click_show_param, _ = get_click_param(show_param) 121 return click_install_param, click_show_param 122 123 124class Typer: 125 def __init__( 126 self, 127 *, 128 name: Optional[str] = Default(None), 129 cls: Optional[Type[TyperGroup]] = Default(None), 130 invoke_without_command: bool = Default(False), 131 no_args_is_help: bool = Default(False), 132 subcommand_metavar: Optional[str] = Default(None), 133 chain: bool = Default(False), 134 result_callback: Optional[Callable[..., Any]] = Default(None), 135 # Command 136 context_settings: Optional[Dict[Any, Any]] = Default(None), 137 callback: Optional[Callable[..., Any]] = Default(None), 138 help: Optional[str] = Default(None), 139 epilog: Optional[str] = Default(None), 140 short_help: Optional[str] = Default(None), 141 options_metavar: str = Default("[OPTIONS]"), 142 add_help_option: bool = Default(True), 143 hidden: bool = Default(False), 144 deprecated: bool = Default(False), 145 add_completion: bool = True, 146 # Rich settings 147 rich_markup_mode: MarkupMode = Default(DEFAULT_MARKUP_MODE), 148 rich_help_panel: Union[str, None] = Default(None), 149 pretty_exceptions_enable: bool = True, 150 pretty_exceptions_show_locals: bool = True, 151 pretty_exceptions_short: bool = True, 152 ): 153 self._add_completion = add_completion 154 self.rich_markup_mode: MarkupMode = rich_markup_mode 155 self.rich_help_panel = rich_help_panel 156 self.pretty_exceptions_enable = pretty_exceptions_enable 157 self.pretty_exceptions_show_locals = pretty_exceptions_show_locals 158 self.pretty_exceptions_short = pretty_exceptions_short 159 self.info = TyperInfo( 160 name=name, 161 cls=cls, 162 invoke_without_command=invoke_without_command, 163 no_args_is_help=no_args_is_help, 164 subcommand_metavar=subcommand_metavar, 165 chain=chain, 166 result_callback=result_callback, 167 context_settings=context_settings, 168 callback=callback, 169 help=help, 170 epilog=epilog, 171 short_help=short_help, 172 options_metavar=options_metavar, 173 add_help_option=add_help_option, 174 hidden=hidden, 175 deprecated=deprecated, 176 ) 177 self.registered_groups: List[TyperInfo] = [] 178 self.registered_commands: List[CommandInfo] = [] 179 self.registered_callback: Optional[TyperInfo] = None 180 181 def callback( 182 self, 183 name: Optional[str] = Default(None), 184 *, 185 cls: Optional[Type[TyperGroup]] = Default(None), 186 invoke_without_command: bool = Default(False), 187 no_args_is_help: bool = Default(False), 188 subcommand_metavar: Optional[str] = Default(None), 189 chain: bool = Default(False), 190 result_callback: Optional[Callable[..., Any]] = Default(None), 191 # Command 192 context_settings: Optional[Dict[Any, Any]] = Default(None), 193 help: Optional[str] = Default(None), 194 epilog: Optional[str] = Default(None), 195 short_help: Optional[str] = Default(None), 196 options_metavar: str = Default("[OPTIONS]"), 197 add_help_option: bool = Default(True), 198 hidden: bool = Default(False), 199 deprecated: bool = Default(False), 200 # Rich settings 201 rich_help_panel: Union[str, None] = Default(None), 202 ) -> Callable[[CommandFunctionType], CommandFunctionType]: 203 def decorator(f: CommandFunctionType) -> CommandFunctionType: 204 self.registered_callback = TyperInfo( 205 name=name, 206 cls=cls, 207 invoke_without_command=invoke_without_command, 208 no_args_is_help=no_args_is_help, 209 subcommand_metavar=subcommand_metavar, 210 chain=chain, 211 result_callback=result_callback, 212 context_settings=context_settings, 213 callback=f, 214 help=help, 215 epilog=epilog, 216 short_help=short_help, 217 options_metavar=options_metavar, 218 add_help_option=add_help_option, 219 hidden=hidden, 220 deprecated=deprecated, 221 rich_help_panel=rich_help_panel, 222 ) 223 return f 224 225 return decorator 226 227 def command( 228 self, 229 name: Optional[str] = None, 230 *, 231 cls: Optional[Type[TyperCommand]] = None, 232 context_settings: Optional[Dict[Any, Any]] = None, 233 help: Optional[str] = None, 234 epilog: Optional[str] = None, 235 short_help: Optional[str] = None, 236 options_metavar: str = "[OPTIONS]", 237 add_help_option: bool = True, 238 no_args_is_help: bool = False, 239 hidden: bool = False, 240 deprecated: bool = False, 241 # Rich settings 242 rich_help_panel: Union[str, None] = Default(None), 243 ) -> Callable[[CommandFunctionType], CommandFunctionType]: 244 if cls is None: 245 cls = TyperCommand 246 247 def decorator(f: CommandFunctionType) -> CommandFunctionType: 248 self.registered_commands.append( 249 CommandInfo( 250 name=name, 251 cls=cls, 252 context_settings=context_settings, 253 callback=f, 254 help=help, 255 epilog=epilog, 256 short_help=short_help, 257 options_metavar=options_metavar, 258 add_help_option=add_help_option, 259 no_args_is_help=no_args_is_help, 260 hidden=hidden, 261 deprecated=deprecated, 262 # Rich settings 263 rich_help_panel=rich_help_panel, 264 ) 265 ) 266 return f 267 268 return decorator 269 270 def add_typer( 271 self, 272 typer_instance: "Typer", 273 *, 274 name: Optional[str] = Default(None), 275 cls: Optional[Type[TyperGroup]] = Default(None), 276 invoke_without_command: bool = Default(False), 277 no_args_is_help: bool = Default(False), 278 subcommand_metavar: Optional[str] = Default(None), 279 chain: bool = Default(False), 280 result_callback: Optional[Callable[..., Any]] = Default(None), 281 # Command 282 context_settings: Optional[Dict[Any, Any]] = Default(None), 283 callback: Optional[Callable[..., Any]] = Default(None), 284 help: Optional[str] = Default(None), 285 epilog: Optional[str] = Default(None), 286 short_help: Optional[str] = Default(None), 287 options_metavar: str = Default("[OPTIONS]"), 288 add_help_option: bool = Default(True), 289 hidden: bool = Default(False), 290 deprecated: bool = Default(False), 291 # Rich settings 292 rich_help_panel: Union[str, None] = Default(None), 293 ) -> None: 294 self.registered_groups.append( 295 TyperInfo( 296 typer_instance, 297 name=name, 298 cls=cls, 299 invoke_without_command=invoke_without_command, 300 no_args_is_help=no_args_is_help, 301 subcommand_metavar=subcommand_metavar, 302 chain=chain, 303 result_callback=result_callback, 304 context_settings=context_settings, 305 callback=callback, 306 help=help, 307 epilog=epilog, 308 short_help=short_help, 309 options_metavar=options_metavar, 310 add_help_option=add_help_option, 311 hidden=hidden, 312 deprecated=deprecated, 313 rich_help_panel=rich_help_panel, 314 ) 315 ) 316 317 def __call__(self, *args: Any, **kwargs: Any) -> Any: 318 if sys.excepthook != except_hook: 319 sys.excepthook = except_hook 320 try: 321 return get_command(self)(*args, **kwargs) 322 except Exception as e: 323 # Set a custom attribute to tell the hook to show nice exceptions for user 324 # code. An alternative/first implementation was a custom exception with 325 # raise custom_exc from e 326 # but that means the last error shown is the custom exception, not the 327 # actual error. This trick improves developer experience by showing the 328 # actual error last. 329 setattr( 330 e, 331 _typer_developer_exception_attr_name, 332 DeveloperExceptionConfig( 333 pretty_exceptions_enable=self.pretty_exceptions_enable, 334 pretty_exceptions_show_locals=self.pretty_exceptions_show_locals, 335 pretty_exceptions_short=self.pretty_exceptions_short, 336 ), 337 ) 338 raise e 339 340 341def get_group(typer_instance: Typer) -> TyperGroup: 342 group = get_group_from_info( 343 TyperInfo(typer_instance), 344 pretty_exceptions_short=typer_instance.pretty_exceptions_short, 345 rich_markup_mode=typer_instance.rich_markup_mode, 346 ) 347 return group 348 349 350def get_command(typer_instance: Typer) -> click.Command: 351 if typer_instance._add_completion: 352 click_install_param, click_show_param = get_install_completion_arguments() 353 if ( 354 typer_instance.registered_callback 355 or typer_instance.info.callback 356 or typer_instance.registered_groups 357 or len(typer_instance.registered_commands) > 1 358 ): 359 # Create a Group 360 click_command: click.Command = get_group(typer_instance) 361 if typer_instance._add_completion: 362 click_command.params.append(click_install_param) 363 click_command.params.append(click_show_param) 364 return click_command 365 elif len(typer_instance.registered_commands) == 1: 366 # Create a single Command 367 single_command = typer_instance.registered_commands[0] 368 369 if not single_command.context_settings and not isinstance( 370 typer_instance.info.context_settings, DefaultPlaceholder 371 ): 372 single_command.context_settings = typer_instance.info.context_settings 373 374 click_command = get_command_from_info( 375 single_command, 376 pretty_exceptions_short=typer_instance.pretty_exceptions_short, 377 rich_markup_mode=typer_instance.rich_markup_mode, 378 ) 379 if typer_instance._add_completion: 380 click_command.params.append(click_install_param) 381 click_command.params.append(click_show_param) 382 return click_command 383 raise RuntimeError( 384 "Could not get a command for this Typer instance" 385 ) # pragma: no cover 386 387 388def get_group_name(typer_info: TyperInfo) -> Optional[str]: 389 if typer_info.callback: 390 # Priority 1: Callback passed in app.add_typer() 391 return get_command_name(typer_info.callback.__name__) 392 if typer_info.typer_instance: 393 registered_callback = typer_info.typer_instance.registered_callback 394 if registered_callback: 395 if registered_callback.callback: 396 # Priority 2: Callback passed in @subapp.callback() 397 return get_command_name(registered_callback.callback.__name__) 398 if typer_info.typer_instance.info.callback: 399 return get_command_name(typer_info.typer_instance.info.callback.__name__) 400 return None 401 402 403def solve_typer_info_help(typer_info: TyperInfo) -> str: 404 # Priority 1: Explicit value was set in app.add_typer() 405 if not isinstance(typer_info.help, DefaultPlaceholder): 406 return inspect.cleandoc(typer_info.help or "") 407 # Priority 2: Explicit value was set in sub_app.callback() 408 try: 409 callback_help = typer_info.typer_instance.registered_callback.help 410 if not isinstance(callback_help, DefaultPlaceholder): 411 return inspect.cleandoc(callback_help or "") 412 except AttributeError: 413 pass 414 # Priority 3: Explicit value was set in sub_app = typer.Typer() 415 try: 416 instance_help = typer_info.typer_instance.info.help 417 if not isinstance(instance_help, DefaultPlaceholder): 418 return inspect.cleandoc(instance_help or "") 419 except AttributeError: 420 pass 421 # Priority 4: Implicit inference from callback docstring in app.add_typer() 422 if typer_info.callback: 423 doc = inspect.getdoc(typer_info.callback) 424 if doc: 425 return doc 426 # Priority 5: Implicit inference from callback docstring in @app.callback() 427 try: 428 callback = typer_info.typer_instance.registered_callback.callback 429 if not isinstance(callback, DefaultPlaceholder): 430 doc = inspect.getdoc(callback or "") 431 if doc: 432 return doc 433 except AttributeError: 434 pass 435 # Priority 6: Implicit inference from callback docstring in typer.Typer() 436 try: 437 instance_callback = typer_info.typer_instance.info.callback 438 if not isinstance(instance_callback, DefaultPlaceholder): 439 doc = inspect.getdoc(instance_callback) 440 if doc: 441 return doc 442 except AttributeError: 443 pass 444 # Value not set, use the default 445 return typer_info.help.value 446 447 448def solve_typer_info_defaults(typer_info: TyperInfo) -> TyperInfo: 449 values: Dict[str, Any] = {} 450 for name, value in typer_info.__dict__.items(): 451 # Priority 1: Value was set in app.add_typer() 452 if not isinstance(value, DefaultPlaceholder): 453 values[name] = value 454 continue 455 # Priority 2: Value was set in @subapp.callback() 456 try: 457 callback_value = getattr( 458 typer_info.typer_instance.registered_callback, # type: ignore 459 name, 460 ) 461 if not isinstance(callback_value, DefaultPlaceholder): 462 values[name] = callback_value 463 continue 464 except AttributeError: 465 pass 466 # Priority 3: Value set in subapp = typer.Typer() 467 try: 468 instance_value = getattr( 469 typer_info.typer_instance.info, # type: ignore 470 name, 471 ) 472 if not isinstance(instance_value, DefaultPlaceholder): 473 values[name] = instance_value 474 continue 475 except AttributeError: 476 pass 477 # Value not set, use the default 478 values[name] = value.value 479 if values["name"] is None: 480 values["name"] = get_group_name(typer_info) 481 values["help"] = solve_typer_info_help(typer_info) 482 return TyperInfo(**values) 483 484 485def get_group_from_info( 486 group_info: TyperInfo, 487 *, 488 pretty_exceptions_short: bool, 489 rich_markup_mode: MarkupMode, 490) -> TyperGroup: 491 assert ( 492 group_info.typer_instance 493 ), "A Typer instance is needed to generate a Click Group" 494 commands: Dict[str, click.Command] = {} 495 for command_info in group_info.typer_instance.registered_commands: 496 command = get_command_from_info( 497 command_info=command_info, 498 pretty_exceptions_short=pretty_exceptions_short, 499 rich_markup_mode=rich_markup_mode, 500 ) 501 if command.name: 502 commands[command.name] = command 503 for sub_group_info in group_info.typer_instance.registered_groups: 504 sub_group = get_group_from_info( 505 sub_group_info, 506 pretty_exceptions_short=pretty_exceptions_short, 507 rich_markup_mode=rich_markup_mode, 508 ) 509 if sub_group.name: 510 commands[sub_group.name] = sub_group 511 solved_info = solve_typer_info_defaults(group_info) 512 ( 513 params, 514 convertors, 515 context_param_name, 516 ) = get_params_convertors_ctx_param_name_from_function(solved_info.callback) 517 cls = solved_info.cls or TyperGroup 518 assert issubclass(cls, TyperGroup), f"{cls} should be a subclass of {TyperGroup}" 519 group = cls( 520 name=solved_info.name or "", 521 commands=commands, 522 invoke_without_command=solved_info.invoke_without_command, 523 no_args_is_help=solved_info.no_args_is_help, 524 subcommand_metavar=solved_info.subcommand_metavar, 525 chain=solved_info.chain, 526 result_callback=solved_info.result_callback, 527 context_settings=solved_info.context_settings, 528 callback=get_callback( 529 callback=solved_info.callback, 530 params=params, 531 convertors=convertors, 532 context_param_name=context_param_name, 533 pretty_exceptions_short=pretty_exceptions_short, 534 ), 535 params=params, 536 help=solved_info.help, 537 epilog=solved_info.epilog, 538 short_help=solved_info.short_help, 539 options_metavar=solved_info.options_metavar, 540 add_help_option=solved_info.add_help_option, 541 hidden=solved_info.hidden, 542 deprecated=solved_info.deprecated, 543 rich_markup_mode=rich_markup_mode, 544 # Rich settings 545 rich_help_panel=solved_info.rich_help_panel, 546 ) 547 return group 548 549 550def get_command_name(name: str) -> str: 551 return name.lower().replace("_", "-") 552 553 554def get_params_convertors_ctx_param_name_from_function( 555 callback: Optional[Callable[..., Any]], 556) -> Tuple[List[Union[click.Argument, click.Option]], Dict[str, Any], Optional[str]]: 557 params = [] 558 convertors = {} 559 context_param_name = None 560 if callback: 561 parameters = get_params_from_function(callback) 562 for param_name, param in parameters.items(): 563 if lenient_issubclass(param.annotation, click.Context): 564 context_param_name = param_name 565 continue 566 click_param, convertor = get_click_param(param) 567 if convertor: 568 convertors[param_name] = convertor 569 params.append(click_param) 570 return params, convertors, context_param_name 571 572 573def get_command_from_info( 574 command_info: CommandInfo, 575 *, 576 pretty_exceptions_short: bool, 577 rich_markup_mode: MarkupMode, 578) -> click.Command: 579 assert command_info.callback, "A command must have a callback function" 580 name = command_info.name or get_command_name(command_info.callback.__name__) 581 use_help = command_info.help 582 if use_help is None: 583 use_help = inspect.getdoc(command_info.callback) 584 else: 585 use_help = inspect.cleandoc(use_help) 586 ( 587 params, 588 convertors, 589 context_param_name, 590 ) = get_params_convertors_ctx_param_name_from_function(command_info.callback) 591 cls = command_info.cls or TyperCommand 592 command = cls( 593 name=name, 594 context_settings=command_info.context_settings, 595 callback=get_callback( 596 callback=command_info.callback, 597 params=params, 598 convertors=convertors, 599 context_param_name=context_param_name, 600 pretty_exceptions_short=pretty_exceptions_short, 601 ), 602 params=params, # type: ignore 603 help=use_help, 604 epilog=command_info.epilog, 605 short_help=command_info.short_help, 606 options_metavar=command_info.options_metavar, 607 add_help_option=command_info.add_help_option, 608 no_args_is_help=command_info.no_args_is_help, 609 hidden=command_info.hidden, 610 deprecated=command_info.deprecated, 611 rich_markup_mode=rich_markup_mode, 612 # Rich settings 613 rich_help_panel=command_info.rich_help_panel, 614 ) 615 return command 616 617 618def determine_type_convertor(type_: Any) -> Optional[Callable[[Any], Any]]: 619 convertor: Optional[Callable[[Any], Any]] = None 620 if lenient_issubclass(type_, Path): 621 convertor = param_path_convertor 622 if lenient_issubclass(type_, Enum): 623 convertor = generate_enum_convertor(type_) 624 return convertor 625 626 627def param_path_convertor(value: Optional[str] = None) -> Optional[Path]: 628 if value is not None: 629 return Path(value) 630 return None 631 632 633def generate_enum_convertor(enum: Type[Enum]) -> Callable[[Any], Any]: 634 val_map = {str(val.value): val for val in enum} 635 636 def convertor(value: Any) -> Any: 637 if value is not None: 638 val = str(value) 639 if val in val_map: 640 key = val_map[val] 641 return enum(key) 642 643 return convertor 644 645 646def generate_list_convertor( 647 convertor: Optional[Callable[[Any], Any]], default_value: Optional[Any] 648) -> Callable[[Sequence[Any]], Optional[List[Any]]]: 649 def internal_convertor(value: Sequence[Any]) -> Optional[List[Any]]: 650 if default_value is None and len(value) == 0: 651 return None 652 return [convertor(v) if convertor else v for v in value] 653 654 return internal_convertor 655 656 657def generate_tuple_convertor( 658 types: Sequence[Any], 659) -> Callable[[Optional[Tuple[Any, ...]]], Optional[Tuple[Any, ...]]]: 660 convertors = [determine_type_convertor(type_) for type_ in types] 661 662 def internal_convertor( 663 param_args: Optional[Tuple[Any, ...]], 664 ) -> Optional[Tuple[Any, ...]]: 665 if param_args is None: 666 return None 667 return tuple( 668 convertor(arg) if convertor else arg 669 for (convertor, arg) in zip(convertors, param_args) 670 ) 671 672 return internal_convertor 673 674 675def get_callback( 676 *, 677 callback: Optional[Callable[..., Any]] = None, 678 params: Sequence[click.Parameter] = [], 679 convertors: Optional[Dict[str, Callable[[str], Any]]] = None, 680 context_param_name: Optional[str] = None, 681 pretty_exceptions_short: bool, 682) -> Optional[Callable[..., Any]]: 683 use_convertors = convertors or {} 684 if not callback: 685 return None 686 parameters = get_params_from_function(callback) 687 use_params: Dict[str, Any] = {} 688 for param_name in parameters: 689 use_params[param_name] = None 690 for param in params: 691 if param.name: 692 use_params[param.name] = param.default 693 694 def wrapper(**kwargs: Any) -> Any: 695 _rich_traceback_guard = pretty_exceptions_short # noqa: F841 696 for k, v in kwargs.items(): 697 if k in use_convertors: 698 use_params[k] = use_convertors[k](v) 699 else: 700 use_params[k] = v 701 if context_param_name: 702 use_params[context_param_name] = click.get_current_context() 703 return callback(**use_params) 704 705 update_wrapper(wrapper, callback) 706 return wrapper 707 708 709def get_click_type( 710 *, annotation: Any, parameter_info: ParameterInfo 711) -> click.ParamType: 712 if parameter_info.click_type is not None: 713 return parameter_info.click_type 714 715 elif parameter_info.parser is not None: 716 return click.types.FuncParamType(parameter_info.parser) 717 718 elif annotation == str: 719 return click.STRING 720 elif annotation == int: 721 if parameter_info.min is not None or parameter_info.max is not None: 722 min_ = None 723 max_ = None 724 if parameter_info.min is not None: 725 min_ = int(parameter_info.min) 726 if parameter_info.max is not None: 727 max_ = int(parameter_info.max) 728 return click.IntRange(min=min_, max=max_, clamp=parameter_info.clamp) 729 else: 730 return click.INT 731 elif annotation == float: 732 if parameter_info.min is not None or parameter_info.max is not None: 733 return click.FloatRange( 734 min=parameter_info.min, 735 max=parameter_info.max, 736 clamp=parameter_info.clamp, 737 ) 738 else: 739 return click.FLOAT 740 elif annotation == bool: 741 return click.BOOL 742 elif annotation == UUID: 743 return click.UUID 744 elif annotation == datetime: 745 return click.DateTime(formats=parameter_info.formats) 746 elif ( 747 annotation == Path 748 or parameter_info.allow_dash 749 or parameter_info.path_type 750 or parameter_info.resolve_path 751 ): 752 return click.Path( 753 exists=parameter_info.exists, 754 file_okay=parameter_info.file_okay, 755 dir_okay=parameter_info.dir_okay, 756 writable=parameter_info.writable, 757 readable=parameter_info.readable, 758 resolve_path=parameter_info.resolve_path, 759 allow_dash=parameter_info.allow_dash, 760 path_type=parameter_info.path_type, 761 ) 762 elif lenient_issubclass(annotation, FileTextWrite): 763 return click.File( 764 mode=parameter_info.mode or "w", 765 encoding=parameter_info.encoding, 766 errors=parameter_info.errors, 767 lazy=parameter_info.lazy, 768 atomic=parameter_info.atomic, 769 ) 770 elif lenient_issubclass(annotation, FileText): 771 return click.File( 772 mode=parameter_info.mode or "r", 773 encoding=parameter_info.encoding, 774 errors=parameter_info.errors, 775 lazy=parameter_info.lazy, 776 atomic=parameter_info.atomic, 777 ) 778 elif lenient_issubclass(annotation, FileBinaryRead): 779 return click.File( 780 mode=parameter_info.mode or "rb", 781 encoding=parameter_info.encoding, 782 errors=parameter_info.errors, 783 lazy=parameter_info.lazy, 784 atomic=parameter_info.atomic, 785 ) 786 elif lenient_issubclass(annotation, FileBinaryWrite): 787 return click.File( 788 mode=parameter_info.mode or "wb", 789 encoding=parameter_info.encoding, 790 errors=parameter_info.errors, 791 lazy=parameter_info.lazy, 792 atomic=parameter_info.atomic, 793 ) 794 elif lenient_issubclass(annotation, Enum): 795 return click.Choice( 796 [item.value for item in annotation], 797 case_sensitive=parameter_info.case_sensitive, 798 ) 799 raise RuntimeError(f"Type not yet supported: {annotation}") # pragma: no cover 800 801 802def lenient_issubclass( 803 cls: Any, class_or_tuple: Union[AnyType, Tuple[AnyType, ...]] 804) -> bool: 805 return isinstance(cls, type) and issubclass(cls, class_or_tuple) 806 807 808def get_click_param( 809 param: ParamMeta, 810) -> Tuple[Union[click.Argument, click.Option], Any]: 811 # First, find out what will be: 812 # * ParamInfo (ArgumentInfo or OptionInfo) 813 # * default_value 814 # * required 815 default_value = None 816 required = False 817 if isinstance(param.default, ParameterInfo): 818 parameter_info = param.default 819 if parameter_info.default == Required: 820 required = True 821 else: 822 default_value = parameter_info.default 823 elif param.default == Required or param.default == param.empty: 824 required = True 825 parameter_info = ArgumentInfo() 826 else: 827 default_value = param.default 828 parameter_info = OptionInfo() 829 annotation: Any 830 if not param.annotation == param.empty: 831 annotation = param.annotation 832 else: 833 annotation = str 834 main_type = annotation 835 is_list = False 836 is_tuple = False 837 parameter_type: Any = None 838 is_flag = None 839 origin = get_origin(main_type) 840 841 if origin is not None: 842 # Handle SomeType | None and Optional[SomeType] 843 if is_union(origin): 844 types = [] 845 for type_ in get_args(main_type): 846 if type_ is NoneType: 847 continue 848 types.append(type_) 849 assert len(types) == 1, "Typer Currently doesn't support Union types" 850 main_type = types[0] 851 origin = get_origin(main_type) 852 # Handle Tuples and Lists 853 if lenient_issubclass(origin, List): 854 main_type = get_args(main_type)[0] 855 assert not get_origin( 856 main_type 857 ), "List types with complex sub-types are not currently supported" 858 is_list = True 859 elif lenient_issubclass(origin, Tuple): # type: ignore 860 types = [] 861 for type_ in get_args(main_type): 862 assert not get_origin( 863 type_ 864 ), "Tuple types with complex sub-types are not currently supported" 865 types.append( 866 get_click_type(annotation=type_, parameter_info=parameter_info) 867 ) 868 parameter_type = tuple(types) 869 is_tuple = True 870 if parameter_type is None: 871 parameter_type = get_click_type( 872 annotation=main_type, parameter_info=parameter_info 873 ) 874 convertor = determine_type_convertor(main_type) 875 if is_list: 876 convertor = generate_list_convertor( 877 convertor=convertor, default_value=default_value 878 ) 879 if is_tuple: 880 convertor = generate_tuple_convertor(get_args(main_type)) 881 if isinstance(parameter_info, OptionInfo): 882 if main_type is bool and parameter_info.is_flag is not False: 883 is_flag = True 884 # Click doesn't accept a flag of type bool, only None, and then it sets it 885 # to bool internally 886 parameter_type = None 887 default_option_name = get_command_name(param.name) 888 if is_flag: 889 default_option_declaration = ( 890 f"--{default_option_name}/--no-{default_option_name}" 891 ) 892 else: 893 default_option_declaration = f"--{default_option_name}" 894 param_decls = [param.name] 895 if parameter_info.param_decls: 896 param_decls.extend(parameter_info.param_decls) 897 else: 898 param_decls.append(default_option_declaration) 899 return ( 900 TyperOption( 901 # Option 902 param_decls=param_decls, 903 show_default=parameter_info.show_default, 904 prompt=parameter_info.prompt, 905 confirmation_prompt=parameter_info.confirmation_prompt, 906 prompt_required=parameter_info.prompt_required, 907 hide_input=parameter_info.hide_input, 908 is_flag=is_flag, 909 flag_value=parameter_info.flag_value, 910 multiple=is_list, 911 count=parameter_info.count, 912 allow_from_autoenv=parameter_info.allow_from_autoenv, 913 type=parameter_type, 914 help=parameter_info.help, 915 hidden=parameter_info.hidden, 916 show_choices=parameter_info.show_choices, 917 show_envvar=parameter_info.show_envvar, 918 # Parameter 919 required=required, 920 default=default_value, 921 callback=get_param_callback( 922 callback=parameter_info.callback, convertor=convertor 923 ), 924 metavar=parameter_info.metavar, 925 expose_value=parameter_info.expose_value, 926 is_eager=parameter_info.is_eager, 927 envvar=parameter_info.envvar, 928 shell_complete=parameter_info.shell_complete, 929 autocompletion=get_param_completion(parameter_info.autocompletion), 930 # Rich settings 931 rich_help_panel=parameter_info.rich_help_panel, 932 ), 933 convertor, 934 ) 935 elif isinstance(parameter_info, ArgumentInfo): 936 param_decls = [param.name] 937 nargs = None 938 if is_list: 939 nargs = -1 940 return ( 941 TyperArgument( 942 # Argument 943 param_decls=param_decls, 944 type=parameter_type, 945 required=required, 946 nargs=nargs, 947 # TyperArgument 948 show_default=parameter_info.show_default, 949 show_choices=parameter_info.show_choices, 950 show_envvar=parameter_info.show_envvar, 951 help=parameter_info.help, 952 hidden=parameter_info.hidden, 953 # Parameter 954 default=default_value, 955 callback=get_param_callback( 956 callback=parameter_info.callback, convertor=convertor 957 ), 958 metavar=parameter_info.metavar, 959 expose_value=parameter_info.expose_value, 960 is_eager=parameter_info.is_eager, 961 envvar=parameter_info.envvar, 962 shell_complete=parameter_info.shell_complete, 963 autocompletion=get_param_completion(parameter_info.autocompletion), 964 # Rich settings 965 rich_help_panel=parameter_info.rich_help_panel, 966 ), 967 convertor, 968 ) 969 raise AssertionError("A click.Parameter should be returned") # pragma: no cover 970 971 972def get_param_callback( 973 *, 974 callback: Optional[Callable[..., Any]] = None, 975 convertor: Optional[Callable[..., Any]] = None, 976) -> Optional[Callable[..., Any]]: 977 if not callback: 978 return None 979 parameters = get_params_from_function(callback) 980 ctx_name = None 981 click_param_name = None 982 value_name = None 983 untyped_names: List[str] = [] 984 for param_name, param_sig in parameters.items(): 985 if lenient_issubclass(param_sig.annotation, click.Context): 986 ctx_name = param_name 987 elif lenient_issubclass(param_sig.annotation, click.Parameter): 988 click_param_name = param_name 989 else: 990 untyped_names.append(param_name) 991 # Extract value param name first 992 if untyped_names: 993 value_name = untyped_names.pop() 994 # If context and Click param were not typed (old/Click callback style) extract them 995 if untyped_names: 996 if ctx_name is None: 997 ctx_name = untyped_names.pop(0) 998 if click_param_name is None: 999 if untyped_names: 1000 click_param_name = untyped_names.pop(0) 1001 if untyped_names: 1002 raise click.ClickException( 1003 "Too many CLI parameter callback function parameters" 1004 ) 1005 1006 def wrapper(ctx: click.Context, param: click.Parameter, value: Any) -> Any: 1007 use_params: Dict[str, Any] = {} 1008 if ctx_name: 1009 use_params[ctx_name] = ctx 1010 if click_param_name: 1011 use_params[click_param_name] = param 1012 if value_name: 1013 if convertor: 1014 use_value = convertor(value) 1015 else: 1016 use_value = value 1017 use_params[value_name] = use_value 1018 return callback(**use_params) 1019 1020 update_wrapper(wrapper, callback) 1021 return wrapper 1022 1023 1024def get_param_completion( 1025 callback: Optional[Callable[..., Any]] = None, 1026) -> Optional[Callable[..., Any]]: 1027 if not callback: 1028 return None 1029 parameters = get_params_from_function(callback) 1030 ctx_name = None 1031 args_name = None 1032 incomplete_name = None 1033 unassigned_params = list(parameters.values()) 1034 for param_sig in unassigned_params[:]: 1035 origin = get_origin(param_sig.annotation) 1036 if lenient_issubclass(param_sig.annotation, click.Context): 1037 ctx_name = param_sig.name 1038 unassigned_params.remove(param_sig) 1039 elif lenient_issubclass(origin, List): 1040 args_name = param_sig.name 1041 unassigned_params.remove(param_sig) 1042 elif lenient_issubclass(param_sig.annotation, str): 1043 incomplete_name = param_sig.name 1044 unassigned_params.remove(param_sig) 1045 # If there are still unassigned parameters (not typed), extract by name 1046 for param_sig in unassigned_params[:]: 1047 if ctx_name is None and param_sig.name == "ctx": 1048 ctx_name = param_sig.name 1049 unassigned_params.remove(param_sig) 1050 elif args_name is None and param_sig.name == "args": 1051 args_name = param_sig.name 1052 unassigned_params.remove(param_sig) 1053 elif incomplete_name is None and param_sig.name == "incomplete": 1054 incomplete_name = param_sig.name 1055 unassigned_params.remove(param_sig) 1056 # Extract value param name first 1057 if unassigned_params: 1058 show_params = " ".join([param.name for param in unassigned_params]) 1059 raise click.ClickException( 1060 f"Invalid autocompletion callback parameters: {show_params}" 1061 ) 1062 1063 def wrapper(ctx: click.Context, args: List[str], incomplete: Optional[str]) -> Any: 1064 use_params: Dict[str, Any] = {} 1065 if ctx_name: 1066 use_params[ctx_name] = ctx 1067 if args_name: 1068 use_params[args_name] = args 1069 if incomplete_name: 1070 use_params[incomplete_name] = incomplete 1071 return callback(**use_params) 1072 1073 update_wrapper(wrapper, callback) 1074 return wrapper 1075 1076 1077def run(function: Callable[..., Any]) -> None: 1078 app = Typer(add_completion=False) 1079 app.command()(function) 1080 app()
def
except_hook( exc_type: Type[BaseException], exc_value: BaseException, tb: Optional[traceback]) -> None:
63def except_hook( 64 exc_type: Type[BaseException], exc_value: BaseException, tb: Optional[TracebackType] 65) -> None: 66 exception_config: Union[DeveloperExceptionConfig, None] = getattr( 67 exc_value, _typer_developer_exception_attr_name, None 68 ) 69 standard_traceback = os.getenv("_TYPER_STANDARD_TRACEBACK") 70 if ( 71 standard_traceback 72 or not exception_config 73 or not exception_config.pretty_exceptions_enable 74 ): 75 _original_except_hook(exc_type, exc_value, tb) 76 return 77 typer_path = os.path.dirname(__file__) 78 click_path = os.path.dirname(click.__file__) 79 supress_internal_dir_names = [typer_path, click_path] 80 exc = exc_value 81 if rich: 82 from .rich_utils import MAX_WIDTH 83 84 rich_tb = Traceback.from_exception( 85 type(exc), 86 exc, 87 exc.__traceback__, 88 show_locals=exception_config.pretty_exceptions_show_locals, 89 suppress=supress_internal_dir_names, 90 width=MAX_WIDTH, 91 ) 92 console_stderr.print(rich_tb) 93 return 94 tb_exc = traceback.TracebackException.from_exception(exc) 95 stack: List[FrameSummary] = [] 96 for frame in tb_exc.stack: 97 if any(frame.filename.startswith(path) for path in supress_internal_dir_names): 98 if not exception_config.pretty_exceptions_short: 99 # Hide the line for internal libraries, Typer and Click 100 stack.append( 101 traceback.FrameSummary( 102 filename=frame.filename, 103 lineno=frame.lineno, 104 name=frame.name, 105 line="", 106 ) 107 ) 108 else: 109 stack.append(frame) 110 # Type ignore ref: https://github.com/python/typeshed/pull/8244 111 final_stack_summary = StackSummary.from_list(stack) 112 tb_exc.stack = final_stack_summary 113 for line in tb_exc.format(): 114 print(line, file=sys.stderr) 115 return
def
get_install_completion_arguments() -> Tuple[click.core.Parameter, click.core.Parameter]:
118def get_install_completion_arguments() -> Tuple[click.Parameter, click.Parameter]: 119 install_param, show_param = get_completion_inspect_parameters() 120 click_install_param, _ = get_click_param(install_param) 121 click_show_param, _ = get_click_param(show_param) 122 return click_install_param, click_show_param
class
Typer:
125class Typer: 126 def __init__( 127 self, 128 *, 129 name: Optional[str] = Default(None), 130 cls: Optional[Type[TyperGroup]] = Default(None), 131 invoke_without_command: bool = Default(False), 132 no_args_is_help: bool = Default(False), 133 subcommand_metavar: Optional[str] = Default(None), 134 chain: bool = Default(False), 135 result_callback: Optional[Callable[..., Any]] = Default(None), 136 # Command 137 context_settings: Optional[Dict[Any, Any]] = Default(None), 138 callback: Optional[Callable[..., Any]] = Default(None), 139 help: Optional[str] = Default(None), 140 epilog: Optional[str] = Default(None), 141 short_help: Optional[str] = Default(None), 142 options_metavar: str = Default("[OPTIONS]"), 143 add_help_option: bool = Default(True), 144 hidden: bool = Default(False), 145 deprecated: bool = Default(False), 146 add_completion: bool = True, 147 # Rich settings 148 rich_markup_mode: MarkupMode = Default(DEFAULT_MARKUP_MODE), 149 rich_help_panel: Union[str, None] = Default(None), 150 pretty_exceptions_enable: bool = True, 151 pretty_exceptions_show_locals: bool = True, 152 pretty_exceptions_short: bool = True, 153 ): 154 self._add_completion = add_completion 155 self.rich_markup_mode: MarkupMode = rich_markup_mode 156 self.rich_help_panel = rich_help_panel 157 self.pretty_exceptions_enable = pretty_exceptions_enable 158 self.pretty_exceptions_show_locals = pretty_exceptions_show_locals 159 self.pretty_exceptions_short = pretty_exceptions_short 160 self.info = TyperInfo( 161 name=name, 162 cls=cls, 163 invoke_without_command=invoke_without_command, 164 no_args_is_help=no_args_is_help, 165 subcommand_metavar=subcommand_metavar, 166 chain=chain, 167 result_callback=result_callback, 168 context_settings=context_settings, 169 callback=callback, 170 help=help, 171 epilog=epilog, 172 short_help=short_help, 173 options_metavar=options_metavar, 174 add_help_option=add_help_option, 175 hidden=hidden, 176 deprecated=deprecated, 177 ) 178 self.registered_groups: List[TyperInfo] = [] 179 self.registered_commands: List[CommandInfo] = [] 180 self.registered_callback: Optional[TyperInfo] = None 181 182 def callback( 183 self, 184 name: Optional[str] = Default(None), 185 *, 186 cls: Optional[Type[TyperGroup]] = Default(None), 187 invoke_without_command: bool = Default(False), 188 no_args_is_help: bool = Default(False), 189 subcommand_metavar: Optional[str] = Default(None), 190 chain: bool = Default(False), 191 result_callback: Optional[Callable[..., Any]] = Default(None), 192 # Command 193 context_settings: Optional[Dict[Any, Any]] = Default(None), 194 help: Optional[str] = Default(None), 195 epilog: Optional[str] = Default(None), 196 short_help: Optional[str] = Default(None), 197 options_metavar: str = Default("[OPTIONS]"), 198 add_help_option: bool = Default(True), 199 hidden: bool = Default(False), 200 deprecated: bool = Default(False), 201 # Rich settings 202 rich_help_panel: Union[str, None] = Default(None), 203 ) -> Callable[[CommandFunctionType], CommandFunctionType]: 204 def decorator(f: CommandFunctionType) -> CommandFunctionType: 205 self.registered_callback = TyperInfo( 206 name=name, 207 cls=cls, 208 invoke_without_command=invoke_without_command, 209 no_args_is_help=no_args_is_help, 210 subcommand_metavar=subcommand_metavar, 211 chain=chain, 212 result_callback=result_callback, 213 context_settings=context_settings, 214 callback=f, 215 help=help, 216 epilog=epilog, 217 short_help=short_help, 218 options_metavar=options_metavar, 219 add_help_option=add_help_option, 220 hidden=hidden, 221 deprecated=deprecated, 222 rich_help_panel=rich_help_panel, 223 ) 224 return f 225 226 return decorator 227 228 def command( 229 self, 230 name: Optional[str] = None, 231 *, 232 cls: Optional[Type[TyperCommand]] = None, 233 context_settings: Optional[Dict[Any, Any]] = None, 234 help: Optional[str] = None, 235 epilog: Optional[str] = None, 236 short_help: Optional[str] = None, 237 options_metavar: str = "[OPTIONS]", 238 add_help_option: bool = True, 239 no_args_is_help: bool = False, 240 hidden: bool = False, 241 deprecated: bool = False, 242 # Rich settings 243 rich_help_panel: Union[str, None] = Default(None), 244 ) -> Callable[[CommandFunctionType], CommandFunctionType]: 245 if cls is None: 246 cls = TyperCommand 247 248 def decorator(f: CommandFunctionType) -> CommandFunctionType: 249 self.registered_commands.append( 250 CommandInfo( 251 name=name, 252 cls=cls, 253 context_settings=context_settings, 254 callback=f, 255 help=help, 256 epilog=epilog, 257 short_help=short_help, 258 options_metavar=options_metavar, 259 add_help_option=add_help_option, 260 no_args_is_help=no_args_is_help, 261 hidden=hidden, 262 deprecated=deprecated, 263 # Rich settings 264 rich_help_panel=rich_help_panel, 265 ) 266 ) 267 return f 268 269 return decorator 270 271 def add_typer( 272 self, 273 typer_instance: "Typer", 274 *, 275 name: Optional[str] = Default(None), 276 cls: Optional[Type[TyperGroup]] = Default(None), 277 invoke_without_command: bool = Default(False), 278 no_args_is_help: bool = Default(False), 279 subcommand_metavar: Optional[str] = Default(None), 280 chain: bool = Default(False), 281 result_callback: Optional[Callable[..., Any]] = Default(None), 282 # Command 283 context_settings: Optional[Dict[Any, Any]] = Default(None), 284 callback: Optional[Callable[..., Any]] = Default(None), 285 help: Optional[str] = Default(None), 286 epilog: Optional[str] = Default(None), 287 short_help: Optional[str] = Default(None), 288 options_metavar: str = Default("[OPTIONS]"), 289 add_help_option: bool = Default(True), 290 hidden: bool = Default(False), 291 deprecated: bool = Default(False), 292 # Rich settings 293 rich_help_panel: Union[str, None] = Default(None), 294 ) -> None: 295 self.registered_groups.append( 296 TyperInfo( 297 typer_instance, 298 name=name, 299 cls=cls, 300 invoke_without_command=invoke_without_command, 301 no_args_is_help=no_args_is_help, 302 subcommand_metavar=subcommand_metavar, 303 chain=chain, 304 result_callback=result_callback, 305 context_settings=context_settings, 306 callback=callback, 307 help=help, 308 epilog=epilog, 309 short_help=short_help, 310 options_metavar=options_metavar, 311 add_help_option=add_help_option, 312 hidden=hidden, 313 deprecated=deprecated, 314 rich_help_panel=rich_help_panel, 315 ) 316 ) 317 318 def __call__(self, *args: Any, **kwargs: Any) -> Any: 319 if sys.excepthook != except_hook: 320 sys.excepthook = except_hook 321 try: 322 return get_command(self)(*args, **kwargs) 323 except Exception as e: 324 # Set a custom attribute to tell the hook to show nice exceptions for user 325 # code. An alternative/first implementation was a custom exception with 326 # raise custom_exc from e 327 # but that means the last error shown is the custom exception, not the 328 # actual error. This trick improves developer experience by showing the 329 # actual error last. 330 setattr( 331 e, 332 _typer_developer_exception_attr_name, 333 DeveloperExceptionConfig( 334 pretty_exceptions_enable=self.pretty_exceptions_enable, 335 pretty_exceptions_show_locals=self.pretty_exceptions_show_locals, 336 pretty_exceptions_short=self.pretty_exceptions_short, 337 ), 338 ) 339 raise e
Typer( *, name: Optional[str] = <typer.models.DefaultPlaceholder object>, cls: Optional[Type[typer.core.TyperGroup]] = <typer.models.DefaultPlaceholder object>, invoke_without_command: bool = <typer.models.DefaultPlaceholder object>, no_args_is_help: bool = <typer.models.DefaultPlaceholder object>, subcommand_metavar: Optional[str] = <typer.models.DefaultPlaceholder object>, chain: bool = <typer.models.DefaultPlaceholder object>, result_callback: Optional[Callable[..., Any]] = <typer.models.DefaultPlaceholder object>, context_settings: Optional[Dict[Any, Any]] = <typer.models.DefaultPlaceholder object>, callback: Optional[Callable[..., Any]] = <typer.models.DefaultPlaceholder object>, help: Optional[str] = <typer.models.DefaultPlaceholder object>, epilog: Optional[str] = <typer.models.DefaultPlaceholder object>, short_help: Optional[str] = <typer.models.DefaultPlaceholder object>, options_metavar: str = <typer.models.DefaultPlaceholder object>, add_help_option: bool = <typer.models.DefaultPlaceholder object>, hidden: bool = <typer.models.DefaultPlaceholder object>, deprecated: bool = <typer.models.DefaultPlaceholder object>, add_completion: bool = True, rich_markup_mode: Literal['markdown', 'rich', None] = <typer.models.DefaultPlaceholder object>, rich_help_panel: Optional[str] = <typer.models.DefaultPlaceholder object>, pretty_exceptions_enable: bool = True, pretty_exceptions_show_locals: bool = True, pretty_exceptions_short: bool = True)
126 def __init__( 127 self, 128 *, 129 name: Optional[str] = Default(None), 130 cls: Optional[Type[TyperGroup]] = Default(None), 131 invoke_without_command: bool = Default(False), 132 no_args_is_help: bool = Default(False), 133 subcommand_metavar: Optional[str] = Default(None), 134 chain: bool = Default(False), 135 result_callback: Optional[Callable[..., Any]] = Default(None), 136 # Command 137 context_settings: Optional[Dict[Any, Any]] = Default(None), 138 callback: Optional[Callable[..., Any]] = Default(None), 139 help: Optional[str] = Default(None), 140 epilog: Optional[str] = Default(None), 141 short_help: Optional[str] = Default(None), 142 options_metavar: str = Default("[OPTIONS]"), 143 add_help_option: bool = Default(True), 144 hidden: bool = Default(False), 145 deprecated: bool = Default(False), 146 add_completion: bool = True, 147 # Rich settings 148 rich_markup_mode: MarkupMode = Default(DEFAULT_MARKUP_MODE), 149 rich_help_panel: Union[str, None] = Default(None), 150 pretty_exceptions_enable: bool = True, 151 pretty_exceptions_show_locals: bool = True, 152 pretty_exceptions_short: bool = True, 153 ): 154 self._add_completion = add_completion 155 self.rich_markup_mode: MarkupMode = rich_markup_mode 156 self.rich_help_panel = rich_help_panel 157 self.pretty_exceptions_enable = pretty_exceptions_enable 158 self.pretty_exceptions_show_locals = pretty_exceptions_show_locals 159 self.pretty_exceptions_short = pretty_exceptions_short 160 self.info = TyperInfo( 161 name=name, 162 cls=cls, 163 invoke_without_command=invoke_without_command, 164 no_args_is_help=no_args_is_help, 165 subcommand_metavar=subcommand_metavar, 166 chain=chain, 167 result_callback=result_callback, 168 context_settings=context_settings, 169 callback=callback, 170 help=help, 171 epilog=epilog, 172 short_help=short_help, 173 options_metavar=options_metavar, 174 add_help_option=add_help_option, 175 hidden=hidden, 176 deprecated=deprecated, 177 ) 178 self.registered_groups: List[TyperInfo] = [] 179 self.registered_commands: List[CommandInfo] = [] 180 self.registered_callback: Optional[TyperInfo] = None
registered_groups: List[typer.models.TyperInfo]
registered_commands: List[typer.models.CommandInfo]
registered_callback: Optional[typer.models.TyperInfo]
def
callback( self, name: Optional[str] = <typer.models.DefaultPlaceholder object>, *, cls: Optional[Type[typer.core.TyperGroup]] = <typer.models.DefaultPlaceholder object>, invoke_without_command: bool = <typer.models.DefaultPlaceholder object>, no_args_is_help: bool = <typer.models.DefaultPlaceholder object>, subcommand_metavar: Optional[str] = <typer.models.DefaultPlaceholder object>, chain: bool = <typer.models.DefaultPlaceholder object>, result_callback: Optional[Callable[..., Any]] = <typer.models.DefaultPlaceholder object>, context_settings: Optional[Dict[Any, Any]] = <typer.models.DefaultPlaceholder object>, help: Optional[str] = <typer.models.DefaultPlaceholder object>, epilog: Optional[str] = <typer.models.DefaultPlaceholder object>, short_help: Optional[str] = <typer.models.DefaultPlaceholder object>, options_metavar: str = <typer.models.DefaultPlaceholder object>, add_help_option: bool = <typer.models.DefaultPlaceholder object>, hidden: bool = <typer.models.DefaultPlaceholder object>, deprecated: bool = <typer.models.DefaultPlaceholder object>, rich_help_panel: Optional[str] = <typer.models.DefaultPlaceholder object>) -> Callable[[~CommandFunctionType], ~CommandFunctionType]:
182 def callback( 183 self, 184 name: Optional[str] = Default(None), 185 *, 186 cls: Optional[Type[TyperGroup]] = Default(None), 187 invoke_without_command: bool = Default(False), 188 no_args_is_help: bool = Default(False), 189 subcommand_metavar: Optional[str] = Default(None), 190 chain: bool = Default(False), 191 result_callback: Optional[Callable[..., Any]] = Default(None), 192 # Command 193 context_settings: Optional[Dict[Any, Any]] = Default(None), 194 help: Optional[str] = Default(None), 195 epilog: Optional[str] = Default(None), 196 short_help: Optional[str] = Default(None), 197 options_metavar: str = Default("[OPTIONS]"), 198 add_help_option: bool = Default(True), 199 hidden: bool = Default(False), 200 deprecated: bool = Default(False), 201 # Rich settings 202 rich_help_panel: Union[str, None] = Default(None), 203 ) -> Callable[[CommandFunctionType], CommandFunctionType]: 204 def decorator(f: CommandFunctionType) -> CommandFunctionType: 205 self.registered_callback = TyperInfo( 206 name=name, 207 cls=cls, 208 invoke_without_command=invoke_without_command, 209 no_args_is_help=no_args_is_help, 210 subcommand_metavar=subcommand_metavar, 211 chain=chain, 212 result_callback=result_callback, 213 context_settings=context_settings, 214 callback=f, 215 help=help, 216 epilog=epilog, 217 short_help=short_help, 218 options_metavar=options_metavar, 219 add_help_option=add_help_option, 220 hidden=hidden, 221 deprecated=deprecated, 222 rich_help_panel=rich_help_panel, 223 ) 224 return f 225 226 return decorator
def
command( self, name: Optional[str] = None, *, cls: Optional[Type[typer.core.TyperCommand]] = None, context_settings: Optional[Dict[Any, Any]] = None, help: Optional[str] = None, epilog: Optional[str] = None, short_help: Optional[str] = None, options_metavar: str = '[OPTIONS]', add_help_option: bool = True, no_args_is_help: bool = False, hidden: bool = False, deprecated: bool = False, rich_help_panel: Optional[str] = <typer.models.DefaultPlaceholder object>) -> Callable[[~CommandFunctionType], ~CommandFunctionType]:
228 def command( 229 self, 230 name: Optional[str] = None, 231 *, 232 cls: Optional[Type[TyperCommand]] = None, 233 context_settings: Optional[Dict[Any, Any]] = None, 234 help: Optional[str] = None, 235 epilog: Optional[str] = None, 236 short_help: Optional[str] = None, 237 options_metavar: str = "[OPTIONS]", 238 add_help_option: bool = True, 239 no_args_is_help: bool = False, 240 hidden: bool = False, 241 deprecated: bool = False, 242 # Rich settings 243 rich_help_panel: Union[str, None] = Default(None), 244 ) -> Callable[[CommandFunctionType], CommandFunctionType]: 245 if cls is None: 246 cls = TyperCommand 247 248 def decorator(f: CommandFunctionType) -> CommandFunctionType: 249 self.registered_commands.append( 250 CommandInfo( 251 name=name, 252 cls=cls, 253 context_settings=context_settings, 254 callback=f, 255 help=help, 256 epilog=epilog, 257 short_help=short_help, 258 options_metavar=options_metavar, 259 add_help_option=add_help_option, 260 no_args_is_help=no_args_is_help, 261 hidden=hidden, 262 deprecated=deprecated, 263 # Rich settings 264 rich_help_panel=rich_help_panel, 265 ) 266 ) 267 return f 268 269 return decorator
def
add_typer( self, typer_instance: Typer, *, name: Optional[str] = <typer.models.DefaultPlaceholder object>, cls: Optional[Type[typer.core.TyperGroup]] = <typer.models.DefaultPlaceholder object>, invoke_without_command: bool = <typer.models.DefaultPlaceholder object>, no_args_is_help: bool = <typer.models.DefaultPlaceholder object>, subcommand_metavar: Optional[str] = <typer.models.DefaultPlaceholder object>, chain: bool = <typer.models.DefaultPlaceholder object>, result_callback: Optional[Callable[..., Any]] = <typer.models.DefaultPlaceholder object>, context_settings: Optional[Dict[Any, Any]] = <typer.models.DefaultPlaceholder object>, callback: Optional[Callable[..., Any]] = <typer.models.DefaultPlaceholder object>, help: Optional[str] = <typer.models.DefaultPlaceholder object>, epilog: Optional[str] = <typer.models.DefaultPlaceholder object>, short_help: Optional[str] = <typer.models.DefaultPlaceholder object>, options_metavar: str = <typer.models.DefaultPlaceholder object>, add_help_option: bool = <typer.models.DefaultPlaceholder object>, hidden: bool = <typer.models.DefaultPlaceholder object>, deprecated: bool = <typer.models.DefaultPlaceholder object>, rich_help_panel: Optional[str] = <typer.models.DefaultPlaceholder object>) -> None:
271 def add_typer( 272 self, 273 typer_instance: "Typer", 274 *, 275 name: Optional[str] = Default(None), 276 cls: Optional[Type[TyperGroup]] = Default(None), 277 invoke_without_command: bool = Default(False), 278 no_args_is_help: bool = Default(False), 279 subcommand_metavar: Optional[str] = Default(None), 280 chain: bool = Default(False), 281 result_callback: Optional[Callable[..., Any]] = Default(None), 282 # Command 283 context_settings: Optional[Dict[Any, Any]] = Default(None), 284 callback: Optional[Callable[..., Any]] = Default(None), 285 help: Optional[str] = Default(None), 286 epilog: Optional[str] = Default(None), 287 short_help: Optional[str] = Default(None), 288 options_metavar: str = Default("[OPTIONS]"), 289 add_help_option: bool = Default(True), 290 hidden: bool = Default(False), 291 deprecated: bool = Default(False), 292 # Rich settings 293 rich_help_panel: Union[str, None] = Default(None), 294 ) -> None: 295 self.registered_groups.append( 296 TyperInfo( 297 typer_instance, 298 name=name, 299 cls=cls, 300 invoke_without_command=invoke_without_command, 301 no_args_is_help=no_args_is_help, 302 subcommand_metavar=subcommand_metavar, 303 chain=chain, 304 result_callback=result_callback, 305 context_settings=context_settings, 306 callback=callback, 307 help=help, 308 epilog=epilog, 309 short_help=short_help, 310 options_metavar=options_metavar, 311 add_help_option=add_help_option, 312 hidden=hidden, 313 deprecated=deprecated, 314 rich_help_panel=rich_help_panel, 315 ) 316 )
351def get_command(typer_instance: Typer) -> click.Command: 352 if typer_instance._add_completion: 353 click_install_param, click_show_param = get_install_completion_arguments() 354 if ( 355 typer_instance.registered_callback 356 or typer_instance.info.callback 357 or typer_instance.registered_groups 358 or len(typer_instance.registered_commands) > 1 359 ): 360 # Create a Group 361 click_command: click.Command = get_group(typer_instance) 362 if typer_instance._add_completion: 363 click_command.params.append(click_install_param) 364 click_command.params.append(click_show_param) 365 return click_command 366 elif len(typer_instance.registered_commands) == 1: 367 # Create a single Command 368 single_command = typer_instance.registered_commands[0] 369 370 if not single_command.context_settings and not isinstance( 371 typer_instance.info.context_settings, DefaultPlaceholder 372 ): 373 single_command.context_settings = typer_instance.info.context_settings 374 375 click_command = get_command_from_info( 376 single_command, 377 pretty_exceptions_short=typer_instance.pretty_exceptions_short, 378 rich_markup_mode=typer_instance.rich_markup_mode, 379 ) 380 if typer_instance._add_completion: 381 click_command.params.append(click_install_param) 382 click_command.params.append(click_show_param) 383 return click_command 384 raise RuntimeError( 385 "Could not get a command for this Typer instance" 386 ) # pragma: no cover
389def get_group_name(typer_info: TyperInfo) -> Optional[str]: 390 if typer_info.callback: 391 # Priority 1: Callback passed in app.add_typer() 392 return get_command_name(typer_info.callback.__name__) 393 if typer_info.typer_instance: 394 registered_callback = typer_info.typer_instance.registered_callback 395 if registered_callback: 396 if registered_callback.callback: 397 # Priority 2: Callback passed in @subapp.callback() 398 return get_command_name(registered_callback.callback.__name__) 399 if typer_info.typer_instance.info.callback: 400 return get_command_name(typer_info.typer_instance.info.callback.__name__) 401 return None
404def solve_typer_info_help(typer_info: TyperInfo) -> str: 405 # Priority 1: Explicit value was set in app.add_typer() 406 if not isinstance(typer_info.help, DefaultPlaceholder): 407 return inspect.cleandoc(typer_info.help or "") 408 # Priority 2: Explicit value was set in sub_app.callback() 409 try: 410 callback_help = typer_info.typer_instance.registered_callback.help 411 if not isinstance(callback_help, DefaultPlaceholder): 412 return inspect.cleandoc(callback_help or "") 413 except AttributeError: 414 pass 415 # Priority 3: Explicit value was set in sub_app = typer.Typer() 416 try: 417 instance_help = typer_info.typer_instance.info.help 418 if not isinstance(instance_help, DefaultPlaceholder): 419 return inspect.cleandoc(instance_help or "") 420 except AttributeError: 421 pass 422 # Priority 4: Implicit inference from callback docstring in app.add_typer() 423 if typer_info.callback: 424 doc = inspect.getdoc(typer_info.callback) 425 if doc: 426 return doc 427 # Priority 5: Implicit inference from callback docstring in @app.callback() 428 try: 429 callback = typer_info.typer_instance.registered_callback.callback 430 if not isinstance(callback, DefaultPlaceholder): 431 doc = inspect.getdoc(callback or "") 432 if doc: 433 return doc 434 except AttributeError: 435 pass 436 # Priority 6: Implicit inference from callback docstring in typer.Typer() 437 try: 438 instance_callback = typer_info.typer_instance.info.callback 439 if not isinstance(instance_callback, DefaultPlaceholder): 440 doc = inspect.getdoc(instance_callback) 441 if doc: 442 return doc 443 except AttributeError: 444 pass 445 # Value not set, use the default 446 return typer_info.help.value
449def solve_typer_info_defaults(typer_info: TyperInfo) -> TyperInfo: 450 values: Dict[str, Any] = {} 451 for name, value in typer_info.__dict__.items(): 452 # Priority 1: Value was set in app.add_typer() 453 if not isinstance(value, DefaultPlaceholder): 454 values[name] = value 455 continue 456 # Priority 2: Value was set in @subapp.callback() 457 try: 458 callback_value = getattr( 459 typer_info.typer_instance.registered_callback, # type: ignore 460 name, 461 ) 462 if not isinstance(callback_value, DefaultPlaceholder): 463 values[name] = callback_value 464 continue 465 except AttributeError: 466 pass 467 # Priority 3: Value set in subapp = typer.Typer() 468 try: 469 instance_value = getattr( 470 typer_info.typer_instance.info, # type: ignore 471 name, 472 ) 473 if not isinstance(instance_value, DefaultPlaceholder): 474 values[name] = instance_value 475 continue 476 except AttributeError: 477 pass 478 # Value not set, use the default 479 values[name] = value.value 480 if values["name"] is None: 481 values["name"] = get_group_name(typer_info) 482 values["help"] = solve_typer_info_help(typer_info) 483 return TyperInfo(**values)
def
get_group_from_info( group_info: typer.models.TyperInfo, *, pretty_exceptions_short: bool, rich_markup_mode: Literal['markdown', 'rich', None]) -> typer.core.TyperGroup:
486def get_group_from_info( 487 group_info: TyperInfo, 488 *, 489 pretty_exceptions_short: bool, 490 rich_markup_mode: MarkupMode, 491) -> TyperGroup: 492 assert ( 493 group_info.typer_instance 494 ), "A Typer instance is needed to generate a Click Group" 495 commands: Dict[str, click.Command] = {} 496 for command_info in group_info.typer_instance.registered_commands: 497 command = get_command_from_info( 498 command_info=command_info, 499 pretty_exceptions_short=pretty_exceptions_short, 500 rich_markup_mode=rich_markup_mode, 501 ) 502 if command.name: 503 commands[command.name] = command 504 for sub_group_info in group_info.typer_instance.registered_groups: 505 sub_group = get_group_from_info( 506 sub_group_info, 507 pretty_exceptions_short=pretty_exceptions_short, 508 rich_markup_mode=rich_markup_mode, 509 ) 510 if sub_group.name: 511 commands[sub_group.name] = sub_group 512 solved_info = solve_typer_info_defaults(group_info) 513 ( 514 params, 515 convertors, 516 context_param_name, 517 ) = get_params_convertors_ctx_param_name_from_function(solved_info.callback) 518 cls = solved_info.cls or TyperGroup 519 assert issubclass(cls, TyperGroup), f"{cls} should be a subclass of {TyperGroup}" 520 group = cls( 521 name=solved_info.name or "", 522 commands=commands, 523 invoke_without_command=solved_info.invoke_without_command, 524 no_args_is_help=solved_info.no_args_is_help, 525 subcommand_metavar=solved_info.subcommand_metavar, 526 chain=solved_info.chain, 527 result_callback=solved_info.result_callback, 528 context_settings=solved_info.context_settings, 529 callback=get_callback( 530 callback=solved_info.callback, 531 params=params, 532 convertors=convertors, 533 context_param_name=context_param_name, 534 pretty_exceptions_short=pretty_exceptions_short, 535 ), 536 params=params, 537 help=solved_info.help, 538 epilog=solved_info.epilog, 539 short_help=solved_info.short_help, 540 options_metavar=solved_info.options_metavar, 541 add_help_option=solved_info.add_help_option, 542 hidden=solved_info.hidden, 543 deprecated=solved_info.deprecated, 544 rich_markup_mode=rich_markup_mode, 545 # Rich settings 546 rich_help_panel=solved_info.rich_help_panel, 547 ) 548 return group
def
get_command_name(name: str) -> str:
def
get_params_convertors_ctx_param_name_from_function( callback: Optional[Callable[..., Any]]) -> Tuple[List[Union[click.core.Argument, click.core.Option]], Dict[str, Any], Optional[str]]:
555def get_params_convertors_ctx_param_name_from_function( 556 callback: Optional[Callable[..., Any]], 557) -> Tuple[List[Union[click.Argument, click.Option]], Dict[str, Any], Optional[str]]: 558 params = [] 559 convertors = {} 560 context_param_name = None 561 if callback: 562 parameters = get_params_from_function(callback) 563 for param_name, param in parameters.items(): 564 if lenient_issubclass(param.annotation, click.Context): 565 context_param_name = param_name 566 continue 567 click_param, convertor = get_click_param(param) 568 if convertor: 569 convertors[param_name] = convertor 570 params.append(click_param) 571 return params, convertors, context_param_name
def
get_command_from_info( command_info: typer.models.CommandInfo, *, pretty_exceptions_short: bool, rich_markup_mode: Literal['markdown', 'rich', None]) -> click.core.Command:
574def get_command_from_info( 575 command_info: CommandInfo, 576 *, 577 pretty_exceptions_short: bool, 578 rich_markup_mode: MarkupMode, 579) -> click.Command: 580 assert command_info.callback, "A command must have a callback function" 581 name = command_info.name or get_command_name(command_info.callback.__name__) 582 use_help = command_info.help 583 if use_help is None: 584 use_help = inspect.getdoc(command_info.callback) 585 else: 586 use_help = inspect.cleandoc(use_help) 587 ( 588 params, 589 convertors, 590 context_param_name, 591 ) = get_params_convertors_ctx_param_name_from_function(command_info.callback) 592 cls = command_info.cls or TyperCommand 593 command = cls( 594 name=name, 595 context_settings=command_info.context_settings, 596 callback=get_callback( 597 callback=command_info.callback, 598 params=params, 599 convertors=convertors, 600 context_param_name=context_param_name, 601 pretty_exceptions_short=pretty_exceptions_short, 602 ), 603 params=params, # type: ignore 604 help=use_help, 605 epilog=command_info.epilog, 606 short_help=command_info.short_help, 607 options_metavar=command_info.options_metavar, 608 add_help_option=command_info.add_help_option, 609 no_args_is_help=command_info.no_args_is_help, 610 hidden=command_info.hidden, 611 deprecated=command_info.deprecated, 612 rich_markup_mode=rich_markup_mode, 613 # Rich settings 614 rich_help_panel=command_info.rich_help_panel, 615 ) 616 return command
def
determine_type_convertor(type_: Any) -> Optional[Callable[[Any], Any]]:
619def determine_type_convertor(type_: Any) -> Optional[Callable[[Any], Any]]: 620 convertor: Optional[Callable[[Any], Any]] = None 621 if lenient_issubclass(type_, Path): 622 convertor = param_path_convertor 623 if lenient_issubclass(type_, Enum): 624 convertor = generate_enum_convertor(type_) 625 return convertor
def
generate_enum_convertor(enum: Type[enum.Enum]) -> Callable[[Any], Any]:
634def generate_enum_convertor(enum: Type[Enum]) -> Callable[[Any], Any]: 635 val_map = {str(val.value): val for val in enum} 636 637 def convertor(value: Any) -> Any: 638 if value is not None: 639 val = str(value) 640 if val in val_map: 641 key = val_map[val] 642 return enum(key) 643 644 return convertor
def
generate_list_convertor( convertor: Optional[Callable[[Any], Any]], default_value: Optional[Any]) -> Callable[[Sequence[Any]], Optional[List[Any]]]:
647def generate_list_convertor( 648 convertor: Optional[Callable[[Any], Any]], default_value: Optional[Any] 649) -> Callable[[Sequence[Any]], Optional[List[Any]]]: 650 def internal_convertor(value: Sequence[Any]) -> Optional[List[Any]]: 651 if default_value is None and len(value) == 0: 652 return None 653 return [convertor(v) if convertor else v for v in value] 654 655 return internal_convertor
def
generate_tuple_convertor( types: Sequence[Any]) -> Callable[[Optional[Tuple[Any, ...]]], Optional[Tuple[Any, ...]]]:
658def generate_tuple_convertor( 659 types: Sequence[Any], 660) -> Callable[[Optional[Tuple[Any, ...]]], Optional[Tuple[Any, ...]]]: 661 convertors = [determine_type_convertor(type_) for type_ in types] 662 663 def internal_convertor( 664 param_args: Optional[Tuple[Any, ...]], 665 ) -> Optional[Tuple[Any, ...]]: 666 if param_args is None: 667 return None 668 return tuple( 669 convertor(arg) if convertor else arg 670 for (convertor, arg) in zip(convertors, param_args) 671 ) 672 673 return internal_convertor
def
get_callback( *, callback: Optional[Callable[..., Any]] = None, params: Sequence[click.core.Parameter] = [], convertors: Optional[Dict[str, Callable[[str], Any]]] = None, context_param_name: Optional[str] = None, pretty_exceptions_short: bool) -> Optional[Callable[..., Any]]:
676def get_callback( 677 *, 678 callback: Optional[Callable[..., Any]] = None, 679 params: Sequence[click.Parameter] = [], 680 convertors: Optional[Dict[str, Callable[[str], Any]]] = None, 681 context_param_name: Optional[str] = None, 682 pretty_exceptions_short: bool, 683) -> Optional[Callable[..., Any]]: 684 use_convertors = convertors or {} 685 if not callback: 686 return None 687 parameters = get_params_from_function(callback) 688 use_params: Dict[str, Any] = {} 689 for param_name in parameters: 690 use_params[param_name] = None 691 for param in params: 692 if param.name: 693 use_params[param.name] = param.default 694 695 def wrapper(**kwargs: Any) -> Any: 696 _rich_traceback_guard = pretty_exceptions_short # noqa: F841 697 for k, v in kwargs.items(): 698 if k in use_convertors: 699 use_params[k] = use_convertors[k](v) 700 else: 701 use_params[k] = v 702 if context_param_name: 703 use_params[context_param_name] = click.get_current_context() 704 return callback(**use_params) 705 706 update_wrapper(wrapper, callback) 707 return wrapper
def
get_click_type( *, annotation: Any, parameter_info: typer.models.ParameterInfo) -> click.types.ParamType:
710def get_click_type( 711 *, annotation: Any, parameter_info: ParameterInfo 712) -> click.ParamType: 713 if parameter_info.click_type is not None: 714 return parameter_info.click_type 715 716 elif parameter_info.parser is not None: 717 return click.types.FuncParamType(parameter_info.parser) 718 719 elif annotation == str: 720 return click.STRING 721 elif annotation == int: 722 if parameter_info.min is not None or parameter_info.max is not None: 723 min_ = None 724 max_ = None 725 if parameter_info.min is not None: 726 min_ = int(parameter_info.min) 727 if parameter_info.max is not None: 728 max_ = int(parameter_info.max) 729 return click.IntRange(min=min_, max=max_, clamp=parameter_info.clamp) 730 else: 731 return click.INT 732 elif annotation == float: 733 if parameter_info.min is not None or parameter_info.max is not None: 734 return click.FloatRange( 735 min=parameter_info.min, 736 max=parameter_info.max, 737 clamp=parameter_info.clamp, 738 ) 739 else: 740 return click.FLOAT 741 elif annotation == bool: 742 return click.BOOL 743 elif annotation == UUID: 744 return click.UUID 745 elif annotation == datetime: 746 return click.DateTime(formats=parameter_info.formats) 747 elif ( 748 annotation == Path 749 or parameter_info.allow_dash 750 or parameter_info.path_type 751 or parameter_info.resolve_path 752 ): 753 return click.Path( 754 exists=parameter_info.exists, 755 file_okay=parameter_info.file_okay, 756 dir_okay=parameter_info.dir_okay, 757 writable=parameter_info.writable, 758 readable=parameter_info.readable, 759 resolve_path=parameter_info.resolve_path, 760 allow_dash=parameter_info.allow_dash, 761 path_type=parameter_info.path_type, 762 ) 763 elif lenient_issubclass(annotation, FileTextWrite): 764 return click.File( 765 mode=parameter_info.mode or "w", 766 encoding=parameter_info.encoding, 767 errors=parameter_info.errors, 768 lazy=parameter_info.lazy, 769 atomic=parameter_info.atomic, 770 ) 771 elif lenient_issubclass(annotation, FileText): 772 return click.File( 773 mode=parameter_info.mode or "r", 774 encoding=parameter_info.encoding, 775 errors=parameter_info.errors, 776 lazy=parameter_info.lazy, 777 atomic=parameter_info.atomic, 778 ) 779 elif lenient_issubclass(annotation, FileBinaryRead): 780 return click.File( 781 mode=parameter_info.mode or "rb", 782 encoding=parameter_info.encoding, 783 errors=parameter_info.errors, 784 lazy=parameter_info.lazy, 785 atomic=parameter_info.atomic, 786 ) 787 elif lenient_issubclass(annotation, FileBinaryWrite): 788 return click.File( 789 mode=parameter_info.mode or "wb", 790 encoding=parameter_info.encoding, 791 errors=parameter_info.errors, 792 lazy=parameter_info.lazy, 793 atomic=parameter_info.atomic, 794 ) 795 elif lenient_issubclass(annotation, Enum): 796 return click.Choice( 797 [item.value for item in annotation], 798 case_sensitive=parameter_info.case_sensitive, 799 ) 800 raise RuntimeError(f"Type not yet supported: {annotation}") # pragma: no cover
def
lenient_issubclass( cls: Any, class_or_tuple: Union[Type[Any], Tuple[Type[Any], ...]]) -> bool:
def
get_click_param( param: typer.models.ParamMeta) -> Tuple[Union[click.core.Argument, click.core.Option], Any]:
809def get_click_param( 810 param: ParamMeta, 811) -> Tuple[Union[click.Argument, click.Option], Any]: 812 # First, find out what will be: 813 # * ParamInfo (ArgumentInfo or OptionInfo) 814 # * default_value 815 # * required 816 default_value = None 817 required = False 818 if isinstance(param.default, ParameterInfo): 819 parameter_info = param.default 820 if parameter_info.default == Required: 821 required = True 822 else: 823 default_value = parameter_info.default 824 elif param.default == Required or param.default == param.empty: 825 required = True 826 parameter_info = ArgumentInfo() 827 else: 828 default_value = param.default 829 parameter_info = OptionInfo() 830 annotation: Any 831 if not param.annotation == param.empty: 832 annotation = param.annotation 833 else: 834 annotation = str 835 main_type = annotation 836 is_list = False 837 is_tuple = False 838 parameter_type: Any = None 839 is_flag = None 840 origin = get_origin(main_type) 841 842 if origin is not None: 843 # Handle SomeType | None and Optional[SomeType] 844 if is_union(origin): 845 types = [] 846 for type_ in get_args(main_type): 847 if type_ is NoneType: 848 continue 849 types.append(type_) 850 assert len(types) == 1, "Typer Currently doesn't support Union types" 851 main_type = types[0] 852 origin = get_origin(main_type) 853 # Handle Tuples and Lists 854 if lenient_issubclass(origin, List): 855 main_type = get_args(main_type)[0] 856 assert not get_origin( 857 main_type 858 ), "List types with complex sub-types are not currently supported" 859 is_list = True 860 elif lenient_issubclass(origin, Tuple): # type: ignore 861 types = [] 862 for type_ in get_args(main_type): 863 assert not get_origin( 864 type_ 865 ), "Tuple types with complex sub-types are not currently supported" 866 types.append( 867 get_click_type(annotation=type_, parameter_info=parameter_info) 868 ) 869 parameter_type = tuple(types) 870 is_tuple = True 871 if parameter_type is None: 872 parameter_type = get_click_type( 873 annotation=main_type, parameter_info=parameter_info 874 ) 875 convertor = determine_type_convertor(main_type) 876 if is_list: 877 convertor = generate_list_convertor( 878 convertor=convertor, default_value=default_value 879 ) 880 if is_tuple: 881 convertor = generate_tuple_convertor(get_args(main_type)) 882 if isinstance(parameter_info, OptionInfo): 883 if main_type is bool and parameter_info.is_flag is not False: 884 is_flag = True 885 # Click doesn't accept a flag of type bool, only None, and then it sets it 886 # to bool internally 887 parameter_type = None 888 default_option_name = get_command_name(param.name) 889 if is_flag: 890 default_option_declaration = ( 891 f"--{default_option_name}/--no-{default_option_name}" 892 ) 893 else: 894 default_option_declaration = f"--{default_option_name}" 895 param_decls = [param.name] 896 if parameter_info.param_decls: 897 param_decls.extend(parameter_info.param_decls) 898 else: 899 param_decls.append(default_option_declaration) 900 return ( 901 TyperOption( 902 # Option 903 param_decls=param_decls, 904 show_default=parameter_info.show_default, 905 prompt=parameter_info.prompt, 906 confirmation_prompt=parameter_info.confirmation_prompt, 907 prompt_required=parameter_info.prompt_required, 908 hide_input=parameter_info.hide_input, 909 is_flag=is_flag, 910 flag_value=parameter_info.flag_value, 911 multiple=is_list, 912 count=parameter_info.count, 913 allow_from_autoenv=parameter_info.allow_from_autoenv, 914 type=parameter_type, 915 help=parameter_info.help, 916 hidden=parameter_info.hidden, 917 show_choices=parameter_info.show_choices, 918 show_envvar=parameter_info.show_envvar, 919 # Parameter 920 required=required, 921 default=default_value, 922 callback=get_param_callback( 923 callback=parameter_info.callback, convertor=convertor 924 ), 925 metavar=parameter_info.metavar, 926 expose_value=parameter_info.expose_value, 927 is_eager=parameter_info.is_eager, 928 envvar=parameter_info.envvar, 929 shell_complete=parameter_info.shell_complete, 930 autocompletion=get_param_completion(parameter_info.autocompletion), 931 # Rich settings 932 rich_help_panel=parameter_info.rich_help_panel, 933 ), 934 convertor, 935 ) 936 elif isinstance(parameter_info, ArgumentInfo): 937 param_decls = [param.name] 938 nargs = None 939 if is_list: 940 nargs = -1 941 return ( 942 TyperArgument( 943 # Argument 944 param_decls=param_decls, 945 type=parameter_type, 946 required=required, 947 nargs=nargs, 948 # TyperArgument 949 show_default=parameter_info.show_default, 950 show_choices=parameter_info.show_choices, 951 show_envvar=parameter_info.show_envvar, 952 help=parameter_info.help, 953 hidden=parameter_info.hidden, 954 # Parameter 955 default=default_value, 956 callback=get_param_callback( 957 callback=parameter_info.callback, convertor=convertor 958 ), 959 metavar=parameter_info.metavar, 960 expose_value=parameter_info.expose_value, 961 is_eager=parameter_info.is_eager, 962 envvar=parameter_info.envvar, 963 shell_complete=parameter_info.shell_complete, 964 autocompletion=get_param_completion(parameter_info.autocompletion), 965 # Rich settings 966 rich_help_panel=parameter_info.rich_help_panel, 967 ), 968 convertor, 969 ) 970 raise AssertionError("A click.Parameter should be returned") # pragma: no cover
def
get_param_callback( *, callback: Optional[Callable[..., Any]] = None, convertor: Optional[Callable[..., Any]] = None) -> Optional[Callable[..., Any]]:
973def get_param_callback( 974 *, 975 callback: Optional[Callable[..., Any]] = None, 976 convertor: Optional[Callable[..., Any]] = None, 977) -> Optional[Callable[..., Any]]: 978 if not callback: 979 return None 980 parameters = get_params_from_function(callback) 981 ctx_name = None 982 click_param_name = None 983 value_name = None 984 untyped_names: List[str] = [] 985 for param_name, param_sig in parameters.items(): 986 if lenient_issubclass(param_sig.annotation, click.Context): 987 ctx_name = param_name 988 elif lenient_issubclass(param_sig.annotation, click.Parameter): 989 click_param_name = param_name 990 else: 991 untyped_names.append(param_name) 992 # Extract value param name first 993 if untyped_names: 994 value_name = untyped_names.pop() 995 # If context and Click param were not typed (old/Click callback style) extract them 996 if untyped_names: 997 if ctx_name is None: 998 ctx_name = untyped_names.pop(0) 999 if click_param_name is None: 1000 if untyped_names: 1001 click_param_name = untyped_names.pop(0) 1002 if untyped_names: 1003 raise click.ClickException( 1004 "Too many CLI parameter callback function parameters" 1005 ) 1006 1007 def wrapper(ctx: click.Context, param: click.Parameter, value: Any) -> Any: 1008 use_params: Dict[str, Any] = {} 1009 if ctx_name: 1010 use_params[ctx_name] = ctx 1011 if click_param_name: 1012 use_params[click_param_name] = param 1013 if value_name: 1014 if convertor: 1015 use_value = convertor(value) 1016 else: 1017 use_value = value 1018 use_params[value_name] = use_value 1019 return callback(**use_params) 1020 1021 update_wrapper(wrapper, callback) 1022 return wrapper
def
get_param_completion( callback: Optional[Callable[..., Any]] = None) -> Optional[Callable[..., Any]]:
1025def get_param_completion( 1026 callback: Optional[Callable[..., Any]] = None, 1027) -> Optional[Callable[..., Any]]: 1028 if not callback: 1029 return None 1030 parameters = get_params_from_function(callback) 1031 ctx_name = None 1032 args_name = None 1033 incomplete_name = None 1034 unassigned_params = list(parameters.values()) 1035 for param_sig in unassigned_params[:]: 1036 origin = get_origin(param_sig.annotation) 1037 if lenient_issubclass(param_sig.annotation, click.Context): 1038 ctx_name = param_sig.name 1039 unassigned_params.remove(param_sig) 1040 elif lenient_issubclass(origin, List): 1041 args_name = param_sig.name 1042 unassigned_params.remove(param_sig) 1043 elif lenient_issubclass(param_sig.annotation, str): 1044 incomplete_name = param_sig.name 1045 unassigned_params.remove(param_sig) 1046 # If there are still unassigned parameters (not typed), extract by name 1047 for param_sig in unassigned_params[:]: 1048 if ctx_name is None and param_sig.name == "ctx": 1049 ctx_name = param_sig.name 1050 unassigned_params.remove(param_sig) 1051 elif args_name is None and param_sig.name == "args": 1052 args_name = param_sig.name 1053 unassigned_params.remove(param_sig) 1054 elif incomplete_name is None and param_sig.name == "incomplete": 1055 incomplete_name = param_sig.name 1056 unassigned_params.remove(param_sig) 1057 # Extract value param name first 1058 if unassigned_params: 1059 show_params = " ".join([param.name for param in unassigned_params]) 1060 raise click.ClickException( 1061 f"Invalid autocompletion callback parameters: {show_params}" 1062 ) 1063 1064 def wrapper(ctx: click.Context, args: List[str], incomplete: Optional[str]) -> Any: 1065 use_params: Dict[str, Any] = {} 1066 if ctx_name: 1067 use_params[ctx_name] = ctx 1068 if args_name: 1069 use_params[args_name] = args 1070 if incomplete_name: 1071 use_params[incomplete_name] = incomplete 1072 return callback(**use_params) 1073 1074 update_wrapper(wrapper, callback) 1075 return wrapper
def
run(function: Callable[..., Any]) -> None: