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) ->
9191return 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
951019610297103class ServiceInfo(RecordUpdateListener):
@@ -227,16 +233,19 @@ def addresses(self, value: List[bytes]) -> None:
227233self._get_address_and_nsec_records_cache = None
228234229235for 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:
233238raise TypeError(
234239"Addresses must either be IPv4 or IPv6 strings, bytes, or integers;"
235240f" got {address!r}. Hint: convert string addresses with socket.inet_pton"
236241 )
237242if addr.version == 4:
243+if TYPE_CHECKING:
244+assert isinstance(addr, IPv4Address)
238245self._ipv4_addresses.append(addr)
239246else:
247+if TYPE_CHECKING:
248+assert isinstance(addr, IPv6Address)
240249self._ipv6_addresses.append(addr)
241250242251@property
@@ -394,11 +403,8 @@ def _get_ip_addresses_from_cache_lifo(
394403for record in self._get_address_records_from_cache_by_type(zc, type):
395404if record.is_expired(now):
396405continue
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:
402408address_list.append(ip_addr)
403409address_list.reverse() # Reverse to get LIFO order
404410return address_list
@@ -446,13 +452,14 @@ def _process_record_threadsafe(self, zc: 'Zeroconf', record: DNSRecord, now: flo
446452if record_key == self.server_key and record_type is DNSAddress:
447453if TYPE_CHECKING:
448454assert 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)
453458return False
454459455-if type(ip_addr) is IPv4Address:
460+if ip_addr.version == 4:
461+if TYPE_CHECKING:
462+assert isinstance(ip_addr, IPv4Address)
456463ipv4_addresses = self._ipv4_addresses
457464if ip_addr not in ipv4_addresses:
458465ipv4_addresses.insert(0, ip_addr)
@@ -463,6 +470,8 @@ def _process_record_threadsafe(self, zc: 'Zeroconf', record: DNSRecord, now: flo
463470464471return False
465472473+if TYPE_CHECKING:
474+assert isinstance(ip_addr, IPv6Address)
466475ipv6_addresses = self._ipv6_addresses
467476if ip_addr not in self._ipv6_addresses:
468477ipv6_addresses.insert(0, ip_addr)
@@ -516,7 +525,7 @@ def dns_addresses(
516525records = [
517526DNSAddress(
518527name,
519-_TYPE_AAAA if type(ip_addr) is IPv6Address else _TYPE_A,
528+_TYPE_AAAA if ip_addr.version == 6 else _TYPE_A,
520529class_,
521530ttl,
522531ip_addr.packed,