diff --git a/homeassistant/helpers/deprecation.py b/homeassistant/helpers/deprecation.py index 5a0682fdda2..20dbacde480 100644 --- a/homeassistant/helpers/deprecation.py +++ b/homeassistant/helpers/deprecation.py @@ -97,7 +97,7 @@ def get_deprecated( def deprecated_class( - replacement: str, + replacement: str, *, breaks_in_ha_version: str | None = None ) -> Callable[[Callable[_P, _R]], Callable[_P, _R]]: """Mark class as deprecated and provide a replacement class to be used instead. @@ -111,7 +111,9 @@ def deprecated_class( @functools.wraps(cls) def deprecated_cls(*args: _P.args, **kwargs: _P.kwargs) -> _R: """Wrap for the original class.""" - _print_deprecation_warning(cls, replacement, "class", "instantiated") + _print_deprecation_warning( + cls, replacement, "class", "instantiated", breaks_in_ha_version + ) return cls(*args, **kwargs) return deprecated_cls @@ -120,7 +122,7 @@ def deprecated_class( def deprecated_function( - replacement: str, + replacement: str, *, breaks_in_ha_version: str | None = None ) -> Callable[[Callable[_P, _R]], Callable[_P, _R]]: """Mark function as deprecated and provide a replacement to be used instead. @@ -134,7 +136,9 @@ def deprecated_function( @functools.wraps(func) def deprecated_func(*args: _P.args, **kwargs: _P.kwargs) -> _R: """Wrap for the original function.""" - _print_deprecation_warning(func, replacement, "function", "called") + _print_deprecation_warning( + func, replacement, "function", "called", breaks_in_ha_version + ) return func(*args, **kwargs) return deprecated_func @@ -147,15 +151,21 @@ def _print_deprecation_warning( replacement: str, description: str, verb: str, + breaks_in_ha_version: str | None, ) -> None: logger = logging.getLogger(obj.__module__) + if breaks_in_ha_version: + breaks_in = f" which will be removed in HA Core {breaks_in_ha_version}" + else: + breaks_in = "" try: integration_frame = get_integration_frame() except MissingIntegrationFrame: logger.warning( - "%s is a deprecated %s. Use %s instead", + "%s is a deprecated %s%s. Use %s instead", obj.__name__, description, + breaks_in, replacement, ) else: @@ -170,22 +180,24 @@ def _print_deprecation_warning( ) logger.warning( ( - "%s was %s from %s, this is a deprecated %s. Use %s instead," + "%s was %s from %s, this is a deprecated %s%s. Use %s instead," " please %s" ), obj.__name__, verb, integration_frame.integration, description, + breaks_in, replacement, report_issue, ) else: logger.warning( - "%s was %s from %s, this is a deprecated %s. Use %s instead", + "%s was %s from %s, this is a deprecated %s%s. Use %s instead", obj.__name__, verb, integration_frame.integration, description, + breaks_in, replacement, ) diff --git a/homeassistant/util/yaml/loader.py b/homeassistant/util/yaml/loader.py index 275a51cd760..0d06ddfb757 100644 --- a/homeassistant/util/yaml/loader.py +++ b/homeassistant/util/yaml/loader.py @@ -137,7 +137,7 @@ class FastSafeLoader(FastestAvailableSafeLoader, _LoaderMixin): self.secrets = secrets -@deprecated_class("FastSafeLoader") +@deprecated_class("FastSafeLoader", breaks_in_ha_version="2024.6") class SafeLoader(FastSafeLoader): """Provided for backwards compatibility. Logs when instantiated.""" @@ -151,7 +151,7 @@ class PythonSafeLoader(yaml.SafeLoader, _LoaderMixin): self.secrets = secrets -@deprecated_class("PythonSafeLoader") +@deprecated_class("PythonSafeLoader", breaks_in_ha_version="2024.6") class SafeLineLoader(PythonSafeLoader): """Provided for backwards compatibility. Logs when instantiated.""" diff --git a/tests/helpers/test_deprecation.py b/tests/helpers/test_deprecation.py index 1216bd6e293..46716263d5b 100644 --- a/tests/helpers/test_deprecation.py +++ b/tests/helpers/test_deprecation.py @@ -119,32 +119,52 @@ def test_deprecated_class(mock_get_logger) -> None: assert len(mock_logger.warning.mock_calls) == 1 -def test_deprecated_function(caplog: pytest.LogCaptureFixture) -> None: +@pytest.mark.parametrize( + ("breaks_in_ha_version", "extra_msg"), + [ + (None, ""), + ("2099.1", " which will be removed in HA Core 2099.1"), + ], +) +def test_deprecated_function( + caplog: pytest.LogCaptureFixture, + breaks_in_ha_version: str | None, + extra_msg: str, +) -> None: """Test deprecated_function decorator. This tests the behavior when the calling integration is not known. """ - @deprecated_function("new_function") + @deprecated_function("new_function", breaks_in_ha_version=breaks_in_ha_version) def mock_deprecated_function(): pass mock_deprecated_function() assert ( - "mock_deprecated_function is a deprecated function. Use new_function instead" - in caplog.text - ) + f"mock_deprecated_function is a deprecated function{extra_msg}. " + "Use new_function instead" + ) in caplog.text +@pytest.mark.parametrize( + ("breaks_in_ha_version", "extra_msg"), + [ + (None, ""), + ("2099.1", " which will be removed in HA Core 2099.1"), + ], +) def test_deprecated_function_called_from_built_in_integration( caplog: pytest.LogCaptureFixture, + breaks_in_ha_version: str | None, + extra_msg: str, ) -> None: """Test deprecated_function decorator. This tests the behavior when the calling integration is built-in. """ - @deprecated_function("new_function") + @deprecated_function("new_function", breaks_in_ha_version=breaks_in_ha_version) def mock_deprecated_function(): pass @@ -170,14 +190,24 @@ def test_deprecated_function_called_from_built_in_integration( ): mock_deprecated_function() assert ( - "mock_deprecated_function was called from hue, this is a deprecated function. " - "Use new_function instead" in caplog.text - ) + "mock_deprecated_function was called from hue, " + f"this is a deprecated function{extra_msg}. " + "Use new_function instead" + ) in caplog.text +@pytest.mark.parametrize( + ("breaks_in_ha_version", "extra_msg"), + [ + (None, ""), + ("2099.1", " which will be removed in HA Core 2099.1"), + ], +) def test_deprecated_function_called_from_custom_integration( hass: HomeAssistant, caplog: pytest.LogCaptureFixture, + breaks_in_ha_version: str | None, + extra_msg: str, ) -> None: """Test deprecated_function decorator. @@ -186,7 +216,7 @@ def test_deprecated_function_called_from_custom_integration( mock_integration(hass, MockModule("hue"), built_in=False) - @deprecated_function("new_function") + @deprecated_function("new_function", breaks_in_ha_version=breaks_in_ha_version) def mock_deprecated_function(): pass @@ -212,7 +242,8 @@ def test_deprecated_function_called_from_custom_integration( ): mock_deprecated_function() assert ( - "mock_deprecated_function was called from hue, this is a deprecated function. " - "Use new_function instead, please report it to the author of the 'hue' custom " - "integration" in caplog.text - ) + "mock_deprecated_function was called from hue, " + f"this is a deprecated function{extra_msg}. " + "Use new_function instead, please report it to the author of the " + "'hue' custom integration" + ) in caplog.text diff --git a/tests/util/yaml/test_init.py b/tests/util/yaml/test_init.py index 6f6f48813ce..a96d08933ee 100644 --- a/tests/util/yaml/test_init.py +++ b/tests/util/yaml/test_init.py @@ -637,7 +637,7 @@ async def test_deprecated_loaders( loader_class() assert ( f"{loader_class.__name__} was instantiated from hue, this is a deprecated " - f"class. Use {new_class} instead" + f"class which will be removed in HA Core 2024.6. Use {new_class} instead" ) in caplog.text