Skip to content

Commit

Permalink
config_flow: collect the results of possible_matches generator.
Browse files Browse the repository at this point in the history
Instead of trying to turn the possible_matches generator into an async
generator, instead collect its results and call the whole thing inside
a hass async task.

Hopefully this gets rid of the final warnings about I/O being done in
the main thread during config flow.

Issue #2584
  • Loading branch information
make-all committed Dec 15, 2024
1 parent 6413edc commit 4aed152
Show file tree
Hide file tree
Showing 4 changed files with 15 additions and 10 deletions.
2 changes: 1 addition & 1 deletion custom_components/tuya_local/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ async def async_step_select_type(self, user_input=None):
best_match = 0
best_matching_type = None

async for type in self.device.async_possible_types():
for type in await self.device.async_possible_types():
types.append(type.config_type)
q = type.match_quality(
self.device._get_cached_state(),
Expand Down
16 changes: 10 additions & 6 deletions custom_components/tuya_local/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@
_LOGGER = logging.getLogger(__name__)


def _collect_possible_matches(cached_state, product_ids):
"""Collect possible matches from generator into an array."""
return list(possible_matches(cached_state, product_ids))


class TuyaLocalDevice(object):
def __init__(
self,
Expand Down Expand Up @@ -396,19 +401,18 @@ async def async_possible_types(self):
await self.async_refresh()
cached_state = self._get_cached_state()

for matched in await self._hass.async_add_executor_job(
possible_matches,
return await self._hass.async_add_executor_job(
_collect_possible_matches,
cached_state,
self._product_ids,
):
await asyncio.sleep(0)
yield matched
)

async def async_inferred_type(self):
best_match = None
best_quality = 0
cached_state = self._get_cached_state()
async for config in self.async_possible_types():
possible = await self.async_possible_types()
for config in possible:
quality = config.match_quality(cached_state, self._product_ids)
_LOGGER.info(
"%s considering %s with quality %s",
Expand Down
6 changes: 3 additions & 3 deletions tests/test_config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -350,9 +350,9 @@ def setup_device_mock(mock, failure=False, type="test"):
mock_type.legacy_type = type
mock_type.config_type = type
mock_type.match_quality.return_value = 100
mock_iter = MagicMock()
mock_iter.__aiter__.return_value = [mock_type] if not failure else []
mock.async_possible_types = MagicMock(return_value=mock_iter)
mock.async_possible_types = AsyncMock(
return_value=[mock_type] if not failure else []
)


@pytest.mark.asyncio
Expand Down
1 change: 1 addition & 0 deletions tests/test_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ def setUp(self):
self.hass().data = {"tuya_local": {}}

def job(func, *args):
print(f"{args}")
return func(*args)

self.hass().async_add_executor_job = AsyncMock()
Expand Down

0 comments on commit 4aed152

Please sign in to comment.