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.network import NoURLAvailableError, get_url
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
_LOGGER = logging.getLogger(__name__)
@ -367,20 +366,27 @@ class APIDomainServicesView(HomeAssistantView):
Returns a list of changed states.
"""
hass = request.app["hass"]
hass: ha.HomeAssistant = request.app["hass"]
body = await request.text()
try:
data = json.loads(body) if body else None
except ValueError:
return self.json_message("Data should be valid JSON.", HTTP_BAD_REQUEST)
with AsyncTrackStates(hass) as changed_states:
try:
await hass.services.async_call(
domain, service, data, blocking=True, context=self.context(request)
)
except (vol.Invalid, ServiceNotFound) as ex:
raise HTTPBadRequest() from ex
context = self.context(request)
try:
await hass.services.async_call(
domain, service, data, blocking=True, context=context
)
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)

View File

@ -22,6 +22,7 @@ from homeassistant.core import Context, State
from homeassistant.loader import IntegrationNotFound, async_get_integration, bind_hass
import homeassistant.util.dt as dt_util
from .frame import report
from .typing import HomeAssistantType
_LOGGER = logging.getLogger(__name__)
@ -35,6 +36,9 @@ class AsyncTrackStates:
when with-block is exited.
Must be run within the event loop.
Deprecated. Remove after June 2021.
Warning added via `get_changed_since`.
"""
def __init__(self, hass: HomeAssistantType) -> None:
@ -61,7 +65,11 @@ class AsyncTrackStates:
def get_changed_since(
states: Iterable[State], utc_point_in_time: dt.datetime
) -> 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]

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):
"""Test if the API allows us to call a service."""
test_value = []
@ha.callback
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.
"""
if "test" in service_call.data:
test_value.append(1)
hass.states.async_set(
"test.data",
"on",
{"data": service_call.data["test"]},
context=service_call.context,
)
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}
)
await hass.async_block_till_done()
assert len(test_value) == 1
data = await resp.json()
assert len(data) == 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):

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
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."""
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()",
),
],
):
found_frame, integration, path = frame.get_integration_frame()
found_frame, integration, path = frame.get_integration_frame()
assert integration == "hue"
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):

View File

@ -25,7 +25,7 @@ from homeassistant.util import dt as dt_util
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."""
point1 = dt_util.utcnow()
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."""
point1 = dt_util.utcnow()
point2 = point1 + timedelta(seconds=5)