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