def compatible_option(*param_decls: str, **attrs: t.Any):
    """
    Make a new ``@click.option`` decorator that provides backwards compatibility.

    This decorator extends the default ``@click.option`` decorator by adding three
    additional parameters:

        * ``equivalent``: a tuple of (new_option, example_usage) that specifies
                          the new option and its example usage that replaces the given option.
        * ``append_msg``: a string to append a help message to the original help message.
    """
    import click

    from .utils import MEMO_KEY
    from .utils import normalize_none_type

    append_msg = attrs.pop("append_msg", "")
    equivalent: tuple[str, str] = attrs.pop("equivalent", ())
    factory = attrs.pop("factory", click)

    assert equivalent is not None and len(equivalent) == 2, (
        "'equivalent' must be a tuple of (new_option, usage)"
    )

    prepend_msg = "(Equivalent to ``--%s %s``):" % equivalent

    def obsolete_callback(ctx: Context, param: Parameter, value: ClickParamType):
        # NOTE: We want to transform memoized options to tuple[str] | bool | None

        assert param.name is not None, "argument name must be set."

        opt = param.opts[0]
        # ensure that our memoized are available under CLI context.
        if MEMO_KEY not in ctx.params:
            # By default, multiple options are stored as a tuple.
            # our memoized options are stored as a dict.
            ctx.params[MEMO_KEY] = {}

        # default can be a callable. We only care about the result.
        default_value = param.get_default(ctx)

        # We need to run transformation here such that we don't have to deal with
        # nested value.
        # NOTE: if users are using both old and new options, the new option will take
        # precedence, and the old option will be ignored.
        value = normalize_none_type(value)

        # if given param.name is not in the memoized options, we need to create them.
        if param.name not in ctx.params[MEMO_KEY]:
            # Initialize the memoized options with default value.
            ctx.params[MEMO_KEY][param.name] = () if param.multiple else default_value
            if value is not None:
                # Only warning if given value is different from the default.
                # Since we are going to transform default value from old options to
                # the kwargs map, there is no need to bloat logs.
                if value != default_value:
                    msg = "is now deprecated, use the equivalent"
                    if isinstance(value, bool):
                        logger.warning(
                            "'%s' %s '--%s %s' instead.",
                            opt,
                            msg,
                            equivalent[0],
                            opt.lstrip("--"),
                        )
                    elif isinstance(value, tuple):
                        obsolete_format = " ".join(
                            map(lambda s: "%s=%s" % (opt, s), value)
                        )
                        new_format = " ".join(
                            map(
                                lambda s: "--%s %s=%s"
                                % (equivalent[0], opt.lstrip("--"), s),
                                value,
                            )
                        )
                        logger.warning(
                            "'%s' %s '%s' instead.", obsolete_format, msg, new_format
                        )
                    else:
                        assert isinstance(value, str)
                        logger.warning(
                            "'%s=%s' %s '--%s %s=%s' instead.",
                            opt,
                            value,
                            msg,
                            equivalent[0],
                            opt.lstrip("--"),
                            value,
                        )
                if isinstance(value, (bool, str)):
                    ctx.params[MEMO_KEY][param.name] = value
                elif isinstance(value, tuple):
                    assert param.multiple
                    ctx.params[MEMO_KEY][param.name] += value
                else:
                    raise ValueError(f"Unexpected value type: {type(value)}")

    def decorator(f: F[t.Any]) -> t.Callable[[F[t.Any]], Command]:
        # We will set default value to None if 'default' is not set.
        msg = attrs.pop("help", None)
        assert msg is not None, "'help' is required."
        attrs.setdefault("help", " ".join([prepend_msg, msg, append_msg]))
        attrs.setdefault("expose_value", False)
        attrs.setdefault("callback", obsolete_callback)
        return factory.option(*param_decls, **attrs)(f)

    return decorator
Make a new ``@click.option`` decorator that provides backwards compatibility.

This decorator extends the default ``@click.option`` decorator by adding three
additional parameters:

* ``equivalent``: a tuple of (new_option, example_usage) that specifies
the new option and its example usage that replaces the given option.
* ``append_msg``: a string to append a help message to the original help message.
src/bentoml_cli/containerize.py