Skip to content

Context

Every app has a special internal object that keeps track of state relevant to the script's execution. For some advanced use-cases, you may want to access it directly. This can be done by declaring a function parameter of type typer.Context.

Similarly, you can also declare a function parameter with type typer.CallbackParam in case a callback could be used by several CLI parameters, and you need to figure out which one it was.

from typing import Annotated

import typer

app = typer.Typer()


def name_callback(ctx: typer.Context, param: typer.CallbackParam, value: str):
    if ctx.resilient_parsing:
        return
    print(f"Validating param: {param.name}")
    if value != "Rick":
        raise typer.BadParameter("Only Rick is allowed")
    return value


@app.command()
def main(name: Annotated[str | None, typer.Option(callback=name_callback)] = None):
    print(f"Hello {name}")


if __name__ == "__main__":
    app()

typer.Context

Context(
    command,
    parent=None,
    info_name=None,
    obj=None,
    auto_envvar_prefix=None,
    default_map=None,
    terminal_width=None,
    max_content_width=None,
    resilient_parsing=False,
    allow_extra_args=None,
    allow_interspersed_args=None,
    ignore_unknown_options=None,
    help_option_names=None,
    token_normalize_func=None,
    color=None,
    show_default=None,
)

Bases: Context

The Context has some additional data about the current execution of your program. When declaring it in a callback function, you can access this additional information.

Source code in typer/_click/core.py
def __init__(
    self,
    command: "Command",
    parent: Union["Context", None] = None,
    info_name: str | None = None,
    obj: Any | None = None,
    auto_envvar_prefix: str | None = None,
    default_map: MutableMapping[str, Any] | None = None,
    terminal_width: int | None = None,
    max_content_width: int | None = None,
    resilient_parsing: bool = False,
    allow_extra_args: bool | None = None,
    allow_interspersed_args: bool | None = None,
    ignore_unknown_options: bool | None = None,
    help_option_names: list[str] | None = None,
    token_normalize_func: Callable[[str], str] | None = None,
    color: bool | None = None,
    show_default: bool | None = None,
) -> None:
    self.parent = parent
    self.command = command
    self.info_name = info_name
    # Map of parameter names to their parsed values.
    self.params: dict[str, Any] = {}
    # the leftover arguments.
    self.args: list[str] = []
    # protected arguments. used to implement nested parsing.
    self._protected_args: list[str] = []
    # the collected prefixes of the command's options.
    self._opt_prefixes: set[str] = set(parent._opt_prefixes) if parent else set()

    if obj is None and parent is not None:
        obj = parent.obj

    self.obj: Any = obj
    self._meta: dict[str, Any] = getattr(parent, "meta", {})

    # A dictionary (-like object) with defaults for parameters.
    if (
        default_map is None
        and info_name is not None
        and parent is not None
        and parent.default_map is not None
    ):
        default_map = parent.default_map.get(info_name)

    self.default_map: MutableMapping[str, Any] | None = default_map

    # This flag indicates if a subcommand is going to be executed.
    self.invoked_subcommand: str | None = None

    if terminal_width is None and parent is not None:
        terminal_width = parent.terminal_width

    # The width of the terminal (None is autodetection).
    self.terminal_width: int | None = terminal_width

    if max_content_width is None and parent is not None:
        max_content_width = parent.max_content_width

    self.max_content_width: int | None = max_content_width

    if allow_extra_args is None:
        allow_extra_args = command.allow_extra_args

    self.allow_extra_args = allow_extra_args

    if allow_interspersed_args is None:
        allow_interspersed_args = command.allow_interspersed_args

    self.allow_interspersed_args: bool = allow_interspersed_args

    if ignore_unknown_options is None:
        ignore_unknown_options = command.ignore_unknown_options

    self.ignore_unknown_options: bool = ignore_unknown_options

    if help_option_names is None:
        if parent is not None:
            help_option_names = parent.help_option_names
        else:
            help_option_names = ["--help"]

    self.help_option_names: list[str] = help_option_names

    if token_normalize_func is None and parent is not None:
        token_normalize_func = parent.token_normalize_func

    # An optional normalization function for tokens. (options, choices, commands etc.)
    self.token_normalize_func: Callable[[str], str] | None = token_normalize_func

    # Indicates if resilient parsing is enabled.
    self.resilient_parsing: bool = resilient_parsing

    # If there is no envvar prefix yet, but the parent has one and
    # the command on this level has a name, we can expand the envvar
    # prefix automatically.
    if auto_envvar_prefix is None:
        if (
            parent is not None
            and parent.auto_envvar_prefix is not None
            and self.info_name is not None
        ):
            auto_envvar_prefix = (
                f"{parent.auto_envvar_prefix}_{self.info_name.upper()}"
            )
    else:
        auto_envvar_prefix = auto_envvar_prefix.upper()

    if auto_envvar_prefix is not None:
        auto_envvar_prefix = auto_envvar_prefix.replace("-", "_")

    self.auto_envvar_prefix: str | None = auto_envvar_prefix

    if color is None and parent is not None:
        color = parent.color

    # Controls if styling output is wanted or not.
    self.color: bool | None = color

    if show_default is None and parent is not None:
        show_default = parent.show_default

    # Show option default values when formatting help text.
    self.show_default: bool | None = show_default

    self._close_callbacks: list[Callable[[], Any]] = []
    self._depth = 0
    self._parameter_source: dict[str, ParameterSource] = {}
    self._exit_stack = ExitStack()

