@@ -172,7 +172,7 @@ def __init__(
172172 self .priority = priority
173173 self .server = server if server else None
174174 self .server_key = server .lower () if server else None
175- self ._properties : Dict [Union [str , bytes ], Optional [Union [str , bytes ]]] = {}
175+ self ._properties : Optional [ Dict [Union [str , bytes ], Optional [Union [str , bytes ]]]] = None
176176 if isinstance (properties , bytes ):
177177 self ._set_text (properties )
178178 else :
@@ -226,14 +226,18 @@ def addresses(self, value: List[bytes]) -> None:
226226 self ._ipv6_addresses .append (addr )
227227
228228 @property
229- def properties (self ) -> Dict :
229+ def properties (self ) -> Dict [ Union [ str , bytes ], Optional [ Union [ str , bytes ]]] :
230230 """If properties were set in the constructor this property returns the original dictionary
231231 of type `Dict[Union[bytes, str], Any]`.
232232
233233 If properties are coming from the network, after decoding a TXT record, the keys are always
234234 bytes and the values are either bytes, if there was a value, even empty, or `None`, if there
235235 was none. No further decoding is attempted. The type returned is `Dict[bytes, Optional[bytes]]`.
236236 """
237+ if self ._properties is None :
238+ self ._unpack_text_into_properties ()
239+ if TYPE_CHECKING :
240+ assert self ._properties is not None
237241 return self ._properties
238242
239243 async def async_wait (self , timeout : float ) -> None :
@@ -317,10 +321,10 @@ def parsed_scoped_addresses(self, version: IPVersion = IPVersion.All) -> List[st
317321 for addr in self ._ip_addresses_by_version_value (version .value )
318322 ]
319323
320- def _set_properties (self , properties : Dict ) -> None :
324+ def _set_properties (self , properties : Dict [ Union [ str , bytes ], Optional [ Union [ str , bytes ]]] ) -> None :
321325 """Sets properties and text of this info from a dictionary"""
322326 self ._properties = properties
323- list_ = []
327+ list_ : List [ bytes ] = []
324328 result = b''
325329 for key , value in properties .items ():
326330 if isinstance (key , str ):
@@ -338,14 +342,25 @@ def _set_properties(self, properties: Dict) -> None:
338342
339343 def _set_text (self , text : bytes ) -> None :
340344 """Sets properties and text given a text field"""
345+ if text == self .text :
346+ return
341347 self .text = text
348+ # Clear the properties cache
349+ self ._properties = None
350+
351+ def _unpack_text_into_properties (self ) -> None :
352+ """Unpacks the text field into properties"""
353+ text = self .text
342354 end = len (text )
343355 if end == 0 :
356+ # Properties should be set atomically
357+ # in case another thread is reading them
344358 self ._properties = {}
345359 return
360+
346361 result : Dict [Union [str , bytes ], Optional [Union [str , bytes ]]] = {}
347362 index = 0
348- strs = []
363+ strs : List [ bytes ] = []
349364 while index < end :
350365 length = text [index ]
351366 index += 1
@@ -355,17 +370,20 @@ def _set_text(self, text: bytes) -> None:
355370 key : bytes
356371 value : Optional [bytes ]
357372 for s in strs :
358- try :
359- key , value = s .split (b'=' , 1 )
360- except ValueError :
373+ key_value = s .split (b'=' , 1 )
374+ if len (key_value ) == 2 :
375+ key , value = key_value
376+ else :
361377 # No equals sign at all
362378 key = s
363379 value = None
364380
365381 # Only update non-existent properties
366- if key and result . get ( key ) is None :
382+ if key and key not in result :
367383 result [key ] = value
368384
385+ # Properties should be set atomically
386+ # in case another thread is reading them
369387 self ._properties = result
370388
371389 def get_name (self ) -> str :
0 commit comments