typer.main

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