formatter_class class-attribute instance-attribute

formatter_class = HelpFormatter

parent instance-attribute

parent = parent

command instance-attribute

command = command

info_name instance-attribute

info_name = info_name

params instance-attribute

params = {}

args instance-attribute

args = []

obj instance-attribute

obj = obj

default_map instance-attribute

default_map = default_map

invoked_subcommand instance-attribute

invoked_subcommand = None

terminal_width instance-attribute

terminal_width = terminal_width

max_content_width instance-attribute

max_content_width = max_content_width

allow_extra_args instance-attribute

allow_extra_args = allow_extra_args

allow_interspersed_args instance-attribute

allow_interspersed_args = allow_interspersed_args

ignore_unknown_options instance-attribute

ignore_unknown_options = ignore_unknown_options

help_option_names instance-attribute

help_option_names = help_option_names

token_normalize_func instance-attribute

token_normalize_func = token_normalize_func

resilient_parsing instance-attribute

resilient_parsing = resilient_parsing

auto_envvar_prefix instance-attribute

auto_envvar_prefix = auto_envvar_prefix

color instance-attribute

color = color

show_default instance-attribute

show_default = show_default

meta property

meta

This is a dictionary which is shared with all the contexts that are nested. It exists so that click utilities can store some state here if they need to. It is however the responsibility of that code to manage this dictionary well.

The keys are supposed to be unique dotted strings. For instance module paths are a good choice for it. What is stored in there is irrelevant for the operation of click. However what is important is that code that places data here adheres to the general semantics of the system.

command_path property

command_path

The computed command path. This is used for the usage information on the help page. It's automatically created by combining the info names of the chain of contexts to the root.

scope

scope(cleanup=True)

This helper method can be used with the context object to promote it to the current thread local (see get_current_context). The default behavior of this is to invoke the cleanup functions which can be disabled by setting cleanup to False. The cleanup functions are typically used for things such as closing file handles.

If the cleanup is intended the context object can also be directly used as a context manager.

Source code in typer/_click/core.py
@contextmanager
def scope(self, cleanup: bool = True) -> Iterator["Context"]:
    """This helper method can be used with the context object to promote
    it to the current thread local (see `get_current_context`).
    The default behavior of this is to invoke the cleanup functions which
    can be disabled by setting `cleanup` to `False`.  The cleanup
    functions are typically used for things such as closing file handles.

    If the cleanup is intended the context object can also be directly
    used as a context manager.
    """
    if not cleanup:
        self._depth += 1
    try:
        with self as rv:
            yield rv
    finally:
        if not cleanup:
            self._depth -= 1

make_formatter

make_formatter()

Creates the HelpFormatter for the help and usage output.

Source code in typer/_click/core.py
def make_formatter(self) -> HelpFormatter:
    """Creates the HelpFormatter for the help and
    usage output.
    """
    return self.formatter_class(
        width=self.terminal_width, max_width=self.max_content_width
    )

with_resource

with_resource(context_manager)

Register a resource as if it were used in a with statement. The resource will be cleaned up when the context is popped.

Uses contextlib.ExitStack.enter_context. It calls the resource's __enter__() method and returns the result. When the context is popped, it closes the stack, which calls the resource's __exit__() method.

To register a cleanup function for something that isn't a context manager, use call_on_close. Or use something from contextlib to turn it into a context manager first.

