Remove log flooding prevention logic from discovery info (#61243)

Co-authored-by: epenet <epenet@users.noreply.github.com>
This commit is contained in:
epenet 2021-12-08 21:28:26 +01:00 committed by GitHub
parent 9c11bb8ba1
commit c05eca1c82
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 163 additions and 159 deletions

View file

@ -68,22 +68,17 @@ class DhcpServiceInfo(BaseServiceInfo):
hostname: str hostname: str
macaddress: str macaddress: str
# Used to prevent log flooding. To be removed in 2022.6
_warning_logged: bool = False
def __getitem__(self, name: str) -> Any: def __getitem__(self, name: str) -> Any:
""" """
Enable method for compatibility reason. Enable method for compatibility reason.
Deprecated, and will be removed in version 2022.6. Deprecated, and will be removed in version 2022.6.
""" """
if not self._warning_logged: report(
report( f"accessed discovery_info['{name}'] instead of discovery_info.{name}; this will fail in version 2022.6",
f"accessed discovery_info['{name}'] instead of discovery_info.{name}; this will fail in version 2022.6", exclude_integrations={DOMAIN},
exclude_integrations={DOMAIN}, error_if_core=False,
error_if_core=False, )
)
self._warning_logged = True
return getattr(self, name) return getattr(self, name)
def get(self, name: str, default: Any = None) -> Any: def get(self, name: str, default: Any = None) -> Any:
@ -92,13 +87,11 @@ class DhcpServiceInfo(BaseServiceInfo):
Deprecated, and will be removed in version 2022.6. Deprecated, and will be removed in version 2022.6.
""" """
if not self._warning_logged: report(
report( f"accessed discovery_info.get('{name}') instead of discovery_info.{name}; this will fail in version 2022.6",
f"accessed discovery_info.get('{name}') instead of discovery_info.{name}; this will fail in version 2022.6", exclude_integrations={DOMAIN},
exclude_integrations={DOMAIN}, error_if_core=False,
error_if_core=False, )
)
self._warning_logged = True
if hasattr(self, name): if hasattr(self, name):
return getattr(self, name) return getattr(self, name)
return default return default

View file

@ -261,22 +261,17 @@ class MqttServiceInfo(BaseServiceInfo):
subscribed_topic: str subscribed_topic: str
timestamp: dt.datetime timestamp: dt.datetime
# Used to prevent log flooding. To be removed in 2022.6
_warning_logged: bool = False
def __getitem__(self, name: str) -> Any: def __getitem__(self, name: str) -> Any:
""" """
Allow property access by name for compatibility reason. Allow property access by name for compatibility reason.
Deprecated, and will be removed in version 2022.6. Deprecated, and will be removed in version 2022.6.
""" """
if not self._warning_logged: report(
report( f"accessed discovery_info['{name}'] instead of discovery_info.{name}; this will fail in version 2022.6",
f"accessed discovery_info['{name}'] instead of discovery_info.{name}; this will fail in version 2022.6", exclude_integrations={DOMAIN},
exclude_integrations={"mqtt"}, error_if_core=False,
error_if_core=False, )
)
self._warning_logged = True
return getattr(self, name) return getattr(self, name)

View file

@ -103,22 +103,17 @@ class SsdpServiceInfo(
): ):
"""Prepared info from ssdp/upnp entries.""" """Prepared info from ssdp/upnp entries."""
# Used to prevent log flooding. To be removed in 2022.6
_warning_logged: bool = False
def __getitem__(self, name: str) -> Any: def __getitem__(self, name: str) -> Any:
""" """
Allow property access by name for compatibility reason. Allow property access by name for compatibility reason.
Deprecated, and will be removed in version 2022.6. Deprecated, and will be removed in version 2022.6.
""" """
if not self._warning_logged: report(
report( f"accessed discovery_info['{name}'] instead of discovery_info.{name}; this will fail in version 2022.6",
f"accessed discovery_info['{name}'] instead of discovery_info.{name}; this will fail in version 2022.6", exclude_integrations={DOMAIN},
exclude_integrations={DOMAIN}, error_if_core=False,
error_if_core=False, )
)
self._warning_logged = True
# Use a property if it is available, fallback to upnp data # Use a property if it is available, fallback to upnp data
if hasattr(self, name): if hasattr(self, name):
return getattr(self, name) return getattr(self, name)
@ -132,13 +127,11 @@ class SsdpServiceInfo(
Deprecated, and will be removed in version 2022.6. Deprecated, and will be removed in version 2022.6.
""" """
if not self._warning_logged: report(
report( f"accessed discovery_info.get('{name}') instead of discovery_info.{name}; this will fail in version 2022.6",
f"accessed discovery_info.get('{name}') instead of discovery_info.{name}; this will fail in version 2022.6", exclude_integrations={DOMAIN},
exclude_integrations={DOMAIN}, error_if_core=False,
error_if_core=False, )
)
self._warning_logged = True
if hasattr(self, name): if hasattr(self, name):
return getattr(self, name) return getattr(self, name)
return self.upnp.get(name, self.ssdp_headers.get(name, default)) return self.upnp.get(name, self.ssdp_headers.get(name, default))
@ -149,14 +142,12 @@ class SsdpServiceInfo(
Deprecated, and will be removed in version 2022.6. Deprecated, and will be removed in version 2022.6.
""" """
if not self._warning_logged: report(
report( f"accessed discovery_info.__contains__('{name}') instead of discovery_info.upnp.__contains__('{name}') "
"accessed discovery_info.__contains__() instead of discovery_info.upnp.__contains__() " f"or discovery_info.ssdp_headers.__contains__('{name}'); this will fail in version 2022.6",
"or discovery_info.ssdp_headers.__contains__(); this will fail in version 2022.6", exclude_integrations={DOMAIN},
exclude_integrations={DOMAIN}, error_if_core=False,
error_if_core=False, )
)
self._warning_logged = True
if hasattr(self, name): if hasattr(self, name):
return getattr(self, name) is not None return getattr(self, name) is not None
return name in self.upnp or name in self.ssdp_headers return name in self.upnp or name in self.ssdp_headers

View file

@ -44,22 +44,17 @@ class UsbServiceInfo(BaseServiceInfo):
manufacturer: str | None manufacturer: str | None
description: str | None description: str | None
# Used to prevent log flooding. To be removed in 2022.6
_warning_logged: bool = False
def __getitem__(self, name: str) -> Any: def __getitem__(self, name: str) -> Any:
""" """
Allow property access by name for compatibility reason. Allow property access by name for compatibility reason.
Deprecated, and will be removed in version 2022.6. Deprecated, and will be removed in version 2022.6.
""" """
if not self._warning_logged: report(
report( f"accessed discovery_info['{name}'] instead of discovery_info.{name}; this will fail in version 2022.6",
f"accessed discovery_info['{name}'] instead of discovery_info.{name}; this will fail in version 2022.6", exclude_integrations={DOMAIN},
exclude_integrations={"usb"}, error_if_core=False,
error_if_core=False, )
)
self._warning_logged = True
return getattr(self, name) return getattr(self, name)

View file

@ -107,22 +107,17 @@ class ZeroconfServiceInfo(BaseServiceInfo):
name: str name: str
properties: dict[str, Any] properties: dict[str, Any]
# Used to prevent log flooding. To be removed in 2022.6
_warning_logged: bool = False
def __getitem__(self, name: str) -> Any: def __getitem__(self, name: str) -> Any:
""" """
Enable method for compatibility reason. Enable method for compatibility reason.
Deprecated, and will be removed in version 2022.6. Deprecated, and will be removed in version 2022.6.
""" """
if not self._warning_logged: report(
report( f"accessed discovery_info['{name}'] instead of discovery_info.{name}; this will fail in version 2022.6",
f"accessed discovery_info['{name}'] instead of discovery_info.{name}; this will fail in version 2022.6", exclude_integrations={DOMAIN},
exclude_integrations={DOMAIN}, error_if_core=False,
error_if_core=False, )
)
self._warning_logged = True
return getattr(self, name) return getattr(self, name)
def get(self, name: str, default: Any = None) -> Any: def get(self, name: str, default: Any = None) -> Any:
@ -131,13 +126,11 @@ class ZeroconfServiceInfo(BaseServiceInfo):
Deprecated, and will be removed in version 2022.6. Deprecated, and will be removed in version 2022.6.
""" """
if not self._warning_logged: report(
report( f"accessed discovery_info.get('{name}') instead of discovery_info.{name}; this will fail in version 2022.6",
f"accessed discovery_info.get('{name}') instead of discovery_info.{name}; this will fail in version 2022.6", exclude_integrations={DOMAIN},
exclude_integrations={DOMAIN}, error_if_core=False,
error_if_core=False, )
)
self._warning_logged = True
if hasattr(self, name): if hasattr(self, name):
return getattr(self, name) return getattr(self, name)
return default return default

View file

@ -3,7 +3,8 @@ import datetime
import threading import threading
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
from scapy import arch # pylint: unused-import # noqa: F401 import pytest
from scapy import arch # pylint: disable=unused-import # noqa: F401
from scapy.error import Scapy_Exception from scapy.error import Scapy_Exception
from scapy.layers.dhcp import DHCP from scapy.layers.dhcp import DHCP
from scapy.layers.l2 import Ether from scapy.layers.l2 import Ether
@ -845,6 +846,7 @@ async def test_aiodiscover_finds_new_hosts_after_interval(hass):
) )
@pytest.mark.usefixtures("mock_integration_frame")
async def test_service_info_compatibility(hass, caplog): async def test_service_info_compatibility(hass, caplog):
"""Test compatibility with old-style dict. """Test compatibility with old-style dict.
@ -856,21 +858,13 @@ async def test_service_info_compatibility(hass, caplog):
macaddress="b8b7f16db533", macaddress="b8b7f16db533",
) )
# Ensure first call get logged with patch("homeassistant.helpers.frame._REPORTED_INTEGRATIONS", set()):
assert discovery_info["ip"] == "192.168.210.56" assert discovery_info["ip"] == "192.168.210.56"
assert discovery_info.get("ip") == "192.168.210.56" assert "Detected integration that accessed discovery_info['ip']" in caplog.text
with patch("homeassistant.helpers.frame._REPORTED_INTEGRATIONS", set()):
assert discovery_info.get("ip") == "192.168.210.56"
assert "Detected integration that accessed discovery_info.get('ip')" in caplog.text
assert discovery_info.get("ip", "fallback_host") == "192.168.210.56" assert discovery_info.get("ip", "fallback_host") == "192.168.210.56"
assert discovery_info.get("invalid_key", "fallback_host") == "fallback_host" assert discovery_info.get("invalid_key", "fallback_host") == "fallback_host"
assert "Detected code that accessed discovery_info['ip']" in caplog.text
assert "Detected code that accessed discovery_info.get('ip')" not in caplog.text
# Ensure second call doesn't get logged
caplog.clear()
assert discovery_info["ip"] == "192.168.210.56"
assert discovery_info.get("ip") == "192.168.210.56"
assert "Detected code that accessed discovery_info['ip']" not in caplog.text
assert "Detected code that accessed discovery_info.get('ip')" not in caplog.text
discovery_info._warning_logged = False # pylint: disable=[protected-access]
assert discovery_info.get("ip") == "192.168.210.56"
assert "Detected code that accessed discovery_info.get('ip')" in caplog.text

