@@ -172,7 +172,7 @@ def __init__(
172
172
self .priority = priority
173
173
self .server = server if server else None
174
174
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
176
176
if isinstance (properties , bytes ):
177
177
self ._set_text (properties )
178
178
else :
@@ -226,14 +226,18 @@ def addresses(self, value: List[bytes]) -> None:
226
226
self ._ipv6_addresses .append (addr )
227
227
228
228
@property
229
- def properties (self ) -> Dict :
229
+ def properties (self ) -> Dict [ Union [ str , bytes ], Optional [ Union [ str , bytes ]]] :
230
230
"""If properties were set in the constructor this property returns the original dictionary
231
231
of type `Dict[Union[bytes, str], Any]`.
232
232
233
233
If properties are coming from the network, after decoding a TXT record, the keys are always
234
234
bytes and the values are either bytes, if there was a value, even empty, or `None`, if there
235
235
was none. No further decoding is attempted. The type returned is `Dict[bytes, Optional[bytes]]`.
236
236
"""
237
+ if self ._properties is None :
238
+ self ._unpack_text_into_properties ()
239
+ if TYPE_CHECKING :
240
+ assert self ._properties is not None
237
241
return self ._properties
238
242
239
243
async def async_wait (self , timeout : float ) -> None :
@@ -317,10 +321,10 @@ def parsed_scoped_addresses(self, version: IPVersion = IPVersion.All) -> List[st
317
321
for addr in self ._ip_addresses_by_version_value (version .value )
318
322
]
319
323
320
- def _set_properties (self , properties : Dict ) -> None :
324
+ def _set_properties (self , properties : Dict [ Union [ str , bytes ], Optional [ Union [ str , bytes ]]] ) -> None :
321
325
"""Sets properties and text of this info from a dictionary"""
322
326
self ._properties = properties
323
- list_ = []
327
+ list_ : List [ bytes ] = []
324
328
result = b''
325
329
for key , value in properties .items ():
326
330
if isinstance (key , str ):
@@ -338,14 +342,25 @@ def _set_properties(self, properties: Dict) -> None:
338
342
339
343
def _set_text (self , text : bytes ) -> None :
340
344
"""Sets properties and text given a text field"""
345
+ if text == self .text :
346
+ return
341
347
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
342
354
end = len (text )
343
355
if end == 0 :
356
+ # Properties should be set atomically
357
+ # in case another thread is reading them
344
358
self ._properties = {}
345
359
return
360
+
346
361
result : Dict [Union [str , bytes ], Optional [Union [str , bytes ]]] = {}
347
362
index = 0
348
- strs = []
363
+ strs : List [ bytes ] = []
349
364
while index < end :
350
365
length = text [index ]
351
366
index += 1
@@ -355,17 +370,20 @@ def _set_text(self, text: bytes) -> None:
355
370
key : bytes
356
371
value : Optional [bytes ]
357
372
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 :
361
377
# No equals sign at all
362
378
key = s
363
379
value = None
364
380
365
381
# Only update non-existent properties
366
- if key and result . get ( key ) is None :
382
+ if key and key not in result :
367
383
result [key ] = value
368
384
385
+ # Properties should be set atomically
386
+ # in case another thread is reading them
369
387
self ._properties = result
370
388
371
389
def get_name (self ) -> str :
0 commit comments