Source code in typer/_click/core.py
def with_resource(self, context_manager: AbstractContextManager[V]) -> V:
    """Register a resource as if it were used in a ``with``
    statement. The resource will be cleaned up when the context is
    popped.

    Uses `contextlib.ExitStack.enter_context`. It calls the
    resource's ``__enter__()`` method and returns the result. When
    the context is popped, it closes the stack, which calls the
    resource's ``__exit__()`` method.

    To register a cleanup function for something that isn't a
    context manager, use `call_on_close`. Or use something
    from `contextlib` to turn it into a context manager first.
    """
    return self._exit_stack.enter_context(context_manager)

call_on_close

call_on_close(f)

Register a function to be called when the context tears down.

This can be used to close resources opened during the script execution. Resources that support Python's context manager protocol which would be used in a with statement should be registered with with_resource instead.

Source code in typer/_click/core.py
def call_on_close(self, f: Callable[..., Any]) -> Callable[..., Any]:
    """Register a function to be called when the context tears down.

    This can be used to close resources opened during the script
    execution. Resources that support Python's context manager
    protocol which would be used in a ``with`` statement should be
    registered with `with_resource` instead.
    """
    return self._exit_stack.callback(f)

close

close()

Invoke all close callbacks registered with call_on_close, and exit all context managers entered with with_resource.

Source code in typer/_click/core.py
def close(self) -> None:
    """Invoke all close callbacks registered with `call_on_close`,
    and exit all context managers entered with `with_resource`.
    """
    self._close_with_exception_info(None, None, None)

find_root

find_root()

Finds the outermost context.

Source code in typer/_click/core.py
def find_root(self) -> "Context":
    """Finds the outermost context."""
    node = self
    while node.parent is not None:
        node = node.parent
    return node

find_object

find_object(object_type)

Finds the closest object of a given type.

Source code in typer/_click/core.py
def find_object(self, object_type: type[V]) -> V | None:
    """Finds the closest object of a given type."""
    node: Context | None = self

    while node is not None:
        if isinstance(node.obj, object_type):
            return node.obj

        node = node.parent

    return None

ensure_object

ensure_object(object_type)

Like find_object but sets the innermost object to a new instance of object_type if it does not exist.

Source code in typer/_click/core.py
def ensure_object(self, object_type: type[V]) -> V:
    """Like `find_object` but sets the innermost object to a
    new instance of `object_type` if it does not exist.
    """
    rv = self.find_object(object_type)
    if rv is None:
        self.obj = rv = object_type()
    return rv

lookup_default

lookup_default(
    name: str, call: Literal[True] = True
) -> Any | None
lookup_default(
    name: str, call: Literal[False] = ...
) -> Any | Callable[[], Any] | None
lookup_default(name, call=True)

Get the default for a parameter from default_map.

Source code in typer/_click/core.py
def lookup_default(self, name: str, call: bool = True) -> Any | None:
    """Get the default for a parameter from `default_map`."""
    if self.default_map is not None:
        value = self.default_map.get(name)

        if call and callable(value):
            return value()

        return value

    return None

fail

fail(message)

Aborts the execution of the program with a specific error message.

Source code in typer/_click/core.py
def fail(self, message: str) -> NoReturn:
    """Aborts the execution of the program with a specific error
    message.
    """
    raise UsageError(message, self)

abort

abort()

Aborts the script.

Source code in typer/_click/core.py
def abort(self) -> NoReturn:
    """Aborts the script."""
    raise Abort()

exit

exit(code=0)

Exits the application with a given exit code.

Source code in typer/_click/core.py
def exit(self, code: int = 0) -> NoReturn:
    """Exits the application with a given exit code."""
    self.close()
    raise Exit(code)

get_usage

get_usage()

Helper method to get formatted usage string for the current context and command.

Source code in typer/_click/core.py
def get_usage(self) -> str:
    """Helper method to get formatted usage string for the current
    context and command.
    """
    return self.command.get_usage(self)

get_help

get_help()

Helper method to get formatted help page for the current context and command.

Source code in typer/_click/core.py
def get_help(self) -> str:
    """Helper method to get formatted help page for the current
    context and command.
    """
    return self.command.get_help(self)

invoke

invoke(callback, /, *args, **kwargs)

Invokes a command callback in exactly the way it expects. There are two ways to invoke this method:

  1. the first argument can be a callback and all other arguments and keyword arguments are forwarded directly to the function.
  2. the first argument is a click command object. In that case all arguments are forwarded as well but proper click parameters (options and click arguments) must be keyword arguments and Click will fill in defaults.
