feat: optimize equality checks for DNS records (#1120) · python-zeroconf/python-zeroconf@3a25ff7

@@ -72,9 +72,12 @@ def __init__(self, name: str, type_: int, class_: int) -> None:

7272

self.class_ = class_ & _CLASS_MASK

7373

self.unique = (class_ & _CLASS_UNIQUE) != 0

747475+

def _dns_entry_matches(self, other) -> bool: # type: ignore[no-untyped-def]

76+

return self.key == other.key and self.type == other.type and self.class_ == other.class_

77+7578

def __eq__(self, other: Any) -> bool:

7679

"""Equality test on key (lowercase name), type, and class"""

77-

return _dns_entry_matches(other, self.key, self.type, self.class_) and isinstance(other, DNSEntry)

80+

return isinstance(other, DNSEntry) and self._dns_entry_matches(other)

78817982

@staticmethod

8083

def get_class_(class_: int) -> str:

@@ -117,7 +120,7 @@ def __hash__(self) -> int:

117120118121

def __eq__(self, other: Any) -> bool:

119122

"""Tests equality on dns question."""

120-

return isinstance(other, DNSQuestion) and _dns_entry_matches(other, self.key, self.type, self.class_)

123+

return isinstance(other, DNSQuestion) and self._dns_entry_matches(other)

121124122125

@property

123126

def max_size(self) -> int:

@@ -169,9 +172,9 @@ def __eq__(self, other: Any) -> bool: # pylint: disable=no-self-use

169172

def suppressed_by(self, msg: 'DNSIncoming') -> bool:

170173

"""Returns true if any answer in a message can suffice for the

171174

information held in this record."""

172-

return any(self.suppressed_by_answer(record) for record in msg.answers)

175+

return any(self._suppressed_by_answer(record) for record in msg.answers)

173176174-

def suppressed_by_answer(self, other: 'DNSRecord') -> bool:

177+

def _suppressed_by_answer(self, other) -> bool: # type: ignore[no-untyped-def]

175178

"""Returns true if another record has same name, type and class,

176179

and if its TTL is at least half of this record's."""

177180

return self == other and other.ttl > (self.ttl / 2)

@@ -246,11 +249,13 @@ def write(self, out: 'DNSOutgoing') -> None:

246249247250

def __eq__(self, other: Any) -> bool:

248251

"""Tests equality on address"""

252+

return isinstance(other, DNSAddress) and self._eq(other)

253+254+

def _eq(self, other) -> bool: # type: ignore[no-untyped-def]

249255

return (

250-

isinstance(other, DNSAddress)

251-

and self.address == other.address

256+

self.address == other.address

252257

and self.scope_id == other.scope_id

253-

and _dns_entry_matches(other, self.key, self.type, self.class_)

258+

and self._dns_entry_matches(other)

254259

)

255260256261

def __hash__(self) -> int:

@@ -289,13 +294,12 @@ def write(self, out: 'DNSOutgoing') -> None:

289294

out.write_character_string(self.os.encode('utf-8'))

290295291296

def __eq__(self, other: Any) -> bool:

292-

"""Tests equality on cpu and os"""

293-

return (

294-

isinstance(other, DNSHinfo)

295-

and self.cpu == other.cpu

296-

and self.os == other.os

297-

and _dns_entry_matches(other, self.key, self.type, self.class_)

298-

)

297+

"""Tests equality on cpu and os."""

298+

return isinstance(other, DNSHinfo) and self._eq(other)

299+300+

def _eq(self, other) -> bool: # type: ignore[no-untyped-def]

301+

"""Tests equality on cpu and os."""

302+

return self.cpu == other.cpu and self.os == other.os and self._dns_entry_matches(other)

299303300304

def __hash__(self) -> int:

301305

"""Hash to compare like DNSHinfo."""

@@ -334,12 +338,12 @@ def write(self, out: 'DNSOutgoing') -> None:

334338

out.write_name(self.alias)

335339336340

def __eq__(self, other: Any) -> bool:

337-

"""Tests equality on alias"""

338-

return (

339-

isinstance(other, DNSPointer)

340-

and self.alias == other.alias

341-

and _dns_entry_matches(other, self.key, self.type, self.class_)

342-

)

341+

"""Tests equality on alias."""

342+

return isinstance(other, DNSPointer) and self._eq(other)

343+344+

def _eq(self, other) -> bool: # type: ignore[no-untyped-def]

345+

"""Tests equality on alias."""

346+

return self.alias == other.alias and self._dns_entry_matches(other)

343347344348

def __hash__(self) -> int:

345349

"""Hash to compare like DNSPointer."""

@@ -373,12 +377,12 @@ def __hash__(self) -> int:

373377

return self._hash

374378375379

def __eq__(self, other: Any) -> bool:

376-

"""Tests equality on text"""

377-

return (

378-

isinstance(other, DNSText)

379-

and self.text == other.text

380-

and _dns_entry_matches(other, self.key, self.type, self.class_)

381-

)

380+

"""Tests equality on text."""

381+

return isinstance(other, DNSText) and self._eq(other)

382+383+

def _eq(self, other) -> bool: # type: ignore[no-untyped-def]

384+

"""Tests equality on text."""

385+

return self.text == other.text and self._dns_entry_matches(other)

382386383387

def __repr__(self) -> str:

384388

"""String representation"""

@@ -422,13 +426,16 @@ def write(self, out: 'DNSOutgoing') -> None:

422426423427

def __eq__(self, other: Any) -> bool:

424428

"""Tests equality on priority, weight, port and server"""

429+

return isinstance(other, DNSService) and self._eq(other)

430+431+

def _eq(self, other) -> bool: # type: ignore[no-untyped-def]

432+

"""Tests equality on priority, weight, port and server."""

425433

return (

426-

isinstance(other, DNSService)

427-

and self.priority == other.priority

434+

self.priority == other.priority

428435

and self.weight == other.weight

429436

and self.port == other.port

430437

and self.server == other.server

431-

and _dns_entry_matches(other, self.key, self.type, self.class_)

438+

and self._dns_entry_matches(other)

432439

)

433440434441

def __hash__(self) -> int:

@@ -478,12 +485,15 @@ def write(self, out: 'DNSOutgoing') -> None:

478485

out.write_string(out_bytes)

479486480487

def __eq__(self, other: Any) -> bool:

481-

"""Tests equality on cpu and os"""

488+

"""Tests equality on next_name and rdtypes."""

489+

return isinstance(other, DNSNsec) and self._eq(other)

490+491+

def _eq(self, other) -> bool: # type: ignore[no-untyped-def]

492+

"""Tests equality on next_name and rdtypes."""

482493

return (

483-

isinstance(other, DNSNsec)

484-

and self.next_name == other.next_name

494+

self.next_name == other.next_name

485495

and self.rdtypes == other.rdtypes

486-

and _dns_entry_matches(other, self.key, self.type, self.class_)

496+

and self._dns_entry_matches(other)

487497

)

488498489499

def __hash__(self) -> int:

@@ -497,6 +507,9 @@ def __repr__(self) -> str:

497507

)

498508499509510+

_DNSRecord = DNSRecord

511+512+500513

class DNSRRSet:

501514

"""A set of dns records independent of the ttl."""

502515

@@ -514,20 +527,15 @@ def lookup(self) -> Dict[DNSRecord, DNSRecord]:

514527

self._lookup = {record: record for record in self._records}

515528

return self._lookup

516529517-

def suppresses(self, record: DNSRecord) -> bool:

530+

def suppresses(self, record: _DNSRecord) -> bool:

518531

"""Returns true if any answer in the rrset can suffice for the

519532

information held in this record."""

520-

other = self.lookup.get(record)

533+

if self._lookup is None:

534+

other = self.lookup.get(record)

535+

else:

536+

other = self._lookup.get(record)

521537

return bool(other and other.ttl > (record.ttl / 2))

522538523539

def __contains__(self, record: DNSRecord) -> bool:

524540

"""Returns true if the rrset contains the record."""

525541

return record in self.lookup

526-527-528-

_DNSEntry = DNSEntry

529-

_str = str

530-531-532-

def _dns_entry_matches(entry: _DNSEntry, key: _str, type_: int, class_: int) -> bool:

533-

return key == entry.key and type_ == entry.type and class_ == entry.class_