Skip to content

Commit

Permalink
feat: make async_get_service_info available on the Zeroconf object (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
bdraco committed Apr 1, 2024
1 parent 0108b50 commit c4c2dee
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 12 deletions.
31 changes: 29 additions & 2 deletions src/zeroconf/_core.py
Expand Up @@ -39,7 +39,11 @@
from ._protocol.outgoing import DNSOutgoing
from ._services import ServiceListener
from ._services.browser import ServiceBrowser
from ._services.info import ServiceInfo, instance_name_from_service_info
from ._services.info import (
AsyncServiceInfo,
ServiceInfo,
instance_name_from_service_info,
)
from ._services.registry import ServiceRegistry
from ._transport import _WrappedTransport
from ._updates import RecordUpdateListener
Expand Down Expand Up @@ -261,7 +265,13 @@ def get_service_info(
) -> Optional[ServiceInfo]:
"""Returns network's service information for a particular
name and type, or None if no service matches by the timeout,
which defaults to 3 seconds."""
which defaults to 3 seconds.
:param type_: fully qualified service type name
:param name: the name of the service
:param timeout: milliseconds to wait for a response
:param question_type: The type of questions to ask (DNSQuestionType.QM or DNSQuestionType.QU)
"""
info = ServiceInfo(type_, name)
if info.request(self, timeout, question_type):
return info
Expand Down Expand Up @@ -360,6 +370,23 @@ async def async_update_service(self, info: ServiceInfo) -> Awaitable:
self.registry.async_update(info)
return asyncio.ensure_future(self._async_broadcast_service(info, _REGISTER_TIME, None))

async def async_get_service_info(
self, type_: str, name: str, timeout: int = 3000, question_type: Optional[DNSQuestionType] = None
) -> Optional[AsyncServiceInfo]:
"""Returns network's service information for a particular
name and type, or None if no service matches by the timeout,
which defaults to 3 seconds.
:param type_: fully qualified service type name
:param name: the name of the service
:param timeout: milliseconds to wait for a response
:param question_type: The type of questions to ask (DNSQuestionType.QM or DNSQuestionType.QU)
"""
info = AsyncServiceInfo(type_, name)
if await info.async_request(self, timeout, question_type):
return info
return None

async def _async_broadcast_service(
self,
info: ServiceInfo,
Expand Down
16 changes: 16 additions & 0 deletions src/zeroconf/_services/info.py
Expand Up @@ -770,6 +770,12 @@ def request(
While it is not expected during normal operation,
this function may raise EventLoopBlocked if the underlying
call to `async_request` cannot be completed.
:param zc: Zeroconf instance
:param timeout: time in milliseconds to wait for a response
:param question_type: question type to ask
:param addr: address to send the request to
:param port: port to send the request to
"""
assert zc.loop is not None and zc.loop.is_running()
if zc.loop == get_running_loop():
Expand Down Expand Up @@ -803,6 +809,12 @@ async def async_request(
mDNS multicast address and port. This is useful for directing
requests to a specific host that may be able to respond across
subnets.
:param zc: Zeroconf instance
:param timeout: time in milliseconds to wait for a response
:param question_type: question type to ask
:param addr: address to send the request to
:param port: port to send the request to
"""
if not zc.started:
await zc.async_wait_for_start()
Expand Down Expand Up @@ -924,3 +936,7 @@ def __repr__(self) -> str:
)
),
)


class AsyncServiceInfo(ServiceInfo):
"""An async version of ServiceInfo."""
19 changes: 9 additions & 10 deletions src/zeroconf/asyncio.py
Expand Up @@ -28,7 +28,7 @@
from ._dns import DNSQuestionType
from ._services import ServiceListener
from ._services.browser import _ServiceBrowserBase
from ._services.info import ServiceInfo
from ._services.info import AsyncServiceInfo, ServiceInfo
from ._services.types import ZeroconfServiceTypes
from ._utils.net import InterfaceChoice, InterfacesType, IPVersion
from .const import _BROWSER_TIME, _MDNS_PORT, _SERVICE_TYPE_ENUMERATION_NAME
Expand All @@ -41,10 +41,6 @@
]


class AsyncServiceInfo(ServiceInfo):
"""An async version of ServiceInfo."""


class AsyncServiceBrowser(_ServiceBrowserBase):
"""Used to browse for a service for specific type(s).
Expand Down Expand Up @@ -239,11 +235,14 @@ async def async_get_service_info(
) -> Optional[AsyncServiceInfo]:
"""Returns network's service information for a particular
name and type, or None if no service matches by the timeout,
which defaults to 3 seconds."""
info = AsyncServiceInfo(type_, name)
if await info.async_request(self.zeroconf, timeout, question_type):
return info
return None
which defaults to 3 seconds.
:param type_: fully qualified service type name
:param name: the name of the service
:param timeout: milliseconds to wait for a response
:param question_type: The type of questions to ask (DNSQuestionType.QM or DNSQuestionType.QU)
"""
return await self.zeroconf.async_get_service_info(type_, name, timeout, question_type)

async def async_add_service_listener(self, type_: str, listener: ServiceListener) -> None:
"""Adds a listener for a particular service type. This object
Expand Down
4 changes: 4 additions & 0 deletions tests/test_asyncio.py
Expand Up @@ -680,6 +680,10 @@ async def test_service_info_async_request() -> None:
assert aiosinfo is not None
assert aiosinfo.addresses == [socket.inet_aton("10.0.1.3")]

aiosinfo = await aiozc.zeroconf.async_get_service_info(type_, registration_name)
assert aiosinfo is not None
assert aiosinfo.addresses == [socket.inet_aton("10.0.1.3")]

aiosinfos = await asyncio.gather(
aiozc.async_get_service_info(type_, registration_name),
aiozc.async_get_service_info(type_, registration_name2),
Expand Down

0 comments on commit c4c2dee

Please sign in to comment.