Source code in typer/_click/core.py
def invoke(self, callback: Callable[..., V], /, *args: Any, **kwargs: Any) -> V:
    """Invokes a command callback in exactly the way it expects.  There
    are two ways to invoke this method:

    1.  the first argument can be a callback and all other arguments and
        keyword arguments are forwarded directly to the function.
    2.  the first argument is a click command object.  In that case all
        arguments are forwarded as well but proper click parameters
        (options and click arguments) must be keyword arguments and Click
        will fill in defaults.
    """
    ctx = self

    with augment_usage_errors(self):
        with ctx:
            return callback(*args, **kwargs)

set_parameter_source

set_parameter_source(name, source)

Set the source of a parameter. This indicates the location from which the value of the parameter was obtained.

Source code in typer/_click/core.py
def set_parameter_source(self, name: str, source: ParameterSource) -> None:
    """Set the source of a parameter. This indicates the location
    from which the value of the parameter was obtained.
    """
    self._parameter_source[name] = source

get_parameter_source

get_parameter_source(name)

Get the source of a parameter. This indicates the location from which the value of the parameter was obtained.

This can be useful for determining when a user specified a value on the command line that is the same as the default value. It will be ParameterSource.DEFAULT only if the value was actually taken from the default.

Source code in typer/_click/core.py
def get_parameter_source(self, name: str) -> ParameterSource | None:
    """Get the source of a parameter. This indicates the location
    from which the value of the parameter was obtained.

    This can be useful for determining when a user specified a value
    on the command line that is the same as the default value. It
    will be `ParameterSource.DEFAULT` only if the
    value was actually taken from the default.
    """
    return self._parameter_source.get(name)

typer.CallbackParam

CallbackParam(
    param_decls=None,
    type=None,
    required=False,
    default=None,
    callback=None,
    nargs=None,
    multiple=False,
    metavar=None,
    expose_value=True,
    is_eager=False,
    envvar=None,
    shell_complete=None,
)

Bases: Parameter

In a callback function, you can declare a function parameter with type CallbackParam to access the specific Click Parameter object.

Source code in typer/_click/core.py
def __init__(
    self,
    param_decls: Sequence[str] | None = None,
    type: types.ParamType | Any | None = None,
    required: bool = False,
    default: Any | Callable[[], Any] | None = None,
    callback: Callable[[Context, "Parameter", Any], Any] | None = None,
    nargs: int | None = None,
    multiple: bool = False,
    metavar: str | None = None,
    expose_value: bool = True,
    is_eager: bool = False,
    envvar: str | Sequence[str] | None = None,
    shell_complete: Callable[
        [Context, "Parameter", str], list["CompletionItem"] | list[str]
    ]
    | None = None,
) -> None:
    self.name: str | None
    self.opts: list[str]
    self.secondary_opts: list[str]
    self.name, self.opts, self.secondary_opts = self._parse_decls(
        param_decls or (), expose_value
    )
    self.type: types.ParamType = types.convert_type(type, default)

    # Default nargs to what the type tells us if we have that
    # information available.
    if nargs is None:
        if self.type.is_composite:
            nargs = self.type.arity
        else:
            nargs = 1

    self.required = required
    self.callback = callback
    self.nargs = nargs
    self.multiple = multiple
    self.expose_value = expose_value
    self.default: Any | Callable[[], Any] | None = default
    self.is_eager = is_eager
    self.metavar = metavar
    self.envvar = envvar
    self._custom_shell_complete = shell_complete

param_type_name class-attribute instance-attribute

param_type_name = 'parameter'

name instance-attribute

name

opts instance-attribute

opts

secondary_opts instance-attribute

secondary_opts

type instance-attribute

type = convert_type(type, default)

required instance-attribute

required = required

callback instance-attribute

callback = callback

nargs instance-attribute

nargs = nargs

multiple instance-attribute

multiple = multiple

expose_value instance-attribute

expose_value = expose_value

default instance-attribute

default = default

is_eager instance-attribute

is_eager = is_eager

metavar instance-attribute

metavar = metavar

envvar instance-attribute

envvar = envvar

human_readable_name property

human_readable_name

Returns the human readable name of this parameter. This is the same as the name for options, but the metavar for arguments.

make_metavar

