Skip to content

Commit 9b6adcf

Browse files
authored
fix: make parsed_scoped_addresses return addresses in the same order as all other methods (#1150)
1 parent 9a16be5 commit 9b6adcf

2 files changed

Lines changed: 42 additions & 24 deletions

File tree

src/zeroconf/_services/info.py

Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,8 @@
2222

2323
import ipaddress
2424
import random
25-
import socket
2625
from functools import lru_cache
27-
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Set, Union, cast
26+
from typing import TYPE_CHECKING, Dict, List, Optional, Set, Union, cast
2827

2928
from .._dns import (
3029
DNSAddress,
@@ -40,7 +39,7 @@
4039
from .._updates import RecordUpdate, RecordUpdateListener
4140
from .._utils.asyncio import get_running_loop, run_coro_with_timeout
4241
from .._utils.name import service_type_name
43-
from .._utils.net import IPVersion, _encode_address, _is_v6_address
42+
from .._utils.net import IPVersion, _encode_address
4443
from .._utils.time import current_time_millis
4544
from ..const import (
4645
_CLASS_IN,
@@ -223,7 +222,14 @@ def properties(self) -> Dict:
223222
return self._properties
224223

225224
def addresses_by_version(self, version: IPVersion) -> List[bytes]:
226-
"""List addresses matching IP version."""
225+
"""List addresses matching IP version.
226+
227+
Addresses are guaranteed to be returned in LIFO (last in, first out)
228+
order with IPv4 addresses first and IPv6 addresses second.
229+
230+
This means the first address will always be the most recently added
231+
address of the given IP version.
232+
"""
227233
if version == IPVersion.V4Only:
228234
return [addr.packed for addr in self._ipv4_addresses]
229235
if version == IPVersion.V6Only:
@@ -236,35 +242,47 @@ def addresses_by_version(self, version: IPVersion) -> List[bytes]:
236242
def ip_addresses_by_version(
237243
self, version: IPVersion
238244
) -> Union[List[ipaddress.IPv4Address], List[ipaddress.IPv6Address], List[ipaddress._BaseAddress]]:
239-
"""List ip_address objects matching IP version."""
245+
"""List ip_address objects matching IP version.
246+
247+
Addresses are guaranteed to be returned in LIFO (last in, first out)
248+
order with IPv4 addresses first and IPv6 addresses second.
249+
250+
This means the first address will always be the most recently added
251+
address of the given IP version.
252+
"""
240253
if version == IPVersion.V4Only:
241254
return self._ipv4_addresses
242255
if version == IPVersion.V6Only:
243256
return self._ipv6_addresses
244257
return [*self._ipv4_addresses, *self._ipv6_addresses]
245258

246259
def parsed_addresses(self, version: IPVersion = IPVersion.All) -> List[str]:
247-
"""List addresses in their parsed string form."""
248-
result = self.addresses_by_version(version)
249-
return [
250-
socket.inet_ntop(socket.AF_INET6 if _is_v6_address(addr) else socket.AF_INET, addr)
251-
for addr in result
252-
]
260+
"""List addresses in their parsed string form.
261+
262+
Addresses are guaranteed to be returned in LIFO (last in, first out)
263+
order with IPv4 addresses first and IPv6 addresses second.
264+
265+
This means the first address will always be the most recently added
266+
address of the given IP version.
267+
"""
268+
return [str(addr) for addr in self.ip_addresses_by_version(version)]
253269

254270
def parsed_scoped_addresses(self, version: IPVersion = IPVersion.All) -> List[str]:
255271
"""Equivalent to parsed_addresses, with the exception that IPv6 Link-Local
256272
addresses are qualified with %<interface_index> when available
273+
274+
Addresses are guaranteed to be returned in LIFO (last in, first out)
275+
order with IPv4 addresses first and IPv6 addresses second.
276+
277+
This means the first address will always be the most recently added
278+
address of the given IP version.
257279
"""
258280
if self.interface_index is None:
259281
return self.parsed_addresses(version)
260-
261-
def is_link_local(addr_str: str) -> Any:
262-
addr = _cached_ip_addresses(addr_str)
263-
return addr.version == 6 and addr.is_link_local
264-
265-
ll_addrs = list(filter(is_link_local, self.parsed_addresses(version)))
266-
other_addrs = list(filter(lambda addr: not is_link_local(addr), self.parsed_addresses(version)))
267-
return [f"{addr}%{self.interface_index}" for addr in ll_addrs] + other_addrs
282+
return [
283+
f"{addr}%{self.interface_index}" if addr.version == 6 and addr.is_link_local else str(addr)
284+
for addr in self.ip_addresses_by_version(version)
285+
]
268286

269287
def _set_properties(self, properties: Dict) -> None:
270288
"""Sets properties and text of this info from a dictionary"""
@@ -399,13 +417,13 @@ def dns_addresses(
399417
return [
400418
DNSAddress(
401419
self.server,
402-
_TYPE_AAAA if _is_v6_address(address) else _TYPE_A,
420+
_TYPE_AAAA if address.version == 6 else _TYPE_A,
403421
_CLASS_IN | _CLASS_UNIQUE,
404422
override_ttl if override_ttl is not None else self.host_ttl,
405-
address,
423+
address.packed,
406424
created=created,
407425
)
408-
for address in self.addresses_by_version(version)
426+
for address in self.ip_addresses_by_version(version)
409427
]
410428

411429
def dns_pointer(self, override_ttl: Optional[int] = None, created: Optional[float] = None) -> DNSPointer:

tests/services/test_info.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -557,14 +557,14 @@ def test_multiple_addresses():
557557
assert info.parsed_addresses(r.IPVersion.V4Only) == [address_parsed]
558558
assert info.parsed_addresses(r.IPVersion.V6Only) == [address_v6_parsed, address_v6_ll_parsed]
559559
assert info.parsed_scoped_addresses() == [
560-
address_v6_ll_scoped_parsed,
561560
address_parsed,
562561
address_v6_parsed,
562+
address_v6_ll_scoped_parsed,
563563
]
564564
assert info.parsed_scoped_addresses(r.IPVersion.V4Only) == [address_parsed]
565565
assert info.parsed_scoped_addresses(r.IPVersion.V6Only) == [
566-
address_v6_ll_scoped_parsed,
567566
address_v6_parsed,
567+
address_v6_ll_scoped_parsed,
568568
]
569569

570570

0 commit comments

Comments
 (0)