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