deprecation.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. import inspect
  2. import logging
  3. from typing import Optional, Union
  4. from ray.util import log_once
  5. logger = logging.getLogger(__name__)
  6. # A constant to use for any configuration that should be deprecated
  7. # (to check, whether this config has actually been assigned a proper value or
  8. # not).
  9. DEPRECATED_VALUE = -1
  10. def deprecation_warning(
  11. old: str,
  12. new: Optional[str] = None,
  13. *,
  14. help: Optional[str] = None,
  15. error: Optional[Union[bool, Exception]] = None) -> None:
  16. """Warns (via the `logger` object) or throws a deprecation warning/error.
  17. Args:
  18. old (str): A description of the "thing" that is to be deprecated.
  19. new (Optional[str]): A description of the new "thing" that replaces it.
  20. help (Optional[str]): An optional help text to tell the user, what to
  21. do instead of using `old`.
  22. error (Optional[Union[bool, Exception]]): Whether or which exception to
  23. raise. If True, raise ValueError. If False, just warn.
  24. If error is-a subclass of Exception, raise that Exception.
  25. Raises:
  26. ValueError: If `error=True`.
  27. Exception: Of type `error`, iff error is-a Exception subclass.
  28. """
  29. msg = "`{}` has been deprecated.{}".format(
  30. old, (" Use `{}` instead.".format(new) if new else f" {help}"
  31. if help else ""))
  32. if error is True:
  33. raise ValueError(msg)
  34. elif error and issubclass(error, Exception):
  35. raise error(msg)
  36. else:
  37. logger.warning("DeprecationWarning: " + msg +
  38. " This will raise an error in the future!")
  39. def Deprecated(old=None, *, new=None, help=None, error):
  40. """Annotation for documenting a (soon-to-be) deprecated method.
  41. Methods tagged with this decorator should produce a
  42. `ray.rllib.utils.deprecation.deprecation_warning(old=..., error=False)`
  43. to not break existing code at this point.
  44. In a next major release, this warning can then be made an error
  45. (error=True), which means at this point that the method is already
  46. no longer supported but will still inform the user about the
  47. deprecation event.
  48. In a further major release, the method should be erased.
  49. """
  50. def _inner(obj):
  51. # A deprecated class.
  52. if inspect.isclass(obj):
  53. # Patch the class' init method to raise the warning/error.
  54. obj_init = obj.__init__
  55. def patched_init(*args, **kwargs):
  56. if log_once(old or obj.__name__):
  57. deprecation_warning(
  58. old=old or obj.__name__,
  59. new=new,
  60. help=help,
  61. error=error,
  62. )
  63. return obj_init(*args, **kwargs)
  64. obj.__init__ = patched_init
  65. # Return the patched class (with the warning/error when
  66. # instantiated).
  67. return obj
  68. # A deprecated class method or function.
  69. # Patch with the warning/error at the beginning.
  70. def _ctor(*args, **kwargs):
  71. if log_once(old or obj.__name__):
  72. deprecation_warning(
  73. old=old or obj.__name__,
  74. new=new,
  75. help=help,
  76. error=error,
  77. )
  78. # Call the deprecated method/function.
  79. return obj(*args, **kwargs)
  80. # Return the patched class method/function.
  81. return _ctor
  82. # Return the prepared decorator.
  83. return _inner