Skip to content

Commit 0dbcabf

Browse files
authored
Run ServiceInfo requests in the event loop (#748)
1 parent 7b3b4b5 commit 0dbcabf

5 files changed

Lines changed: 18 additions & 44 deletions

File tree

examples/async_service_info_request.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ async def async_watch_services(aiozc: AsyncZeroconf) -> None:
2828
if not name.endswith(HAP_TYPE):
2929
continue
3030
infos.append(AsyncServiceInfo(HAP_TYPE, name))
31-
tasks = [info.async_request(aiozc, 3000) for info in infos]
31+
tasks = [info.async_request(aiozc.zeroconf, 3000) for info in infos]
3232
await asyncio.gather(*tasks)
3333
for info in infos:
3434
print("Info for %s" % (info.name))

tests/services/test_info.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ def send(out, addr=const._MDNS_ADDR, port=const._MDNS_PORT):
216216
send_event.set()
217217

218218
# patch the zeroconf send
219-
with unittest.mock.patch.object(zc, "send", send):
219+
with unittest.mock.patch.object(zc, "async_send", send):
220220

221221
def mock_incoming_msg(records) -> r.DNSIncoming:
222222

@@ -353,7 +353,7 @@ def send(out, addr=const._MDNS_ADDR, port=const._MDNS_PORT):
353353
send_event.set()
354354

355355
# patch the zeroconf send
356-
with unittest.mock.patch.object(zc, "send", send):
356+
with unittest.mock.patch.object(zc, "async_send", send):
357357

358358
def mock_incoming_msg(records) -> r.DNSIncoming:
359359

tests/test_aio.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,7 @@ async def test_service_info_async_request() -> None:
409409
# Generating the race condition is almost impossible
410410
# without patching since its a TOCTOU race
411411
with unittest.mock.patch("zeroconf.aio.AsyncServiceInfo._is_complete", False):
412-
await aiosinfo.async_request(aiozc, 3000)
412+
await aiosinfo.async_request(aiozc.zeroconf, 3000)
413413
assert aiosinfo is not None
414414
assert aiosinfo.addresses == [socket.inet_aton("10.0.1.3")]
415415

zeroconf/_services/info.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
USA
2121
"""
2222

23+
import asyncio
2324
import socket
2425
from typing import Dict, List, Optional, TYPE_CHECKING, Union, cast
2526

@@ -393,6 +394,13 @@ def _is_complete(self) -> bool:
393394
return not (self.text is None or not self._addresses)
394395

395396
def request(self, zc: 'Zeroconf', timeout: float) -> bool:
397+
"""Returns true if the service could be discovered on the
398+
network, and updates this object with details discovered.
399+
"""
400+
assert zc.loop is not None
401+
return asyncio.run_coroutine_threadsafe(self.async_request(zc, timeout), zc.loop).result()
402+
403+
async def async_request(self, zc: 'Zeroconf', timeout: float) -> bool:
396404
"""Returns true if the service could be discovered on the
397405
network, and updates this object with details discovered.
398406
"""
@@ -403,22 +411,21 @@ def request(self, zc: 'Zeroconf', timeout: float) -> bool:
403411
delay = _LISTENER_TIME
404412
next_ = now
405413
last = now + timeout
414+
await zc.async_wait_for_start()
406415
try:
407-
# Do not set a question on the listener to preload from cache
408-
# since we just checked it above in load_from_cache
409416
zc.add_listener(self, None)
410417
while not self._is_complete:
411418
if last <= now:
412419
return False
413420
if next_ <= now:
414421
out = self.generate_request_query(zc, now)
415422
if not out.questions:
416-
return True
417-
zc.send(out)
423+
return self.load_from_cache(zc)
424+
zc.async_send(out)
418425
next_ = now + delay
419426
delay *= 2
420427

421-
zc.wait(min(next_, last) - now)
428+
await zc.async_wait(min(next_, last) - now)
422429
now = current_time_millis()
423430
finally:
424431
zc.remove_listener(self)

zeroconf/aio.py

Lines changed: 2 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,10 @@
3131
from ._services.types import ZeroconfServiceTypes
3232
from ._utils.aio import wait_condition_or_timeout
3333
from ._utils.net import IPVersion, InterfaceChoice, InterfacesType
34-
from ._utils.time import current_time_millis, millis_to_seconds
34+
from ._utils.time import millis_to_seconds
3535
from .const import (
3636
_BROWSER_TIME,
3737
_CHECK_TIME,
38-
_LISTENER_TIME,
3938
_MDNS_PORT,
4039
_REGISTER_TIME,
4140
_SERVICE_TYPE_ENUMERATION_NAME,
@@ -66,38 +65,6 @@ def update_service(self, aiozc: 'AsyncZeroconf', type_: str, name: str) -> None:
6665
class AsyncServiceInfo(ServiceInfo):
6766
"""An async version of ServiceInfo."""
6867

69-
async def async_request(self, aiozc: 'AsyncZeroconf', timeout: float) -> bool:
70-
"""Returns true if the service could be discovered on the
71-
network, and updates this object with details discovered.
72-
"""
73-
if self.load_from_cache(aiozc.zeroconf):
74-
return True
75-
76-
now = current_time_millis()
77-
delay = _LISTENER_TIME
78-
next_ = now
79-
last = now + timeout
80-
await aiozc.zeroconf.async_wait_for_start()
81-
try:
82-
aiozc.zeroconf.add_listener(self, None)
83-
while not self._is_complete:
84-
if last <= now:
85-
return False
86-
if next_ <= now:
87-
out = self.generate_request_query(aiozc.zeroconf, now)
88-
if not out.questions:
89-
return self.load_from_cache(aiozc.zeroconf)
90-
aiozc.zeroconf.async_send(out)
91-
next_ = now + delay
92-
delay *= 2
93-
94-
await aiozc.zeroconf.async_wait(min(next_, last) - now)
95-
now = current_time_millis()
96-
finally:
97-
aiozc.zeroconf.remove_listener(self)
98-
99-
return True
100-
10168

10269
class AsyncServiceBrowser(_ServiceBrowserBase):
10370
"""Used to browse for a service of a specific type.
@@ -333,7 +300,7 @@ async def async_get_service_info(
333300
name and type, or None if no service matches by the timeout,
334301
which defaults to 3 seconds."""
335302
info = AsyncServiceInfo(type_, name)
336-
if await info.async_request(self, timeout):
303+
if await info.async_request(self.zeroconf, timeout):
337304
return info
338305
return None
339306

0 commit comments

Comments
 (0)