22# SPDX-FileCopyrightText: 2024 Attila Gombos <attila.gombos@effective-range.com>
33# SPDX-License-Identifier: MIT
44
5+ from concurrent .futures import ThreadPoolExecutor
56from dataclasses import dataclass
67from enum import Enum
78from typing import Any , Protocol
@@ -22,6 +23,8 @@ class DiscoveryEventType(Enum):
2223
2324@dataclass
2425class DiscoveryEvent :
26+ group : Group
27+ query : ServiceQuery
2528 service : ServiceInfo
2629 type : DiscoveryEventType
2730
@@ -41,28 +44,26 @@ def stop(self) -> None:
4144 def discover (self , query : ServiceQuery | None = None ) -> None :
4245 raise NotImplementedError ()
4346
44- def get_services (self ) -> dict [UUID , ServiceInfo ]:
45- raise NotImplementedError ()
46-
4747 def register (self , handler : OnDiscoveryEvent ) -> None :
4848 raise NotImplementedError ()
4949
5050 def deregister (self , handler : OnDiscoveryEvent ) -> None :
5151 raise NotImplementedError ()
5252
53- def get_handlers (self ) -> list [ OnDiscoveryEvent ]:
53+ def get_services (self ) -> dict [ UUID , ServiceInfo ]:
5454 raise NotImplementedError ()
5555
5656
5757class DefaultDiscoverer (Discoverer ):
5858
59- def __init__ (self , sender : Sender , receiver : Receiver ) -> None :
59+ def __init__ (self , sender : Sender , receiver : Receiver , max_workers : int = 8 ) -> None :
6060 self ._sender = sender
6161 self ._receiver = receiver
6262 self ._group : Group | None = None
6363 self ._matcher : ServiceMatcher | None = None
64- self ._cache : dict [UUID , ServiceInfo ] = {}
64+ self ._services : dict [UUID , ServiceInfo ] = {}
6565 self ._handlers : list [OnDiscoveryEvent ] = []
66+ self ._handler_executor = ThreadPoolExecutor (max_workers = max_workers )
6667
6768 def __enter__ (self ) -> Discoverer :
6869 return self
@@ -80,65 +81,73 @@ def start(self, group: Group, query: ServiceQuery | None = None) -> None:
8081
8182 def stop (self ) -> None :
8283 self ._group = None
84+ self ._matcher = None
8385 self ._sender .stop ()
86+ self ._receiver .deregister (self ._handle_message )
8487 self ._receiver .stop ()
8588
8689 def discover (self , query : ServiceQuery | None = None ) -> None :
90+ if query :
91+ self ._matcher = ServiceMatcher (query )
92+
8793 if self ._group :
88- if query :
89- self ._matcher = ServiceMatcher (query )
9094 if self ._matcher :
9195 self ._sender .send (self ._matcher .query )
92- log .info ('Service discovery initiated' , query = self ._matcher .query , group = self ._group )
96+ log .info ('Service discovery initiated' , group = self ._group , query = self ._matcher .query )
97+ else :
98+ log .warning ('Cannot discover services, no query provided' , group = self ._group )
9399 else :
94100 log .warning ('Cannot discover services, discoverer not started' , query = query )
95101
96- def get_services (self ) -> dict [UUID , ServiceInfo ]:
97- return self ._cache .copy ()
98-
99102 def register (self , handler : OnDiscoveryEvent ) -> None :
100103 self ._handlers .append (handler )
101104
102105 def deregister (self , handler : OnDiscoveryEvent ) -> None :
103106 self ._handlers .remove (handler )
104107
105- def get_handlers (self ) -> list [ OnDiscoveryEvent ]:
106- return self ._handlers .copy ()
108+ def get_services (self ) -> dict [ UUID , ServiceInfo ]:
109+ return self ._services .copy ()
107110
108111 def _handle_message (self , message : dict [str , Any ]) -> None :
109- try :
110- service = ServiceInfo (UUID (message ['uuid' ]), message ['name' ], message ['role' ], message .get ('urls' , {}))
111- self ._handle_service (service )
112- except Exception as error :
113- log .warn ('Failed to handle received message' , data = message , error = error )
112+ if self ._group and self ._matcher :
113+ try :
114+ service = ServiceInfo (UUID (message ['uuid' ]), message ['name' ], message ['role' ], message .get ('urls' , {}))
115+ log .debug ('Service info received' , service = service , group = self ._group )
116+ self ._handle_service (service , self ._group , self ._matcher )
117+ except Exception as error :
118+ log .warn ('Invalid service info received' , group = self ._group , data = message , error = error )
114119
115- def _handle_service (self , service : ServiceInfo ) -> None :
116- if self . _matcher and self . _matcher .matches (service ):
117- cached = self ._cache .get (service .uuid )
120+ def _handle_service (self , service : ServiceInfo , group : Group , matcher : ServiceMatcher ) -> None :
121+ if matcher .matches (service ):
122+ stored = self ._services .get (service .uuid )
118123
119- if event := self ._create_event (cached , service ):
124+ if event := self ._create_event (group , matcher , stored , service ):
120125 self ._handle_event (event )
121126
122- def _create_event (self , cached : ServiceInfo | None , service : ServiceInfo ) -> DiscoveryEvent | None :
123- if cached :
124- if cached != service :
125- log .info ('Service updated' , old_service = cached , new_service = service )
126- return DiscoveryEvent (service , DiscoveryEventType .UPDATED )
127+ def _create_event (self , group : Group , matcher : ServiceMatcher ,
128+ stored : ServiceInfo | None , service : ServiceInfo ) -> DiscoveryEvent | None :
129+ if stored :
130+ if stored != service :
131+ log .info ('Service updated' , group = group , old_service = stored , new_service = service )
132+ return DiscoveryEvent (group , matcher .query , service , DiscoveryEventType .UPDATED )
127133 else :
128- log .debug ('Service unchanged' , service = service )
134+ log .debug ('Service unchanged' , group = group , service = service )
129135 return None
130136 else :
131- log .info ('Service discovered' , service = service )
132- return DiscoveryEvent (service , DiscoveryEventType .DISCOVERED )
137+ log .info ('New service discovered' , group = group , service = service )
138+ return DiscoveryEvent (group , matcher . query , service , DiscoveryEventType .DISCOVERED )
133139
134140 def _handle_event (self , event : DiscoveryEvent ) -> None :
135- service = event .service
136- self ._cache [service .uuid ] = service
137- for callback in self ._handlers :
138- try :
139- callback (event )
140- except Exception as error :
141- log .warn ('Error in event handler execution' , event = event , error = error )
141+ self ._services [event .service .uuid ] = event .service
142+
143+ for handler in self ._handlers :
144+ self ._handler_executor .submit (self ._execute_handler , handler , event )
145+
146+ def _execute_handler (self , handler : OnDiscoveryEvent , event : DiscoveryEvent ) -> None :
147+ try :
148+ handler (event )
149+ except Exception as error :
150+ log .warn ('Error in event handler execution' , event = event , error = error )
142151
143152
144153class ScheduledDiscoverer (DefaultScheduler [ServiceQuery ], Discoverer ):
@@ -172,8 +181,5 @@ def register(self, handler: OnDiscoveryEvent) -> None:
172181 def deregister (self , handler : OnDiscoveryEvent ) -> None :
173182 self ._discoverer .deregister (handler )
174183
175- def get_handlers (self ) -> list [OnDiscoveryEvent ]:
176- return self ._discoverer .get_handlers ()
177-
178184 def _execute (self , query : ServiceQuery | None = None ) -> None :
179185 self .discover (query )
0 commit comments