"""Utility for deprecating functions.""" import functools import textwrap import warnings def deprecated(since: str, removed_in: str, instructions: str): """Marks functions as deprecated. It will result in a warning when the function is called and a note in the docstring. Args: since: The version when the function was first deprecated. removed_in: The version when the function will be removed. instructions: The action users should take. """ def decorator(function): @functools.wraps(function) def wrapper(*args, **kwargs): warnings.warn( f"'{function.__module__}.{function.__name__}' " f"is deprecated in version {since} and will be " f"removed in {removed_in}. Please {instructions}.", category=FutureWarning, stacklevel=2, ) return function(*args, **kwargs) # Add a deprecation note to the docstring. docstring = function.__doc__ or "" # Add a note to the docstring. deprecation_note = textwrap.dedent( f"""\ .. deprecated:: {since} Deprecated and will be removed in version {removed_in}. Please {instructions}. """ ) # Split docstring at first occurrence of newline summary_and_body = docstring.split("\n\n", 1) if len(summary_and_body) > 1: summary, body = summary_and_body # Dedent the body. We cannot do this with the presence of the summary because # the body contains leading whitespaces when the summary does not. body = textwrap.dedent(body) new_docstring_parts = [deprecation_note, "\n\n", summary, body] else: summary = summary_and_body[0] new_docstring_parts = [deprecation_note, "\n\n", summary] wrapper.__doc__ = "".join(new_docstring_parts) return wrapper return decorator