Do not use AsyncTrackStates (#47255)

This commit is contained in:
Paulus Schoutsen 2021-03-11 23:18:09 -08:00 committed by GitHub
parent 2a22c54fcb
commit ff94e920e4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 73 additions and 43 deletions

View file

@ -37,7 +37,6 @@ from homeassistant.helpers import template
from homeassistant.helpers.json import JSONEncoder from homeassistant.helpers.json import JSONEncoder
from homeassistant.helpers.network import NoURLAvailableError, get_url from homeassistant.helpers.network import NoURLAvailableError, get_url
from homeassistant.helpers.service import async_get_all_descriptions from homeassistant.helpers.service import async_get_all_descriptions
from homeassistant.helpers.state import AsyncTrackStates
from homeassistant.helpers.system_info import async_get_system_info from homeassistant.helpers.system_info import async_get_system_info
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -367,20 +366,27 @@ class APIDomainServicesView(HomeAssistantView):
Returns a list of changed states. Returns a list of changed states.
""" """
hass = request.app["hass"] hass: ha.HomeAssistant = request.app["hass"]
body = await request.text() body = await request.text()
try: try:
data = json.loads(body) if body else None data = json.loads(body) if body else None
except ValueError: except ValueError:
return self.json_message("Data should be valid JSON.", HTTP_BAD_REQUEST) return self.json_message("Data should be valid JSON.", HTTP_BAD_REQUEST)
with AsyncTrackStates(hass) as changed_states: context = self.context(request)
try:
await hass.services.async_call( try:
domain, service, data, blocking=True, context=self.context(request) await hass.services.async_call(
) domain, service, data, blocking=True, context=context
except (vol.Invalid, ServiceNotFound) as ex: )
raise HTTPBadRequest() from ex except (vol.Invalid, ServiceNotFound) as ex:
raise HTTPBadRequest() from ex
changed_states = []
for state in hass.states.async_all():
if state.context is context:
changed_states.append(state)
return self.json(changed_states) return self.json(changed_states)

View file

@ -22,6 +22,7 @@ from homeassistant.core import Context, State
from homeassistant.loader import IntegrationNotFound, async_get_integration, bind_hass from homeassistant.loader import IntegrationNotFound, async_get_integration, bind_hass
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
from .frame import report
from .typing import HomeAssistantType from .typing import HomeAssistantType
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -35,6 +36,9 @@ class AsyncTrackStates:
when with-block is exited. when with-block is exited.
Must be run within the event loop. Must be run within the event loop.
Deprecated. Remove after June 2021.
Warning added via `get_changed_since`.
""" """
def __init__(self, hass: HomeAssistantType) -> None: def __init__(self, hass: HomeAssistantType) -> None:
@ -61,7 +65,11 @@ class AsyncTrackStates:
def get_changed_since( def get_changed_since(
states: Iterable[State], utc_point_in_time: dt.datetime states: Iterable[State], utc_point_in_time: dt.datetime
) -> List[State]: ) -> List[State]:
"""Return list of states that have been changed since utc_point_in_time.""" """Return list of states that have been changed since utc_point_in_time.
Deprecated. Remove after June 2021.
"""
report("uses deprecated `get_changed_since`")
return [state for state in states if state.last_updated >= utc_point_in_time] return [state for state in states if state.last_updated >= utc_point_in_time]

View file

@ -270,7 +270,6 @@ async def test_api_call_service_no_data(hass, mock_api_client):
async def test_api_call_service_with_data(hass, mock_api_client): async def test_api_call_service_with_data(hass, mock_api_client):
"""Test if the API allows us to call a service.""" """Test if the API allows us to call a service."""
test_value = []
@ha.callback @ha.callback
def listener(service_call): def listener(service_call):
@ -278,17 +277,24 @@ async def test_api_call_service_with_data(hass, mock_api_client):
Also test if our data came through. Also test if our data came through.
""" """
if "test" in service_call.data: hass.states.async_set(
test_value.append(1) "test.data",
"on",
{"data": service_call.data["test"]},
context=service_call.context,
)
hass.services.async_register("test_domain", "test_service", listener) hass.services.async_register("test_domain", "test_service", listener)
await mock_api_client.post( resp = await mock_api_client.post(
"/api/services/test_domain/test_service", json={"test": 1} "/api/services/test_domain/test_service", json={"test": 1}
) )
data = await resp.json()
await hass.async_block_till_done() assert len(data) == 1
assert len(test_value) == 1 state = data[0]
assert state["entity_id"] == "test.data"
assert state["state"] == "on"
assert state["attributes"] == {"data": 1}
async def test_api_template(hass, mock_api_client): async def test_api_template(hass, mock_api_client):

31
tests/helpers/conftest.py Normal file
View file

@ -0,0 +1,31 @@
"""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

View file

@ -6,34 +6,13 @@ import pytest
from homeassistant.helpers import frame from homeassistant.helpers import frame
async def test_extract_frame_integration(caplog): async def test_extract_frame_integration(caplog, mock_integration_frame):
"""Test extracting the current frame from integration context.""" """Test extracting the current frame from integration context."""
correct_frame = Mock( found_frame, integration, path = frame.get_integration_frame()
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()",
),
],
):
found_frame, integration, path = frame.get_integration_frame()
assert integration == "hue" assert integration == "hue"
assert path == "homeassistant/components/" assert path == "homeassistant/components/"
assert found_frame == correct_frame assert found_frame == mock_integration_frame
async def test_extract_frame_integration_with_excluded_intergration(caplog): async def test_extract_frame_integration_with_excluded_intergration(caplog):

View file

@ -25,7 +25,7 @@ from homeassistant.util import dt as dt_util
from tests.common import async_mock_service from tests.common import async_mock_service
async def test_async_track_states(hass): async def test_async_track_states(hass, mock_integration_frame):
"""Test AsyncTrackStates context manager.""" """Test AsyncTrackStates context manager."""
point1 = dt_util.utcnow() point1 = dt_util.utcnow()
point2 = point1 + timedelta(seconds=5) point2 = point1 + timedelta(seconds=5)
@ -82,7 +82,7 @@ async def test_call_to_component(hass):
) )
async def test_get_changed_since(hass): async def test_get_changed_since(hass, mock_integration_frame):
"""Test get_changed_since.""" """Test get_changed_since."""
point1 = dt_util.utcnow() point1 = dt_util.utcnow()
point2 = point1 + timedelta(seconds=5) point2 = point1 + timedelta(seconds=5)