Do not use AsyncTrackStates (#47255)
This commit is contained in:
parent
2a22c54fcb
commit
ff94e920e4
6 changed files with 73 additions and 43 deletions
|
@ -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,21 +366,28 @@ 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:
|
try:
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
domain, service, data, blocking=True, context=self.context(request)
|
domain, service, data, blocking=True, context=context
|
||||||
)
|
)
|
||||||
except (vol.Invalid, ServiceNotFound) as ex:
|
except (vol.Invalid, ServiceNotFound) as ex:
|
||||||
raise HTTPBadRequest() from 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)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
31
tests/helpers/conftest.py
Normal 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
|
|
@ -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(
|
|
||||||
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 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):
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue