feat: reduce IP Address parsing overhead in ServiceInfo (#1257) · python-zeroconf/python-zeroconf@83d0b7f

@@ -91,7 +91,13 @@ def instance_name_from_service_info(info: "ServiceInfo", strict: bool = True) ->

9191

return info.name[: -len(service_name) - 1]

9292939394-

_cached_ip_addresses = lru_cache(maxsize=256)(ip_address)

94+

@lru_cache(maxsize=512)

95+

def _cached_ip_addresses(address: Union[str, bytes, int]) -> Optional[Union[IPv4Address, IPv6Address]]:

96+

"""Cache IP addresses."""

97+

try:

98+

return ip_address(address)

99+

except ValueError:

100+

return None

951019610297103

class ServiceInfo(RecordUpdateListener):

@@ -227,16 +233,19 @@ def addresses(self, value: List[bytes]) -> None:

227233

self._get_address_and_nsec_records_cache = None

228234229235

for address in value:

230-

try:

231-

addr = _cached_ip_addresses(address)

232-

except ValueError:

236+

addr = _cached_ip_addresses(address)

237+

if addr is None:

233238

raise TypeError(

234239

"Addresses must either be IPv4 or IPv6 strings, bytes, or integers;"

235240

f" got {address!r}. Hint: convert string addresses with socket.inet_pton"

236241

)

237242

if addr.version == 4:

243+

if TYPE_CHECKING:

244+

assert isinstance(addr, IPv4Address)

238245

self._ipv4_addresses.append(addr)

239246

else:

247+

if TYPE_CHECKING:

248+

assert isinstance(addr, IPv6Address)

240249

self._ipv6_addresses.append(addr)

241250242251

@property

@@ -394,11 +403,8 @@ def _get_ip_addresses_from_cache_lifo(

394403

for record in self._get_address_records_from_cache_by_type(zc, type):

395404

if record.is_expired(now):

396405

continue

397-

try:

398-

ip_addr = _cached_ip_addresses(record.address)

399-

except ValueError:

400-

continue

401-

else:

406+

ip_addr = _cached_ip_addresses(record.address)

407+

if ip_addr is not None:

402408

address_list.append(ip_addr)

403409

address_list.reverse() # Reverse to get LIFO order

404410

return address_list

@@ -446,13 +452,14 @@ def _process_record_threadsafe(self, zc: 'Zeroconf', record: DNSRecord, now: flo

446452

if record_key == self.server_key and record_type is DNSAddress:

447453

if TYPE_CHECKING:

448454

assert isinstance(record, DNSAddress)

449-

try:

450-

ip_addr = _cached_ip_addresses(record.address)

451-

except ValueError as ex:

452-

log.warning("Encountered invalid address while processing %s: %s", record, ex)

455+

ip_addr = _cached_ip_addresses(record.address)

456+

if ip_addr is None:

457+

log.warning("Encountered invalid address while processing %s: %s", record, record.address)

453458

return False

454459455-

if type(ip_addr) is IPv4Address:

460+

if ip_addr.version == 4:

461+

if TYPE_CHECKING:

462+

assert isinstance(ip_addr, IPv4Address)

456463

ipv4_addresses = self._ipv4_addresses

457464

if ip_addr not in ipv4_addresses:

458465

ipv4_addresses.insert(0, ip_addr)

@@ -463,6 +470,8 @@ def _process_record_threadsafe(self, zc: 'Zeroconf', record: DNSRecord, now: flo

463470464471

return False

465472473+

if TYPE_CHECKING:

474+

assert isinstance(ip_addr, IPv6Address)

466475

ipv6_addresses = self._ipv6_addresses

467476

if ip_addr not in self._ipv6_addresses:

468477

ipv6_addresses.insert(0, ip_addr)

@@ -516,7 +525,7 @@ def dns_addresses(

516525

records = [

517526

DNSAddress(

518527

name,

519-

_TYPE_AAAA if type(ip_addr) is IPv6Address else _TYPE_A,

528+

_TYPE_AAAA if ip_addr.version == 6 else _TYPE_A,

520529

class_,

521530

ttl,

522531

ip_addr.packed,