Source code for iris._deprecation

# Copyright Iris contributors
#
# This file is part of Iris and is released under the BSD license.
# See LICENSE in the root of the repository for full licensing details.
"""Utilities for producing runtime deprecation messages."""

from functools import wraps
import inspect
import warnings

from iris.warnings import IrisUserWarning


def explicit_copy_checker(f):
    """Check for explicitly set parameters in a function.

    This is intended to be used as a decorator for functions that take a
    variable number of parameters, to allow the function to determine which
    parameters were explicitly set by the caller.

    This can be helpful when wanting raise DeprecationWarning of function
    parameters, but only when they are explicitly set by the caller, and not
    when they are left at their default value.

    Parameters
    ----------
    f : function
        The function to be decorated. The function must have a signature that
        allows for variable parameters (e.g. ``*args`` and/or ``**kwargs``), and
        the parameters to be checked must be explicitly listed in the function
        signature (i.e. not just passed via ``**kwargs``).

    Returns
    -------
    function
        The decorated function, which will have an additional keyword argument
        ``explicit_params`` added to its signature. This argument will be a set
        of the names of the parameters that were explicitly set by the caller when
        calling the function.

    Examples
    --------
    The following example shows how to use the ``explicit_copy_checker`` decorator to
    check for explicitly set parameters in a function, and raise a DeprecationWarning
    if a deprecated parameter is explicitly set by the caller.

    >>> from iris._deprecation import explicit_copy_checker, IrisDeprecation
    >>> @explicit_copy_checker
    ... def my_function(a, b=1):
    ...     print(f"a={a}, b={b}")
    ...     if "b" in kwargs["explicit_params"]:
    ...         warnings.warn("Parameter 'b' is deprecated.", IrisDeprecation)
    >>> my_function(1)  # No warning, 'b' is not explicitly set
    >>> my_function(1, b=3)  # Warning, 'b' is explicitly set

    """
    varnames = inspect.getfullargspec(f)[0]

    @wraps(f)
    def wrapper(*a, **kw):
        explicit_params = set(list(varnames[: len(a)]) + list(kw.keys()))
        if "copy" in explicit_params:
            if kw["copy"] is False:
                msg = (
                    "Pandas v3 behaviour defaults to copy=True. The `copy`"
                    f" parameter in `{f.__name__}` is deprecated and"
                    "will be removed in a future release."
                )
                warnings.warn(msg, category=IrisUserWarning)
            else:
                msg = (
                    f"The `copy` parameter in `{f.__name__}` is deprecated and"
                    " will be removed in a future release. The function will"
                    " always make a copy of the data array, to ensure that the"
                    " returned Cubes are independent of the input pandas data."
                )
                warn_deprecated(msg)
        else:
            return f(*a, **kw)

    return wrapper


[docs] class IrisDeprecation(UserWarning): """An Iris deprecation warning. Note this subclasses UserWarning for backwards compatibility with Iris' original deprecation warnings. Should subclass DeprecationWarning at the next major release. """ pass
def warn_deprecated(msg, stacklevel=2): """Issue an Iris deprecation warning. Calls :func:`warnings.warn', to emit the message 'msg' as a :class:`warnings.warning`, of the subclass :class:`IrisDeprecationWarning`. The 'stacklevel' keyword is passed through to warnings.warn. However by default this is set to 2, which ensures that the identified code line is in the caller, rather than in this routine. See :mod:`warnings` module documentation. For example:: >>> from iris._deprecation import warn_deprecated >>> def arrgh(): ... warn_deprecated('"arrgh" is deprecated since version 3.5.') ... return 1 ... >>> arrgh() __main__:2: IrisDeprecation: "arrgh" is deprecated since version 3.5. 1 >>> arrgh() 1 >>> """ warnings.warn(msg, category=IrisDeprecation, stacklevel=stacklevel) # A Mixin for a wrapper class that copies the docstring of the wrapped class # into the wrapper. # This is useful in producing wrapper classes that need to mimic the original # but emit deprecation warnings when used. class ClassWrapperSameDocstring(type): def __new__(metacls, classname, bases, class_dict): # Patch the subclass to duplicate the class docstring from the wrapped # class, and give it a special '__new__' that issues a deprecation # warning when creating an instance. parent_class = bases[0] # Copy the original class docstring. class_dict["__doc__"] = parent_class.__doc__ # Return the result. return super().__new__(metacls, classname, bases, class_dict)