View file

@ -1814,6 +1814,7 @@ async def test_publish_json_from_template(hass, mqtt_mock):
assert mqtt_mock.async_publish.call_args[0][1] == test_str assert mqtt_mock.async_publish.call_args[0][1] == test_str
@pytest.mark.usefixtures("mock_integration_frame")
async def test_service_info_compatibility(hass, caplog): async def test_service_info_compatibility(hass, caplog):
"""Test compatibility with old-style dict. """Test compatibility with old-style dict.
@ -1828,11 +1829,6 @@ async def test_service_info_compatibility(hass, caplog):
timestamp=None, timestamp=None,
) )
# Ensure first call get logged with patch("homeassistant.helpers.frame._REPORTED_INTEGRATIONS", set()):
assert discovery_info["topic"] == "tasmota/discovery/DC4F220848A2/config" assert discovery_info["topic"] == "tasmota/discovery/DC4F220848A2/config"
assert "Detected code that accessed discovery_info['topic']" in caplog.text assert "Detected integration that accessed discovery_info['topic']" in caplog.text
# Ensure second call doesn't get logged
caplog.clear()
assert discovery_info["topic"] == "tasmota/discovery/DC4F220848A2/config"
assert "Detected code that accessed discovery_info['topic']" not in caplog.text

View file

