code: 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", True)
          attrs.setdefault("callback", obsolete_callback)
          return factory.option(*param_decls, **attrs)(f)
  
      return decorator
  
file_path: BentoML/src/bentoml_cli/containerize.py
imports: from __future__ import annotations
  import itertools
  import logging
  import shutil
  import subprocess
  import sys
  import tempfile
  import typing
  from functools import partial
label_desc: Use image node_id 1 for calling a global function with example usage: compatible_option() and returns: decorator, factory