Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 29 additions & 2 deletions src/zeroconf/_core.py
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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