@ -793,3 +793,61 @@ async def test_ipv4_does_additional_search_for_sonos(
), ),
) )
assert ssdp_listener.async_search.call_args[1] == {} assert ssdp_listener.async_search.call_args[1] == {}
@pytest.mark.usefixtures("mock_integration_frame")
async def test_service_info_compatibility(hass, caplog):
"""Test compatibility with old-style dict.
To be removed in 2022.6
"""
discovery_info = ssdp.SsdpServiceInfo(
ssdp_st="mock-st",
ssdp_location="http://1.1.1.1",
ssdp_usn="uuid:mock-udn::mock-st",
ssdp_server="mock-server",
ssdp_ext="",
ssdp_headers=_ssdp_headers(
{
"st": "mock-st",
"location": "http://1.1.1.1",
"usn": "uuid:mock-udn::mock-st",
"server": "mock-server",
"ext": "",
}
),
upnp={ssdp.ATTR_UPNP_DEVICE_TYPE: "ABC"},
)
with patch("homeassistant.helpers.frame._REPORTED_INTEGRATIONS", set()):
assert discovery_info["ssdp_st"] == "mock-st"
assert "Detected integration that accessed discovery_info['ssdp_st']" in caplog.text
with patch("homeassistant.helpers.frame._REPORTED_INTEGRATIONS", set()):
assert discovery_info.get("ssdp_location") == "http://1.1.1.1"
assert (
"Detected integration that accessed discovery_info.get('ssdp_location')"
in caplog.text
)
with patch("homeassistant.helpers.frame._REPORTED_INTEGRATIONS", set()):
assert "ssdp_usn" in discovery_info
assert (
"Detected integration that accessed discovery_info.__contains__('ssdp_usn')"
in caplog.text
)
# Root item
assert discovery_info["ssdp_usn"] == "uuid:mock-udn::mock-st"
assert discovery_info.get("ssdp_usn") == "uuid:mock-udn::mock-st"
assert "ssdp_usn" in discovery_info
# SSDP header
assert discovery_info["st"] == "mock-st"
assert discovery_info.get("st") == "mock-st"
assert "st" in discovery_info
# UPnP item
assert discovery_info[ssdp.ATTR_UPNP_DEVICE_TYPE] == "ABC"
assert discovery_info.get(ssdp.ATTR_UPNP_DEVICE_TYPE) == "ABC"
assert ssdp.ATTR_UPNP_DEVICE_TYPE in discovery_info

