⚡ Sangsakala Proxy 🏠 Home
Skip to content

Commit 30bc14f

Browse files
committed
chore: fresh record display formats
1 parent 07a6038 commit 30bc14f

4 files changed

Lines changed: 84 additions & 27 deletions

File tree

‎src/zeroconf/_dns.py‎

Lines changed: 77 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from ._exceptions import AbstractMethodException
1313
from ._utils.net import _is_v6_address
1414
from ._utils.time import current_time_millis
15-
from .const import _CLASS_MASK, _CLASS_UNIQUE, _TYPE_ANY
15+
from .const import _CLASS_MASK, _CLASS_UNIQUE, _CLASSES, _TYPE_ANY, _TYPES
1616

1717
_LEN_BYTE = 1
1818
_LEN_SHORT = 2
@@ -28,6 +28,20 @@
2828
_float = float
2929
_int = int
3030

31+
32+
def _type_label(type_: int) -> str:
33+
return _TYPES.get(type_, f"unknown-type-{type_}")
34+
35+
36+
def _class_label(class_: int) -> str:
37+
return _CLASSES.get(class_, f"unknown-class-{class_}")
38+
39+
40+
def _format_display(kind: str, fields: list[tuple[str, str]]) -> str:
41+
body = " ".join([f"{key}={value}" if key else value for key, value in fields])
42+
return f"<{kind} {body}>"
43+
44+
3145
if TYPE_CHECKING:
3246
from ._protocol.incoming import DNSIncoming
3347
from ._protocol.outgoing import DNSOutgoing
@@ -58,6 +72,39 @@ def __eq__(self, other: Any) -> bool:
5872
"""Equal when key, type and class match."""
5973
return isinstance(other, DNSEntry) and self._dns_entry_matches(other)
6074

75+
@property
76+
def type_label(self) -> str:
77+
"""Human readable label for the record type."""
78+
return _type_label(self.type)
79+
80+
@property
81+
def class_label(self) -> str:
82+
"""Human readable label for the record class."""
83+
return _class_label(self.class_)
84+
85+
def _display_fields(self) -> list[tuple[str, str]]:
86+
fields = [("name", self.name), ("type", self.type_label), ("class", self.class_label)]
87+
if self.unique:
88+
fields.append(("", "unique"))
89+
return fields
90+
91+
@staticmethod
92+
def get_class_(class_: int) -> str:
93+
"""Compatibility alias for the class_label property."""
94+
return _class_label(class_)
95+
96+
@staticmethod
97+
def get_type(t: int) -> str:
98+
"""Compatibility alias for the type_label property."""
99+
return _type_label(t)
100+
101+
def entry_to_string(self, hdr: str, other: bytes | str | None) -> str:
102+
"""Compatibility alias rendering through the display formatter."""
103+
fields = self._display_fields()
104+
if other is not None:
105+
fields.append(("data", str(other)))
106+
return _format_display(hdr, fields)
107+
61108
def _dns_entry_matches(self, other: DNSEntry) -> bool:
62109
return self.key == other.key and self.type == other.type and self.class_ == other.class_
63110

@@ -86,12 +133,8 @@ def __hash__(self) -> int:
86133
return self._hash
87134

88135
def __repr__(self) -> str:
89-
return "{}[question,{},{},{}]".format(
90-
self.get_type(self.type),
91-
"QU" if self.unicast else "QM",
92-
self.get_class_(self.class_),
93-
self.name,
94-
)
136+
mode = "QU" if self.unicast else "QM"
137+
return _format_display("DNSQuestion", [("mode", mode), *self._display_fields()])
95138

96139
def answered_by(self, rec: DNSRecord) -> bool:
97140
return self.class_ == rec.class_ and self.type in (rec.type, _TYPE_ANY) and self.name == rec.name
@@ -188,6 +231,17 @@ def _set_created_ttl(self, created: _float, ttl: _int) -> None:
188231
self.created = created
189232
self.ttl = ttl
190233

234+
def _display_fields(self) -> list[tuple[str, str]]:
235+
remaining = int(self.get_remaining_ttl(current_time_millis()))
236+
return [*DNSEntry._display_fields(self), ("ttl", f"{self.ttl} ({remaining} remaining)")]
237+
238+
def _repr_with(self, *details: tuple[str, str]) -> str:
239+
return _format_display(type(self).__name__, [*self._display_fields(), *details])
240+
241+
def to_string(self, other: bytes | str) -> str:
242+
"""Compatibility alias rendering through the display formatter."""
243+
return self._repr_with(("data", str(other)))
244+
191245
def _suppressed_by_answer(self, answer: DNSRecord) -> bool:
192246
"""True when the answer matches this record with at least half its TTL left."""
193247
return self == answer and self.ttl / 2 < answer.ttl
@@ -220,14 +274,13 @@ def __hash__(self) -> int:
220274

