text: from __future__ import annotations
  
  from functools import wraps
  import inspect
  from textwrap import dedent
  from typing import (
      Any,
      Callable,
      Mapping,
      cast,
  )
  import warnings
  
  from pandas._libs.properties import cache_readonly  # noqa
  from pandas._typing import F
  
  
  def deprecate(
      name: str,
      alternative: Callable[..., Any],
      version: str,
      alt_name: str | None = None,
      klass: type[Warning] | None = None,
      stacklevel: int = 2,
      msg: str | None = None,
  ) -> Callable[[F], F]:
      """
      Return a new function that emits a deprecation warning on use.
  
      To use this method for a deprecated function, another function
      `alternative` with the same signature must exist. The deprecated
      function will emit a deprecation warning, and in the docstring
      it will contain the deprecation directive with the provided version
      so it can be detected for future removal.
  
      Parameters
      ----------
      name : str
          Name of function to deprecate.
      alternative : func
          Function to use instead.
      version : str
          Version of pandas in which the method has been deprecated.
      alt_name : str, optional
          Name to use in preference of alternative.__name__.
      klass : Warning, default FutureWarning
      stacklevel : int, default 2
      msg : str
          The message to display in the warning.
          Default is '{name} is deprecated. Use {alt_name} instead.'
      """
      alt_name = alt_name or alternative.__name__
      klass = klass or FutureWarning
      warning_msg = msg or f"{name} is deprecated, use {alt_name} instead"
  
      @wraps(alternative)
      def wrapper(*args, **kwargs) -> Callable[..., Any]:
          warnings.warn(warning_msg, klass, stacklevel=stacklevel)
          return alternative(*args, **kwargs)
  
      # adding deprecated directive to the docstring
      msg = msg or f"Use `{alt_name}` instead."
      doc_error_msg = (
          "deprecate needs a correctly formatted docstring in "
          "the target function (should have a one liner short "
          "summary, and opening quotes should be in their own "
          f"line). Found:\n{alternative.__doc__}"
      )
  
      # when python is running in optimized mode (i.e. `-OO`), docstrings are
      # removed, so we check that a docstring with correct formatting is used
      # but we allow empty docstrings
      if alternative.__doc__:
          if alternative.__doc__.count("\n") < 3:
              raise AssertionError(doc_error_msg)
          empty1, summary, empty2, doc = alternative.__doc__.split("\n", 3)
          if empty1 or empty2 and not summary:
              raise AssertionError(doc_error_msg)
          wrapper.__doc__ = dedent(
              f"""
          {summary.strip()}
  
          .. deprecated:: {version}
              {msg}
  
          {dedent(doc)}"""
          )
      # error: Incompatible return value type (got "Callable[[VarArg(Any), KwArg(Any)],
      # Callable[...,Any]]", expected "Callable[[F], F]")
      return wrapper  # type: ignore[return-value]
  
  
  def deprecate_kwarg(
      old_arg_name: str,
      new_arg_name: str | None,
      mapping: Mapping[Any, Any] | Callable[[Any], Any] | None = None,
      stacklevel: int = 2,
  ) -> Callable[[F], F]:
      """
      Decorator to deprecate a keyword argument of a function.
  
      Parameters
      ----------
      old_arg_name : str
          Name of argument in function to deprecate
      new_arg_name : str or None
          Name of preferred argument in function. Use None to raise warning that
          ``old_arg_name`` keyword is deprecated.
      mapping : dict or callable
          If mapping is present, use it to translate old arguments to
          new arguments. A callable must do its own value checking;
          values not found in a dict will be forwarded unchanged.
  
      Examples
      --------
      The following deprecates 'cols', using 'columns' instead
  
      >>> @deprecate_kwarg(old_arg_name='cols', new_arg_name='columns')
      ... def f(columns=''):
      ...     print(columns)
      ...
      >>> f(columns='should work ok')
      should work ok
  
      >>> f(cols='should raise warning')
      FutureWarning: cols is deprecated, use columns instead
        warnings.warn(msg, FutureWarning)
      should raise warning
  
      >>> f(cols='should error', columns="can\'t pass do both")
      TypeError: Can only specify 'cols' or 'columns', not both
  
      >>> @deprecate_kwarg('old', 'new', {'yes': True, 'no': False})
      ... def f(new=False):
      ...     print('yes!' if new else 'no!')
      ...
      >>> f(old='yes')
      FutureWarning: old='yes' is deprecated, use new=True instead
        warnings.warn(msg, FutureWarning)
      yes!
  
      To raise a warning that a keyword will be removed entirely in the future
  
      >>> @deprecate_kwarg(old_arg_name='cols', new_arg_name=None)
      ... def f(cols='', another_param=''):
      ...     print(cols)
      ...
      >>> f(cols='should raise warning')
      FutureWarning: the 'cols' keyword is deprecated and will be removed in a
      future version please takes steps to stop use of 'cols'
      should raise warning
      >>> f(another_param='should not raise warning')
      should not raise warning
  
      >>> f(cols='should raise warning', another_param='')
      FutureWarning: the 'cols' keyword is deprecated and will be removed in a
      future version please takes steps to stop use of 'cols'
      should raise warning
      """
      if mapping is not None and not hasattr(mapping, "get") and not callable(mapping):
          raise TypeError(
              "mapping from old to new argument values must be dict or callable!"
          )
  
      def _deprecate_kwarg(func: F) -> F:
          @wraps(func)
          def wrapper(*args, **kwargs) -> Callable[..., Any]:
              old_arg_value = kwargs.pop(old_arg_name, None)
  
              if old_arg_value is not None:
                  if new_arg_name is None:
                      msg = (
                          f"the {repr(old_arg_name)} keyword is deprecated and "
                          "will be removed in a future version. Please take "
                          f"steps to stop the use of {repr(old_arg_name)}"
                      )
                      warnings.warn(msg, FutureWarning, stacklevel=stacklevel)
                      kwargs[old_arg_name] = old_arg_value
                      return func(*args, **kwargs)
  
                  elif mapping is not None:
                      if callable(mapping):
                          new_arg_value = mapping(old_arg_value)
                      else:
                          new_arg_value = mapping.get(old_arg_value, old_arg_value)
                      msg = (
                          f"the {old_arg_name}={repr(old_arg_value)} keyword is "
                          "deprecated, use "
                          f"{new_arg_name}={repr(new_arg_value)} instead"
                      )
                  else:
                      new_arg_value = old_arg_value
                      msg = (
                          f"the {repr(old_arg_name)}' keyword is deprecated, "
                          f"use {repr(new_arg_name)} instead"
                      )
  
                  warnings.warn(msg, FutureWarning, stacklevel=stacklevel)
                  if kwargs.get(new_arg_name) is not None:
                      msg = (
                          f"Can only specify {repr(old_arg_name)} "
                          f"or {repr(new_arg_name)}, not both"
                      )
                      raise TypeError(msg)
                  else:
                      kwargs[new_arg_name] = new_arg_value
              return func(*args, **kwargs)
  
          return cast(F, wrapper)
  
      return _deprecate_kwarg
  
  
  def _format_argument_list(allow_args: list[str] | int):
      """
      Convert the allow_args argument (either string or integer) of
      `deprecate_nonkeyword_arguments` function to a string describing
      it to be inserted into warning message.
  
      Parameters
      ----------
      allowed_args : list, tuple or int
          The `allowed_args` argument for `deprecate_nonkeyword_arguments`,
          but None value is not allowed.
  
      Returns
      -------
      s : str
          The substring describing the argument list in best way to be
          inserted to the warning message.
  
      Examples
      --------
      `format_argument_list(0)` -> ''
      `format_argument_list(1)` -> 'except for the first argument'
      `format_argument_list(2)` -> 'except for the first 2 arguments'
      `format_argument_list([])` -> ''
      `format_argument_list(['a'])` -> "except for the arguments 'a'"
      `format_argument_list(['a', 'b'])` -> "except for the arguments 'a' and 'b'"
      `format_argument_list(['a', 'b', 'c'])` ->
          "except for the arguments 'a', 'b' and 'c'"
      """
      if not allow_args:
          return ""
      elif allow_args == 1:
          return " except for the first argument"
      elif isinstance(allow_args, int):
          return f" except for the first {allow_args} arguments"
      elif len(allow_args) == 1:
          return f" except for the argument '{allow_args[0]}'"
      else:
          last = allow_args[-1]
          args = ", ".join(["'" + x + "'" for x in allow_args[:-1]])
          return f" except for the arguments {args} and '{last}'"
  
  
  def deprecate_nonkeyword_arguments(
      version: str,
      allowed_args: list[str] | int | None = None,
      stacklevel: int = 2,
  ) -> Callable:
      """
      Decorator to deprecate a use of non-keyword arguments of a function.
  
      Parameters
      ----------
      version : str, optional
          The version in which positional arguments will become
          keyword-only.
  
      allowed_args : list or int, optional
          In case of list, it must be the list of names of some
          first arguments of the decorated functions that are
          OK to be given as positional arguments. In case of an
          integer, this is the number of positional arguments
          that will stay positional. In case of None value,
          defaults to list of all arguments not having the
          default value.
  
      stacklevel : int, default=2
          The stack level for warnings.warn
      """
  
      def decorate(func):
          if allowed_args is not None:
              allow_args = allowed_args
          else:
              spec = inspect.getfullargspec(func)
  
              # We must have some defaults if we are deprecating default-less
              assert spec.defaults is not None  # for mypy
              allow_args = spec.args[: -len(spec.defaults)]
  
          @wraps(func)
          def wrapper(*args, **kwargs):
              arguments = _format_argument_list(allow_args)
              if isinstance(allow_args, (list, tuple)):
                  num_allow_args = len(allow_args)
              else:
                  num_allow_args = allow_args
              if len(args) > num_allow_args:
                  msg = (
                      f"Starting with Pandas version {version} all arguments of "
                      f"{func.__name__}{arguments} will be keyword-only"
                  )
                  warnings.warn(msg, FutureWarning, stacklevel=stacklevel)
              return func(*args, **kwargs)
  
          return wrapper
  
      return decorate
  
  
  def rewrite_axis_style_signature(
      name: str, extra_params: list[tuple[str, Any]]
  ) -> Callable[..., Any]:
      def decorate(func: F) -> F:
          @wraps(func)
          def wrapper(*args, **kwargs) -> Callable[..., Any]:
              return func(*args, **kwargs)
  
          kind = inspect.Parameter.POSITIONAL_OR_KEYWORD
          params = [
              inspect.Parameter("self", kind),
              inspect.Parameter(name, kind, default=None),
              inspect.Parameter("index", kind, default=None),
              inspect.Parameter("columns", kind, default=None),
              inspect.Parameter("axis", kind, default=None),
          ]
  
          for pname, default in extra_params:
              params.append(inspect.Parameter(pname, kind, default=default))
  
          sig = inspect.Signature(params)
  
          # https://github.com/python/typing/issues/598
          # error: "F" has no attribute "__signature__"
          func.__signature__ = sig  # type: ignore[attr-defined]
          return cast(F, wrapper)
  
      return decorate
  
  
  def doc(*docstrings: str | Callable, **params) -> Callable[[F], F]:
      """
      A decorator take docstring templates, concatenate them and perform string
      substitution on it.
  
      This decorator will add a variable "_docstring_components" to the wrapped
      callable to keep track the original docstring template for potential usage.
      If it should be consider as a template, it will be saved as a string.
      Otherwise, it will be saved as callable, and later user __doc__ and dedent
      to get docstring.
  
      Parameters
      ----------
      *docstrings : str or callable
          The string / docstring / docstring template to be appended in order
          after default docstring under callable.
      **params
          The string which would be used to format docstring template.
      """
  
      def decorator(decorated: F) -> F:
          # collecting docstring and docstring templates
          docstring_components: list[str | Callable] = []
          if decorated.__doc__:
              docstring_components.append(dedent(decorated.__doc__))
  
          for docstring in docstrings:
              if hasattr(docstring, "_docstring_components"):
                  # error: Item "str" of "Union[str, Callable[..., Any]]" has no attribute
                  # "_docstring_components"
                  # error: Item "function" of "Union[str, Callable[..., Any]]" has no
                  # attribute "_docstring_components"
                  docstring_components.extend(
                      docstring._docstring_components  # type: ignore[union-attr]
                  )
              elif isinstance(docstring, str) or docstring.__doc__:
                  docstring_components.append(docstring)
  
          # formatting templates and concatenating docstring
          decorated.__doc__ = "".join(
              [
                  component.format(**params)
                  if isinstance(component, str)
                  else dedent(component.__doc__ or "")
                  for component in docstring_components
              ]
          )
  
          # error: "F" has no attribute "_docstring_components"
          decorated._docstring_components = (  # type: ignore[attr-defined]
              docstring_components
          )
          return decorated
  
      return decorator
  
  
  # Substitution and Appender are derived from matplotlib.docstring (1.1.0)
  # module https://matplotlib.org/users/license.html
  
  
  class Substitution:
      """
      A decorator to take a function's docstring and perform string
      substitution on it.
  
      This decorator should be robust even if func.__doc__ is None
      (for example, if -OO was passed to the interpreter)
  
      Usage: construct a docstring.Substitution with a sequence or
      dictionary suitable for performing substitution; then
      decorate a suitable function with the constructed object. e.g.
  
      sub_author_name = Substitution(author='Jason')
  
      @sub_author_name
      def some_function(x):
          "%(author)s wrote this function"
  
      # note that some_function.__doc__ is now "Jason wrote this function"
  
      One can also use positional arguments.
  
      sub_first_last_names = Substitution('Edgar Allen', 'Poe')
  
      @sub_first_last_names
      def some_function(x):
          "%s %s wrote the Raven"
      """
  
      def __init__(self, *args, **kwargs):
          if args and kwargs:
              raise AssertionError("Only positional or keyword args are allowed")
  
          self.params = args or kwargs
  
      def __call__(self, func: F) -> F:
          func.__doc__ = func.__doc__ and func.__doc__ % self.params
          return func
  
      def update(self, *args, **kwargs) -> None:
          """
          Update self.params with supplied args.
          """
          if isinstance(self.params, dict):
              self.params.update(*args, **kwargs)
  
  
  class Appender:
      """
      A function decorator that will append an addendum to the docstring
      of the target function.
  
      This decorator should be robust even if func.__doc__ is None
      (for example, if -OO was passed to the interpreter).
  
      Usage: construct a docstring.Appender with a string to be joined to
      the original docstring. An optional 'join' parameter may be supplied
      which will be used to join the docstring and addendum. e.g.
  
      add_copyright = Appender("Copyright (c) 2009", join='\n')
  
      @add_copyright
      def my_dog(has='fleas'):
          "This docstring will have a copyright below"
          pass
      """
  
      addendum: str | None
  
      def __init__(self, addendum: str | None, join: str = "", indents: int = 0):
          if indents > 0:
              self.addendum = indent(addendum, indents=indents)
          else:
              self.addendum = addendum
          self.join = join
  
      def __call__(self, func: F) -> F:
          func.__doc__ = func.__doc__ if func.__doc__ else ""
          self.addendum = self.addendum if self.addendum else ""
          docitems = [func.__doc__, self.addendum]
          func.__doc__ = dedent(self.join.join(docitems))
          return func
  
  
  def indent(text: str | None, indents: int = 1) -> str:
      if not text or not isinstance(text, str):
          return ""
      jointext = "".join(["\n"] + ["    "] * indents)
      return jointext.join(text.split("\n"))
  