View file

@ -779,6 +779,7 @@ def test_human_readable_device_name():
assert "8A2A" in name assert "8A2A" in name
@pytest.mark.usefixtures("mock_integration_frame")
async def test_service_info_compatibility(hass, caplog): async def test_service_info_compatibility(hass, caplog):
"""Test compatibility with old-style dict. """Test compatibility with old-style dict.
@ -794,10 +795,6 @@ async def test_service_info_compatibility(hass, caplog):
) )
# Ensure first call get logged # Ensure first call get logged
assert discovery_info["vid"] == 12345 with patch("homeassistant.helpers.frame._REPORTED_INTEGRATIONS", set()):
assert "Detected code that accessed discovery_info['vid']" in caplog.text assert discovery_info["vid"] == 12345
assert "Detected integration that accessed discovery_info['vid']" in caplog.text
# Ensure second call doesn't get logged
caplog.clear()
assert discovery_info["vid"] == 12345
assert "Detected code that accessed discovery_info['vid']" not in caplog.text

View file

@ -3,6 +3,7 @@ from ipaddress import ip_address
from typing import Any from typing import Any
from unittest.mock import call, patch from unittest.mock import call, patch
import pytest
from zeroconf import InterfaceChoice, IPVersion, ServiceStateChange from zeroconf import InterfaceChoice, IPVersion, ServiceStateChange
from zeroconf.asyncio import AsyncServiceInfo from zeroconf.asyncio import AsyncServiceInfo
@ -1032,6 +1033,7 @@ async def test_no_name(hass, mock_async_zeroconf):
assert info.name == "Home._home-assistant._tcp.local." assert info.name == "Home._home-assistant._tcp.local."
@pytest.mark.usefixtures("mock_integration_frame")
async def test_service_info_compatibility(hass, caplog): async def test_service_info_compatibility(hass, caplog):
"""Test compatibility with old-style dict. """Test compatibility with old-style dict.
@ -1046,21 +1048,15 @@ async def test_service_info_compatibility(hass, caplog):
properties={}, properties={},
) )
# Ensure first call get logged with patch("homeassistant.helpers.frame._REPORTED_INTEGRATIONS", set()):
assert discovery_info["host"] == "mock_host" assert discovery_info["host"] == "mock_host"
assert discovery_info.get("host") == "mock_host" assert "Detected integration that accessed discovery_info['host']" in caplog.text
with patch("homeassistant.helpers.frame._REPORTED_INTEGRATIONS", set()):
assert discovery_info.get("host") == "mock_host"
assert (
"Detected integration that accessed discovery_info.get('host')" in caplog.text
)
assert discovery_info.get("host", "fallback_host") == "mock_host" assert discovery_info.get("host", "fallback_host") == "mock_host"
assert discovery_info.get("invalid_key", "fallback_host") == "fallback_host" assert discovery_info.get("invalid_key", "fallback_host") == "fallback_host"
assert "Detected code that accessed discovery_info['host']" in caplog.text
assert "Detected code that accessed discovery_info.get('host')" not in caplog.text
# Ensure second call doesn't get logged
caplog.clear()
assert discovery_info["host"] == "mock_host"
assert discovery_info.get("host") == "mock_host"
assert "Detected code that accessed discovery_info['host']" not in caplog.text
assert "Detected code that accessed discovery_info.get('host')" not in caplog.text
discovery_info._warning_logged = False
assert discovery_info.get("host") == "mock_host"
assert "Detected code that accessed discovery_info.get('host')" in caplog.text

