typer.main

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

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

This function handles url in different operating systems separately:

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

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

Examples::

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