make_metavar(ctx)
Source code in typer/_click/core.py
def make_metavar(self, ctx: Context) -> str:
    if self.metavar is not None:
        return self.metavar

    metavar = self.type.get_metavar(param=self, ctx=ctx)

    if metavar is None:
        metavar = self.type.name.upper()

    if self.nargs != 1:
        metavar += "..."

    return metavar

get_default

get_default(
    ctx: Context, call: Literal[True] = True
) -> Any | None
get_default(
    ctx: Context, call: bool = ...
) -> Any | Callable[[], Any] | None
get_default(ctx, call=True)

Get the default for the parameter

Source code in typer/_click/core.py
def get_default(
    self, ctx: Context, call: bool = True
) -> Any | Callable[[], Any] | None:
    """Get the default for the parameter"""
    value = ctx.lookup_default(self.name, call=False)  # type: ignore

    if value is None:
        value = self.default

    if call and callable(value):
        value = value()

    return value

add_to_parser abstractmethod

add_to_parser(parser, ctx)
Source code in typer/_click/core.py
@abstractmethod
def add_to_parser(self, parser: _OptionParser, ctx: Context) -> None:
    pass  # pragma: no cover

consume_value

consume_value(ctx, opts)
Source code in typer/_click/core.py
def consume_value(
    self, ctx: Context, opts: Mapping[str, Any]
) -> tuple[Any, ParameterSource]:
    value = opts.get(self.name)  # type: ignore
    source = ParameterSource.COMMANDLINE

    if value is None:
        value = self.value_from_envvar(ctx)
        source = ParameterSource.ENVIRONMENT

    if value is None:
        value = ctx.lookup_default(self.name)  # type: ignore
        source = ParameterSource.DEFAULT_MAP

    if value is None:
        value = self.get_default(ctx)
        source = ParameterSource.DEFAULT

    return value, source

type_cast_value

type_cast_value(ctx, value)

Convert and validate a value against the parameter's type, multiple, and nargs.

Source code in typer/_click/core.py
def type_cast_value(self, ctx: Context, value: Any) -> Any:
    """Convert and validate a value against the parameter's
    `type`, `multiple`, and `nargs`.
    """
    if value is None:
        return () if self.multiple or self.nargs == -1 else None

    def check_iter(value: Any) -> Iterator[Any]:
        assert not isinstance(value, str)
        return iter(value)

    # Define the conversion function based on nargs and type.
    if self.nargs == 1 or self.type.is_composite:

        def convert(value: Any) -> Any:
            return self.type(value, param=self, ctx=ctx)

    elif self.nargs == -1:

        def convert(value: Any) -> Any:  # tuple[t.Any, ...]
            return tuple(self.type(x, self, ctx) for x in check_iter(value))

    else:  # nargs > 1

        def convert(value: Any) -> Any:  # tuple[t.Any, ...]
            value = tuple(check_iter(value))

            if len(value) != self.nargs:
                raise BadParameter(
                    f"Takes {self.nargs} values but {len(value)} given.",
                    ctx=ctx,
                    param=self,
                )

            return tuple(self.type(x, self, ctx) for x in value)

    if self.multiple:
        return tuple(convert(x) for x in check_iter(value))

    return convert(value)

value_is_missing abstractmethod

value_is_missing(value)
Source code in typer/_click/core.py
@abstractmethod
def value_is_missing(self, value: Any) -> bool:
    pass  # pragma: no cover

process_value

process_value(ctx, value)

Process the value of this parameter

Source code in typer/_click/core.py
def process_value(self, ctx: Context, value: Any) -> Any:
    """Process the value of this parameter"""
    value = self.type_cast_value(ctx, value)

    if self.required and self.value_is_missing(value):
        raise MissingParameter(ctx=ctx, param=self)

    if self.callback is not None:
        value = self.callback(ctx, self, value)

    return value

resolve_envvar_value

resolve_envvar_value(ctx)

Returns the value found in the environment variable(s) attached to this parameter.

Environment variables values are always returned as strings <https://docs.python.org/3/library/os.html#os.environ>_.

This method returns None if:

  • the envvar property is not set on Parameter,
  • the environment variable is not found in the environment,
  • the variable is found in the environment but its value is empty (i.e. the environment variable is present but has an empty string).

If envvar is setup with multiple environment variables, then only the first non-empty value is returned.