View file

@ -6,7 +6,7 @@ import logging
import socket import socket
import ssl import ssl
import threading import threading
from unittest.mock import AsyncMock, MagicMock, patch from unittest.mock import AsyncMock, MagicMock, Mock, patch
from aiohttp.test_utils import make_mocked_request from aiohttp.test_utils import make_mocked_request
import freezegun import freezegun
@ -778,3 +778,30 @@ def hass_recorder(enable_statistics, hass_storage):
yield setup_recorder yield setup_recorder
hass.stop() hass.stop()
@pytest.fixture
def mock_integration_frame():
"""Mock as if we're calling code from inside an integration."""
correct_frame = Mock(
filename="/home/paulus/homeassistant/components/hue/light.py",
lineno="23",
line="self.light.is_on",
)
with patch(
"homeassistant.helpers.frame.extract_stack",
return_value=[
Mock(
filename="/home/paulus/homeassistant/core.py",
lineno="23",
line="do_something()",
),
correct_frame,
Mock(
filename="/home/paulus/aiohue/lights.py",
lineno="2",
line="something()",
),
],
):
yield correct_frame

View file

@ -1,31 +0,0 @@
"""Fixtures for helpers."""
from unittest.mock import Mock, patch
import pytest
@pytest.fixture
def mock_integration_frame():
"""Mock as if we're calling code from inside an integration."""
correct_frame = Mock(
filename="/home/paulus/homeassistant/components/hue/light.py",
lineno="23",
line="self.light.is_on",
)
with patch(
"homeassistant.helpers.frame.extract_stack",
return_value=[
Mock(
filename="/home/paulus/homeassistant/core.py",
lineno="23",
line="do_something()",
),
correct_frame,
Mock(
filename="/home/paulus/aiohue/lights.py",
lineno="2",
line="something()",
),
],
):
yield correct_frame