feat: improve performance when IP addresses change frequently (#1407) · python-zeroconf/python-zeroconf@111c91a
@@ -23,7 +23,6 @@
2323import asyncio
2424import random
2525import sys
26-from ipaddress import IPv4Address, IPv6Address, _BaseAddress
2726from typing import TYPE_CHECKING, Dict, List, Optional, Set, Union, cast
28272928from .._cache import DNSCache
@@ -50,6 +49,8 @@
5049wait_for_future_set_or_timeout,
5150)
5251from .._utils.ipaddress import (
52+ZeroconfIPv4Address,
53+ZeroconfIPv6Address,
5354cached_ip_addresses,
5455get_ip_address_object_from_record,
5556ip_bytes_and_scope_to_address,
@@ -187,8 +188,8 @@ def __init__(
187188self.type = type_
188189self._name = name
189190self.key = name.lower()
190-self._ipv4_addresses: List[IPv4Address] = []
191-self._ipv6_addresses: List[IPv6Address] = []
191+self._ipv4_addresses: List[ZeroconfIPv4Address] = []
192+self._ipv6_addresses: List[ZeroconfIPv6Address] = []
192193if addresses is not None:
193194self.addresses = addresses
194195elif parsed_addresses is not None:
@@ -260,11 +261,11 @@ def addresses(self, value: List[bytes]) -> None:
260261 )
261262if addr.version == 4:
262263if TYPE_CHECKING:
263-assert isinstance(addr, IPv4Address)
264+assert isinstance(addr, ZeroconfIPv4Address)
264265self._ipv4_addresses.append(addr)
265266else:
266267if TYPE_CHECKING:
267-assert isinstance(addr, IPv6Address)
268+assert isinstance(addr, ZeroconfIPv6Address)
268269self._ipv6_addresses.append(addr)
269270270271@property
@@ -321,7 +322,7 @@ def addresses_by_version(self, version: IPVersion) -> List[bytes]:
321322322323def ip_addresses_by_version(
323324self, version: IPVersion
324- ) -> Union[List[IPv4Address], List[IPv6Address], List[_BaseAddress]]:
325+ ) -> Union[List[ZeroconfIPv4Address], List[ZeroconfIPv6Address]]:
325326"""List ip_address objects matching IP version.
326327327328 Addresses are guaranteed to be returned in LIFO (last in, first out)
@@ -334,7 +335,7 @@ def ip_addresses_by_version(
334335335336def _ip_addresses_by_version_value(
336337self, version_value: int_
337- ) -> Union[List[IPv4Address], List[IPv6Address]]:
338+ ) -> Union[List[ZeroconfIPv4Address], List[ZeroconfIPv6Address]]:
338339"""Backend for addresses_by_version that uses the raw value."""
339340if version_value == _IPVersion_All_value:
340341return [*self._ipv4_addresses, *self._ipv6_addresses] # type: ignore[return-value]
@@ -440,9 +441,9 @@ def get_name(self) -> str:
440441441442def _get_ip_addresses_from_cache_lifo(
442443self, zc: "Zeroconf", now: float_, type: int_
443- ) -> List[Union[IPv4Address, IPv6Address]]:
444+ ) -> List[Union[ZeroconfIPv4Address, ZeroconfIPv6Address]]:
444445"""Set IPv6 addresses from the cache."""
445-address_list: List[Union[IPv4Address, IPv6Address]] = []
446+address_list: List[Union[ZeroconfIPv4Address, ZeroconfIPv6Address]] = []
446447for record in self._get_address_records_from_cache_by_type(zc, type):
447448if record.is_expired(now):
448449continue
@@ -456,7 +457,7 @@ def _set_ipv6_addresses_from_cache(self, zc: "Zeroconf", now: float_) -> None:
456457"""Set IPv6 addresses from the cache."""
457458if TYPE_CHECKING:
458459self._ipv6_addresses = cast(
459-"List[IPv6Address]",
460+"List[ZeroconfIPv6Address]",
460461self._get_ip_addresses_from_cache_lifo(zc, now, _TYPE_AAAA),
461462 )
462463else:
@@ -466,7 +467,7 @@ def _set_ipv4_addresses_from_cache(self, zc: "Zeroconf", now: float_) -> None:
466467"""Set IPv4 addresses from the cache."""
467468if TYPE_CHECKING:
468469self._ipv4_addresses = cast(
469-"List[IPv4Address]",
470+"List[ZeroconfIPv4Address]",
470471self._get_ip_addresses_from_cache_lifo(zc, now, _TYPE_A),
471472 )
472473else:
@@ -509,24 +510,32 @@ def _process_record_threadsafe(self, zc: "Zeroconf", record: DNSRecord, now: flo
509510510511if ip_addr.version == 4:
511512if TYPE_CHECKING:
512-assert isinstance(ip_addr, IPv4Address)
513+assert isinstance(ip_addr, ZeroconfIPv4Address)
513514ipv4_addresses = self._ipv4_addresses
514515if ip_addr not in ipv4_addresses:
515516ipv4_addresses.insert(0, ip_addr)
516517return True
517-elif ip_addr != ipv4_addresses[0]:
518+# Use int() to compare the addresses as integers
519+# since by default IPv4Address.__eq__ compares the
520+# the addresses on version and int which more than
521+# we need here since we know the version is 4.
522+elif ip_addr.zc_integer != ipv4_addresses[0].zc_integer:
518523ipv4_addresses.remove(ip_addr)
519524ipv4_addresses.insert(0, ip_addr)
520525521526return False
522527523528if TYPE_CHECKING:
524-assert isinstance(ip_addr, IPv6Address)
529+assert isinstance(ip_addr, ZeroconfIPv6Address)
525530ipv6_addresses = self._ipv6_addresses
526531if ip_addr not in self._ipv6_addresses:
527532ipv6_addresses.insert(0, ip_addr)
528533return True
529-elif ip_addr != self._ipv6_addresses[0]:
534+# Use int() to compare the addresses as integers
535+# since by default IPv6Address.__eq__ compares the
536+# the addresses on version and int which more than
537+# we need here since we know the version is 6.
538+elif ip_addr.zc_integer != self._ipv6_addresses[0].zc_integer:
530539ipv6_addresses.remove(ip_addr)
531540ipv6_addresses.insert(0, ip_addr)
532541