Source code in typer/_click/core.py
def resolve_envvar_value(self, ctx: Context) -> str | None:
    """Returns the value found in the environment variable(s) attached to this
    parameter.

    Environment variables values are `always returned as strings
    <https://docs.python.org/3/library/os.html#os.environ>`_.

    This method returns ``None`` if:

    - the `envvar` property is not set on `Parameter`,
    - the environment variable is not found in the environment,
    - the variable is found in the environment but its value is empty (i.e. the
      environment variable is present but has an empty string).

    If `envvar` is setup with multiple environment variables,
    then only the first non-empty value is returned.
    """
    if self.envvar is None:
        return None

    if isinstance(self.envvar, str):
        rv = os.environ.get(self.envvar)

        if rv:
            return rv
    else:
        for envvar in self.envvar:
            rv = os.environ.get(envvar)

            # Return the first non-empty value of the list of environment variables.
            if rv:
                return rv
            # Else, absence of value is interpreted as an environment variable that
            # is not set, so proceed to the next one.

    return None

value_from_envvar

value_from_envvar(ctx)

Process the raw environment variable string for this parameter.

Returns the string as-is or splits it into a sequence of strings if the parameter is expecting multiple values (i.e. its nargs property is set to a value other than 1).

Source code in typer/_click/core.py
def value_from_envvar(self, ctx: Context) -> str | Sequence[str] | None:
    """Process the raw environment variable string for this parameter.

    Returns the string as-is or splits it into a sequence of strings if the
    parameter is expecting multiple values (i.e. its `nargs` property is set
    to a value other than ``1``).
    """
    rv: Any | None = self.resolve_envvar_value(ctx)

    if rv is not None and self.nargs != 1:
        rv = self.type.split_envvar_value(rv)

    return rv

handle_parse_result

handle_parse_result(ctx, opts, args)

Process the value produced by the parser from user input.

Always process the value through the Parameter's type, wherever it comes from.

If the parameter is deprecated, this method warn the user about it. But only if the value has been explicitly set by the user (and as such, is not coming from a default).

Source code in typer/_click/core.py
def handle_parse_result(
    self, ctx: Context, opts: Mapping[str, Any], args: list[str]
) -> tuple[Any, list[str]]:
    """Process the value produced by the parser from user input.

    Always process the value through the Parameter's `type`, wherever it
    comes from.

    If the parameter is deprecated, this method warn the user about it. But only if
    the value has been explicitly set by the user (and as such, is not coming from
    a default).
    """
    with augment_usage_errors(ctx, param=self):
        value, source = self.consume_value(ctx, opts)

        ctx.set_parameter_source(self.name, source)  # type: ignore

        # Process the value through the parameter's type.
        try:
            value = self.process_value(ctx, value)
        except Exception:
            if not ctx.resilient_parsing:
                raise
            value = None

    if self.expose_value:
        ctx.params[self.name] = value  # type: ignore

    return value, args

get_help_record abstractmethod

get_help_record(ctx)
Source code in typer/_click/core.py
@abstractmethod
def get_help_record(self, ctx: Context) -> tuple[str, str] | None:
    pass  # pragma: no cover

get_usage_pieces

get_usage_pieces(ctx)
Source code in typer/_click/core.py
def get_usage_pieces(self, ctx: Context) -> list[str]:
    return []

get_error_hint

get_error_hint(ctx)

Get a stringified version of the param for use in error messages to indicate which param caused the error.

Source code in typer/_click/core.py
def get_error_hint(self, ctx: Context) -> str:
    """Get a stringified version of the param for use in error messages to
    indicate which param caused the error.
    """
    hint_list = self.opts or [self.human_readable_name]
    return " / ".join(f"'{x}'" for x in hint_list)

shell_complete

shell_complete(ctx, incomplete)

Return a list of completions for the incomplete value. If a shell_complete function was given during init, it is used. Otherwise, the type ParamType.shell_complete function is used.

Source code in typer/_click/core.py
def shell_complete(self, ctx: Context, incomplete: str) -> list["CompletionItem"]:
    """Return a list of completions for the incomplete value. If a
    ``shell_complete`` function was given during init, it is used.
    Otherwise, the `type` `ParamType.shell_complete` function is used.
    """
    if self._custom_shell_complete is not None:
        results = self._custom_shell_complete(ctx, self, incomplete)

        if results and isinstance(results[0], str):
            from .shell_completion import CompletionItem

            results = [CompletionItem(c) for c in results]

        return cast("list[CompletionItem]", results)

    return self.type.shell_complete(ctx, self, incomplete)