221275
def __repr__(self) -> str:
222276
try:
223-
return self.to_string(
224-
socket.inet_ntop(
225-
socket.AF_INET6 if _is_v6_address(self.address) else socket.AF_INET,
226-
self.address,
227-
)
277+
data = socket.inet_ntop(
278+
socket.AF_INET6 if _is_v6_address(self.address) else socket.AF_INET,
279+
self.address,
228280
)
229281
except (ValueError, OSError):
230-
return self.to_string(str(self.address))
282+
data = str(self.address)
283+
return self._repr_with(("data", data))
231284

232285
def write(self, out: DNSOutgoing) -> None:
233286
out.write_string(self.address)
@@ -281,6 +334,9 @@ def __hash__(self) -> int:
281334
"""Hash to compare like DNSHinfo."""
282335
return self._hash
283336

337+
def __repr__(self) -> str:
338+
return self._repr_with(("cpu", self.cpu), ("os", self.os))
339+
284340
def write(self, out: DNSOutgoing) -> None:
285341
"""Write the rdata to an outgoing packet."""
286342
out.write_character_string(self.cpu.encode("utf-8"))
@@ -325,9 +381,8 @@ def __hash__(self) -> int:
325381
return self._hash
326382

327383
def __repr__(self) -> str:
328-
return self.to_string(
329-
self.next_name + "," + "|".join([self.get_type(type_) for type_ in self.rdtypes])
330-
)
384+
covered = "|".join([_type_label(t) for t in self.rdtypes])
385+
return self._repr_with(("next_name", self.next_name), ("covers", covered))
331386

332387
def write(self, out: DNSOutgoing) -> None:
333388
"""Write the rdata to an outgoing packet."""
@@ -397,7 +452,7 @@ def __hash__(self) -> int:
397452
return self._hash
398453

399454
def __repr__(self) -> str:
400-
return self.to_string(self.alias)
455+
return self._repr_with(("alias", self.alias))
401456

402457
@property
403458
def max_size_compressed(self) -> int:
@@ -453,6 +508,9 @@ def __hash__(self) -> int:
453508
"""Hash to compare like DNSService."""
454509
return self._hash
455510

511+
def __repr__(self) -> str:
512+
return self._repr_with(("server", self.server), ("port", str(self.port)))
513+
456514
def write(self, out: DNSOutgoing) -> None:
457515
out.write_short(self.priority)
458516
out.write_short(self.weight)
@@ -514,9 +572,8 @@ def __hash__(self) -> int:
514572
return self._hash
515573

516574
def __repr__(self) -> str:
517-
if len(self.text) > 16:
518-
return self.to_string(f"{len(self.text)} bytes")
519-
return self.to_string(self.text)
575+
data = f"{len(self.text)} bytes" if len(self.text) > 16 else str(self.text)
576+
return self._repr_with(("data", data))
520577

521578
def write(self, out: DNSOutgoing) -> None:
522579
out.write_string(self.text)

‎tests/services/test_info.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1090,7 +1090,7 @@ async def test_invalid_a_addresses(caplog):
10901090
info = make_service_info(type_, registration_name, properties=desc, server=host, addresses=[])
10911091
info.load_from_cache(aiozc.zeroconf)
10921092
assert not info.addresses
1093-
assert "Encountered invalid address while processing record" in caplog.text
1093+
assert "Encountered invalid address while processing" in caplog.text
10941094

10951095
await aiozc.async_close()
10961096

‎tests/test_dns.py‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ def test_dns_pointer_repr(self):
5353
@unittest.skipIf(os.environ.get("SKIP_IPV6"), "IPv6 tests disabled")
5454
def test_dns_address_repr(self):
5555
address = r.DNSAddress("irrelevant", const._TYPE_SOA, const._CLASS_IN, 1, b"a")
56-
assert repr(address).endswith("b'a'")
56+
assert "data=b'a'" in repr(address)
5757

5858
address_ipv4 = r.DNSAddress(
5959
"irrelevant",
@@ -62,7 +62,7 @@ def test_dns_address_repr(self):
6262
1,
6363
socket.inet_pton(socket.AF_INET, "127.0.0.1"),
6464
)
65-
assert repr(address_ipv4).endswith("127.0.0.1")
65+
assert "data=127.0.0.1" in repr(address_ipv4)
6666

6767
address_ipv6 = r.DNSAddress(
6868
"irrelevant",
@@ -71,7 +71,7 @@ def test_dns_address_repr(self):
7171
1,
7272
socket.inet_pton(socket.AF_INET6, "::1"),
7373
)
74-
assert repr(address_ipv6).endswith("::1")
74+
assert "data=::1" in repr(address_ipv6)
7575

7676
def test_dns_question_repr(self):
7777
question = r.DNSQuestion("irrelevant", const._TYPE_SRV, const._CLASS_IN | const._CLASS_UNIQUE)

‎tests/test_protocol.py‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -754,7 +754,7 @@ def test_qm_packet_parser():
754754
)
755755
parsed = DNSIncoming(qm_packet)
756756
assert parsed.questions[0].unicast is False
757-
assert ",QM," in str(parsed.questions[0])
757+
assert "mode=QM" in str(parsed.questions[0])
758758

759759

760760
# MDNS 115 Standard query 0x0000 PTR _companion-link._tcp.local, "QU" question OPT
@@ -766,7 +766,7 @@ def test_qu_packet_parser():
766766
)
767767
parsed = DNSIncoming(qu_packet)
768768
assert parsed.questions[0].unicast is True
769-
assert ",QU," in str(parsed.questions[0])
769+
assert "mode=QU" in str(parsed.questions[0])
770770

771771

772772
def test_parse_packet_with_nsec_record():
@@ -780,7 +780,7 @@ def test_parse_packet_with_nsec_record():
780780
)
781781
parsed = DNSIncoming(nsec_packet)
782782
nsec_record = cast(r.DNSNsec, parsed.answers()[3])
783-
assert "nsec," in str(nsec_record)
783+
assert "type=nsec" in str(nsec_record)
784784
assert nsec_record.rdtypes == [16, 33]
785785
assert nsec_record.next_name == "MyHome54 (2)._meshcop._udp.local."
786786

0 commit comments

Comments
 (0)