fix: make parsed_scoped_addresses return addresses in the same order … · python-zeroconf/python-zeroconf@9b6adcf

@@ -22,9 +22,8 @@

22222323

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

28272928

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

224223225224

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]

245258246259

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)]

253269254270

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+

]

268286269287

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

]

410428411429

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