typer.main

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

This function launches the given URL (or filename) in the default viewer application for this file type. If this is an executable, it might launch the executable in a new session. The return value is the exit code of the launched application. Usually, 0 indicates success.

This function handles url in different operating systems separately:

  • On macOS (Darwin), it uses the 'open' command.
  • On Linux and BSD, it uses 'xdg-open' if available.
  • On Windows (and other OSes), it uses the standard webbrowser module.

The function avoids, when possible, using the webbrowser module on Linux and macOS to prevent spammy terminal messages from some browsers (e.g., Chrome).

Examples::

typer.launch("https://typer.tiangolo.com/")
typer.launch("/my/downloaded/file", locate=True)
Parameters
  • url: URL or filename of the thing to launch.
  • wait: Wait for the program to exit before returning. This only works if the launched program blocks. In particular, xdg-open on Linux does not block.
  • locate: if this is set to True then instead of launching the application associated with the URL it will attempt to launch a file manager with the file located. This might have weird effects if the URL does not point to the filesystem.