Skip to content

Commit f309f9a

Browse files
committed
Move the ServiceRegistry into its own module
1 parent c8a0a71 commit f309f9a

4 files changed

Lines changed: 130 additions & 100 deletions

File tree

zeroconf/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@
7676
_TYPE_TXT,
7777
_UNREGISTER_TIME,
7878
)
79-
from .core import NotifyListener, ServiceRegistry, Zeroconf # noqa # import needed for backwards compat
79+
from .core import NotifyListener, Zeroconf # noqa # import needed for backwards compat
8080
from .dns import ( # noqa # import needed for backwards compat
8181
DNSAddress,
8282
DNSCache,
@@ -110,6 +110,7 @@
110110
ServiceInfo,
111111
ServiceStateChange,
112112
)
113+
from .services.registry import ServiceRegistry # noqa # import needed for backwards compat
113114
from .utils.name import service_type_name # noqa # import needed for backwards compat
114115
from .utils.net import ( # noqa # import needed for backwards compat
115116
add_multicast_member,

zeroconf/core.py

Lines changed: 2 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,10 @@
5252
_UNREGISTER_TIME,
5353
)
5454
from .dns import DNSAddress, DNSCache, DNSIncoming, DNSOutgoing, DNSPointer, DNSQuestion, DNSRecord
55-
from .exceptions import NonUniqueNameException, ServiceNameAlreadyRegistered
55+
from .exceptions import NonUniqueNameException
5656
from .logger import QuietLogger, log
5757
from .services import RecordUpdateListener, ServiceBrowser, ServiceInfo, instance_name_from_service_info
58+
from .services.registry import ServiceRegistry
5859
from .utils.name import service_type_name
5960
from .utils.net import (
6061
IPVersion,
@@ -226,96 +227,6 @@ def handle_read(self, socket_: socket.socket) -> None:
226227
self.zc.handle_response(msg)
227228

228229

229-
class ServiceRegistry:
230-
"""A registry to keep track of services.
231-
232-
This class exists to ensure services can
233-
be safely added and removed with thread
234-
safety.
235-
"""
236-
237-
def __init__(
238-
self,
239-
) -> None:
240-
"""Create the ServiceRegistry class."""
241-
self.services = {} # type: Dict[str, ServiceInfo]
242-
self.types = {} # type: Dict[str, List]
243-
self.servers = {} # type: Dict[str, List]
244-
self._lock = threading.Lock() # add and remove services thread safe
245-
246-
def add(self, info: ServiceInfo) -> None:
247-
"""Add a new service to the registry."""
248-
249-
with self._lock:
250-
self._add(info)
251-
252-
def remove(self, info: ServiceInfo) -> None:
253-
"""Remove a new service from the registry."""
254-
255-
with self._lock:
256-
self._remove(info)
257-
258-
def update(self, info: ServiceInfo) -> None:
259-
"""Update new service in the registry."""
260-
261-
with self._lock:
262-
self._remove(info)
263-
self._add(info)
264-
265-
def get_service_infos(self) -> List[ServiceInfo]:
266-
"""Return all ServiceInfo."""
267-
return list(self.services.values())
268-
269-
def get_info_name(self, name: str) -> Optional[ServiceInfo]:
270-
"""Return all ServiceInfo for the name."""
271-
return self.services.get(name)
272-
273-
def get_types(self) -> List[str]:
274-
"""Return all types."""
275-
return list(self.types.keys())
276-
277-
def get_infos_type(self, type_: str) -> List[ServiceInfo]:
278-
"""Return all ServiceInfo matching type."""
279-
return self._get_by_index("types", type_)
280-
281-
def get_infos_server(self, server: str) -> List[ServiceInfo]:
282-
"""Return all ServiceInfo matching server."""
283-
return self._get_by_index("servers", server)
284-
285-
def _get_by_index(self, attr: str, key: str) -> List[ServiceInfo]:
286-
"""Return all ServiceInfo matching the index."""
287-
service_infos = []
288-
289-
for name in getattr(self, attr).get(key, [])[:]:
290-
info = self.services.get(name)
291-
# Since we do not get under a lock since it would be
292-
# a performance issue, its possible
293-
# the service can be unregistered during the get
294-
# so we must check if info is None
295-
if info is not None:
296-
service_infos.append(info)
297-
298-
return service_infos
299-
300-
def _add(self, info: ServiceInfo) -> None:
301-
"""Add a new service under the lock."""
302-
lower_name = info.name.lower()
303-
if lower_name in self.services:
304-
raise ServiceNameAlreadyRegistered
305-
306-
self.services[lower_name] = info
307-
self.types.setdefault(info.type, []).append(lower_name)
308-
self.servers.setdefault(info.server, []).append(lower_name)
309-
310-
def _remove(self, info: ServiceInfo) -> None:
311-
"""Remove a service under the lock."""
312-
lower_name = info.name.lower()
313-
old_service_info = self.services[lower_name]
314-
self.types[old_service_info.type].remove(lower_name)
315-
self.servers[old_service_info.server].remove(lower_name)
316-
del self.services[lower_name]
317-
318-
319230
class QueryHandler:
320231
"""Query the ServiceRegistry."""
321232

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
from collections import OrderedDict
2828
from typing import Any, Callable, Dict, List, Optional, Set, TYPE_CHECKING, Tuple, Union, cast
2929

30-
from .const import (
30+
from ..const import (
3131
_BROWSER_BACKOFF_LIMIT,
3232
_BROWSER_TIME,
3333
_CLASS_IN,
@@ -46,20 +46,20 @@
4646
_TYPE_SRV,
4747
_TYPE_TXT,
4848
)
49-
from .dns import DNSAddress, DNSOutgoing, DNSPointer, DNSQuestion, DNSRecord, DNSService, DNSText
50-
from .exceptions import BadTypeInNameException
51-
from .utils.name import service_type_name
52-
from .utils.net import (
49+
from ..dns import DNSAddress, DNSOutgoing, DNSPointer, DNSQuestion, DNSRecord, DNSService, DNSText
50+
from ..exceptions import BadTypeInNameException
51+
from ..utils.name import service_type_name
52+
from ..utils.net import (
5353
IPVersion,
5454
_encode_address,
5555
_is_v6_address,
5656
)
57-
from .utils.struct import int2byte
58-
from .utils.time import current_time_millis, millis_to_seconds
57+
from ..utils.struct import int2byte
58+
from ..utils.time import current_time_millis, millis_to_seconds
5959

6060
if TYPE_CHECKING:
6161
# https://github.com/PyCQA/pylint/issues/3525
62-
from . import ( # pylint: disable=cyclic-import
62+
from .. import ( # pylint: disable=cyclic-import
6363
ServiceListener,
6464
Zeroconf,
6565
)

zeroconf/services/registry.py

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
""" Multicast DNS Service Discovery for Python, v0.14-wmcbrine
2+
Copyright 2003 Paul Scott-Murphy, 2014 William McBrine
3+
4+
This module provides a framework for the use of DNS Service Discovery
5+
using IP multicast.
6+
7+
This library is free software; you can redistribute it and/or
8+
modify it under the terms of the GNU Lesser General Public
9+
License as published by the Free Software Foundation; either
10+
version 2.1 of the License, or (at your option) any later version.
11+
12+
This library is distributed in the hope that it will be useful,
13+
but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15+
Lesser General Public License for more details.
16+
17+
You should have received a copy of the GNU Lesser General Public
18+
License along with this library; if not, write to the Free Software
19+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
20+
USA
21+
"""
22+
23+
import threading
24+
from typing import Dict, List, Optional
25+
26+
27+
from ..exceptions import ServiceNameAlreadyRegistered
28+
from ..services import ServiceInfo
29+
30+
31+
class ServiceRegistry:
32+
"""A registry to keep track of services.
33+
34+
This class exists to ensure services can
35+
be safely added and removed with thread
36+
safety.
37+
"""
38+
39+
def __init__(
40+
self,
41+
) -> None:
42+
"""Create the ServiceRegistry class."""
43+
self.services = {} # type: Dict[str, ServiceInfo]
44+
self.types = {} # type: Dict[str, List]
45+
self.servers = {} # type: Dict[str, List]
46+
self._lock = threading.Lock() # add and remove services thread safe
47+
48+
def add(self, info: ServiceInfo) -> None:
49+
"""Add a new service to the registry."""
50+
51+
with self._lock:
52+
self._add(info)
53+
54+
def remove(self, info: ServiceInfo) -> None:
55+
"""Remove a new service from the registry."""
56+
57+
with self._lock:
58+
self._remove(info)
59+
60+
def update(self, info: ServiceInfo) -> None:
61+
"""Update new service in the registry."""
62+
63+
with self._lock:
64+
self._remove(info)
65+
self._add(info)
66+
67+
def get_service_infos(self) -> List[ServiceInfo]:
68+
"""Return all ServiceInfo."""
69+
return list(self.services.values())
70+
71+
def get_info_name(self, name: str) -> Optional[ServiceInfo]:
72+
"""Return all ServiceInfo for the name."""
73+
return self.services.get(name)
74+
75+
def get_types(self) -> List[str]:
76+
"""Return all types."""
77+
return list(self.types.keys())
78+
79+
def get_infos_type(self, type_: str) -> List[ServiceInfo]:
80+
"""Return all ServiceInfo matching type."""
81+
return self._get_by_index("types", type_)
82+
83+
def get_infos_server(self, server: str) -> List[ServiceInfo]:
84+
"""Return all ServiceInfo matching server."""
85+
return self._get_by_index("servers", server)
86+
87+
def _get_by_index(self, attr: str, key: str) -> List[ServiceInfo]:
88+
"""Return all ServiceInfo matching the index."""
89+
service_infos = []
90+
91+
for name in getattr(self, attr).get(key, [])[:]:
92+
info = self.services.get(name)
93+
# Since we do not get under a lock since it would be
94+
# a performance issue, its possible
95+
# the service can be unregistered during the get
96+
# so we must check if info is None
97+
if info is not None:
98+
service_infos.append(info)
99+
100+
return service_infos
101+
102+
def _add(self, info: ServiceInfo) -> None:
103+
"""Add a new service under the lock."""
104+
lower_name = info.name.lower()
105+
if lower_name in self.services:
106+
raise ServiceNameAlreadyRegistered
107+
108+
self.services[lower_name] = info
109+
self.types.setdefault(info.type, []).append(lower_name)
110+
self.servers.setdefault(info.server, []).append(lower_name)
111+
112+
def _remove(self, info: ServiceInfo) -> None:
113+
"""Remove a service under the lock."""
114+
lower_name = info.name.lower()
115+
old_service_info = self.services[lower_name]
116+
self.types[old_service_info.type].remove(lower_name)
117+
self.servers[old_service_info.server].remove(lower_name)
118+
del self.services[lower_name]

0 commit comments

Comments
 (0)