_deprecation.py 2.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  1. """Utility for deprecating functions."""
  2. import functools
  3. import textwrap
  4. import warnings
  5. def deprecated(since: str, removed_in: str, instructions: str):
  6. """Marks functions as deprecated.
  7. It will result in a warning when the function is called and a note in the
  8. docstring.
  9. Args:
  10. since: The version when the function was first deprecated.
  11. removed_in: The version when the function will be removed.
  12. instructions: The action users should take.
  13. """
  14. def decorator(function):
  15. @functools.wraps(function)
  16. def wrapper(*args, **kwargs):
  17. warnings.warn(
  18. f"'{function.__module__}.{function.__name__}' "
  19. f"is deprecated in version {since} and will be "
  20. f"removed in {removed_in}. Please {instructions}.",
  21. category=FutureWarning,
  22. stacklevel=2,
  23. )
  24. return function(*args, **kwargs)
  25. # Add a deprecation note to the docstring.
  26. docstring = function.__doc__ or ""
  27. # Add a note to the docstring.
  28. deprecation_note = textwrap.dedent(
  29. f"""\
  30. .. deprecated:: {since}
  31. Deprecated and will be removed in version {removed_in}.
  32. Please {instructions}.
  33. """
  34. )
  35. # Split docstring at first occurrence of newline
  36. summary_and_body = docstring.split("\n\n", 1)
  37. if len(summary_and_body) > 1:
  38. summary, body = summary_and_body
  39. # Dedent the body. We cannot do this with the presence of the summary because
  40. # the body contains leading whitespaces when the summary does not.
  41. body = textwrap.dedent(body)
  42. new_docstring_parts = [deprecation_note, "\n\n", summary, body]
  43. else:
  44. summary = summary_and_body[0]
  45. new_docstring_parts = [deprecation_note, "\n\n", summary]
  46. wrapper.__doc__ = "".join(new_docstring_parts)
  47. return wrapper
  48. return decorator