Hello community,
here is the log from the commit of package python-zeroconf for openSUSE:Factory
checked in at 2020-07-21 15:51:05
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-zeroconf (Old)
and /work/SRC/openSUSE:Factory/.python-zeroconf.new.3592 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-zeroconf"
Tue Jul 21 15:51:05 2020 rev:14 rq:822073 version:0.28.0
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-zeroconf/python-zeroconf.changes
2020-06-10 00:48:08.758902574 +0200
+++
/work/SRC/openSUSE:Factory/.python-zeroconf.new.3592/python-zeroconf.changes
2020-07-21 15:54:22.188586543 +0200
@@ -1,0 +2,7 @@
+Tue Jul 21 10:13:44 UTC 2020 - Marketa Calabkova <[email protected]>
+
+- update to 0.28.0
+ * Added support for passing text addresses to ServiceInfo.
+ * Improved logging (includes fixing an incorrect logging call)
+
+-------------------------------------------------------------------
Old:
----
python-zeroconf-0.27.1.tar.gz
New:
----
python-zeroconf-0.28.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-zeroconf.spec ++++++
--- /var/tmp/diff_new_pack.gQAYn5/_old 2020-07-21 15:54:25.492590561 +0200
+++ /var/tmp/diff_new_pack.gQAYn5/_new 2020-07-21 15:54:25.492590561 +0200
@@ -19,19 +19,19 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
%define skip_python2 1
Name: python-zeroconf
-Version: 0.27.1
+Version: 0.28.0
Release: 0
Summary: Pure Python Multicast DNS Service Discovery Library
(Bonjour/Avahi compatible)
License: LGPL-2.0-only
Group: Development/Languages/Python
URL: https://github.com/jstasiak/python-zeroconf
Source:
https://github.com/jstasiak/python-zeroconf/archive/%{version}.tar.gz#/%{name}-%{version}.tar.gz
-BuildRequires: %{python_module ifaddr}
+BuildRequires: %{python_module ifaddr >= 0.1.7}
BuildRequires: %{python_module pytest}
BuildRequires: %{python_module setuptools}
BuildRequires: fdupes
BuildRequires: python-rpm-macros
-Requires: python-ifaddr
+Requires: python-ifaddr >= 0.1.7
BuildArch: noarch
%python_subpackages
++++++ python-zeroconf-0.27.1.tar.gz -> python-zeroconf-0.28.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-zeroconf-0.27.1/README.rst
new/python-zeroconf-0.28.0/README.rst
--- old/python-zeroconf-0.27.1/README.rst 2020-06-05 11:09:58.000000000
+0200
+++ new/python-zeroconf-0.28.0/README.rst 2020-07-07 13:22:12.000000000
+0200
@@ -134,6 +134,20 @@
Changelog
=========
+0.28.0
+======
+
+* Improved Windows support when using socket errno checks, thanks to Sandy
Patterson.
+* Added support for passing text addresses to ServiceInfo.
+* Improved logging (includes fixing an incorrect logging call)
+* Improved Windows compatibility by using Adapter.index from ifaddr, thanks to
PhilippSelenium.
+* Improved Windows compatibility by stopping using socket.if_nameindex.
+* Fixed an OS X edge case which should also eliminate a memory leak, thanks to
Emil Styrke.
+
+Technically backwards incompatible:
+
+* ``ifaddr`` 0.1.7 or newer is required now.
+
0.27.1
------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-zeroconf-0.27.1/requirements-dev.txt
new/python-zeroconf-0.28.0/requirements-dev.txt
--- old/python-zeroconf-0.27.1/requirements-dev.txt 2020-06-05
11:09:58.000000000 +0200
+++ new/python-zeroconf-0.28.0/requirements-dev.txt 2020-07-07
13:22:12.000000000 +0200
@@ -5,6 +5,7 @@
flake8>=3.6.0
flake8-import-order
ifaddr
-pep8-naming!=0.6.0
+# 0.11.0 breaks things https://github.com/PyCQA/pep8-naming/issues/152
+pep8-naming!=0.6.0,!=0.11.0
pytest
pytest-cov
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-zeroconf-0.27.1/setup.py
new/python-zeroconf-0.28.0/setup.py
--- old/python-zeroconf-0.27.1/setup.py 2020-06-05 11:09:58.000000000 +0200
+++ new/python-zeroconf-0.28.0/setup.py 2020-07-07 13:22:12.000000000 +0200
@@ -39,9 +39,10 @@
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
+ 'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy',
],
keywords=['Bonjour', 'Avahi', 'Zeroconf', 'Multicast DNS', 'Service
Discovery', 'mDNS'],
- install_requires=['ifaddr'],
+ install_requires=['ifaddr>=0.1.7'],
)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-zeroconf-0.27.1/zeroconf/__init__.py
new/python-zeroconf-0.28.0/zeroconf/__init__.py
--- old/python-zeroconf-0.27.1/zeroconf/__init__.py 2020-06-05
11:09:58.000000000 +0200
+++ new/python-zeroconf-0.28.0/zeroconf/__init__.py 2020-07-07
13:22:12.000000000 +0200
@@ -25,7 +25,6 @@
import ipaddress
import itertools
import logging
-import os
import platform
import re
import select
@@ -43,7 +42,7 @@
__author__ = 'Paul Scott-Murphy, William McBrine'
__maintainer__ = 'Jakub Stasiak <[email protected]>'
-__version__ = '0.27.1'
+__version__ = '0.28.0'
__license__ = 'LGPL'
@@ -190,7 +189,7 @@
All = 2
-InterfacesType = Union[List[Union[str, int]], InterfaceChoice]
+InterfacesType = Union[List[Union[str, int, Tuple[Tuple[str, int, int],
int]]], InterfaceChoice]
@enum.unique
@@ -219,6 +218,12 @@
return len(addr) == 16
+def _encode_address(address: str) -> bytes:
+ is_ipv6 = ':' in address
+ address_family = socket.AF_INET6 if is_ipv6 else socket.AF_INET
+ return socket.inet_pton(address_family, address)
+
+
def service_type_name(type_: str, *, allow_underscores: bool = False) -> str:
"""
Validate a fully qualified service name, instance or subtype. [rfc6763]
@@ -1696,8 +1701,8 @@
* server: fully qualified name for service host (defaults to name)
* host_ttl: ttl used for A/SRV records
* other_ttl: ttl used for PTR/TXT records
- * addresses: List of IP addresses as unsigned short (IPv4) or unsigned 128
bit number (IPv6),
- network byte order
+ * addresses and parsed_addresses: List of IP addresses (either as bytes,
network byte order, or in parsed
+ form as text; at most one of those parameters can be provided)
"""
@@ -1718,14 +1723,20 @@
host_ttl: int = _DNS_HOST_TTL,
other_ttl: int = _DNS_OTHER_TTL,
*,
- addresses: Optional[List[bytes]] = None
+ addresses: Optional[List[bytes]] = None,
+ parsed_addresses: Optional[List[str]] = None
) -> None:
+ # Accept both none, or one, but not both.
+ if addresses is not None and parsed_addresses is not None:
+ raise TypeError("addresses and parsed_addresses cannot be provided
together")
if not type_.endswith(service_type_name(name, allow_underscores=True)):
raise BadTypeInNameException
self.type = type_
self.name = name
if addresses is not None:
self._addresses = addresses
+ elif parsed_addresses is not None:
+ self._addresses = [_encode_address(a) for a in parsed_addresses]
else:
self._addresses = []
# This results in an ugly error when registering, better check now
@@ -2005,41 +2016,42 @@
return list(set(addr.ip for iface in ifaddr.get_adapters() for addr in
iface.ips if addr.is_IPv4))
-def get_all_addresses_v6() -> List[int]:
+def get_all_addresses_v6() -> List[Tuple[Tuple[str, int, int], int]]:
# IPv6 multicast uses positive indexes for interfaces
- try:
- nameindex = socket.if_nameindex
- except AttributeError:
- # Requires Python 3.8 on Windows. Fall back to Default.
- QuietLogger.log_warning_once(
- 'if_nameindex is not available, falling back to using the default
IPv6 interface'
- )
- return [0]
-
- return [tpl[0] for tpl in nameindex()]
-
+ # TODO: What about multi-address interfaces?
+ return list(
+ set((addr.ip, iface.index) for iface in ifaddr.get_adapters() for addr
in iface.ips if addr.is_IPv6)
+ )
-def ip_to_index(adapters: List[Any], ip: str) -> int:
- if os.name != 'posix':
- # Adapter names that ifaddr reports are not compatible with what
if_nametoindex expects on Windows.
- # We need https://github.com/pydron/ifaddr/pull/21 but it seems stuck
on review.
- raise RuntimeError('Converting from IP addresses to indexes is not
supported on non-POSIX systems')
+def ip6_to_address_and_index(adapters: List[Any], ip: str) -> Tuple[Tuple[str,
int, int], int]:
ipaddr = ipaddress.ip_address(ip)
for adapter in adapters:
for adapter_ip in adapter.ips:
# IPv6 addresses are represented as tuples
if isinstance(adapter_ip.ip, tuple) and
ipaddress.ip_address(adapter_ip.ip[0]) == ipaddr:
- return socket.if_nametoindex(adapter.name)
+ return (cast(Tuple[str, int, int], adapter_ip.ip), cast(int,
adapter.index))
raise RuntimeError('No adapter found for IP address %s' % ip)
-def ip6_addresses_to_indexes(interfaces: List[Union[str, int]]) -> List[int]:
+def interface_index_to_ip6_address(adapters: List[Any], index: int) ->
Tuple[str, int, int]:
+ for adapter in adapters:
+ if adapter.index == index:
+ for adapter_ip in adapter.ips:
+ # IPv6 addresses are represented as tuples
+ if isinstance(adapter_ip.ip, tuple):
+ return cast(Tuple[str, int, int], adapter_ip.ip)
+
+ raise RuntimeError('No adapter found for index %s' % index)
+
+
+def ip6_addresses_to_indexes(
+ interfaces: List[Union[str, int, Tuple[Tuple[str, int, int], int]]]
+) -> List[Tuple[Tuple[str, int, int], int]]:
"""Convert IPv6 interface addresses to interface indexes.
- IPv4 addresses are ignored. The conversion currently only works on POSIX
- systems.
+ IPv4 addresses are ignored.
:param interfaces: List of IP addresses and indexes.
:returns: List of indexes.
@@ -2049,27 +2061,27 @@
for iface in interfaces:
if isinstance(iface, int):
- result.append(iface)
+ result.append((interface_index_to_ip6_address(adapters, iface),
iface))
elif isinstance(iface, str) and ipaddress.ip_address(iface).version ==
6:
- result.append(ip_to_index(adapters, iface))
+ result.append(ip6_to_address_and_index(adapters, iface))
return result
def normalize_interface_choice(
choice: InterfacesType, ip_version: IPVersion = IPVersion.V4Only
-) -> List[Union[str, int]]:
+) -> List[Union[str, Tuple[Tuple[str, int, int], int]]]:
"""Convert the interfaces choice into internal representation.
:param choice: `InterfaceChoice` or list of interface addresses or indexes
(IPv6 only).
:param ip_address: IP version to use (ignored if `choice` is a list).
:returns: List of IP addresses (for IPv4) and indexes (for IPv6).
"""
- result = [] # type: List[Union[str, int]]
+ result = [] # type: List[Union[str, Tuple[Tuple[str, int, int], int]]]
if choice is InterfaceChoice.Default:
if ip_version != IPVersion.V4Only:
# IPv6 multicast uses interface 0 to mean the default
- result.append(0)
+ result.append((('', 0, 0), 0))
if ip_version != IPVersion.V6Only:
result.append('0.0.0.0')
elif choice is InterfaceChoice.All:
@@ -2092,8 +2104,18 @@
def new_socket(
- port: int = _MDNS_PORT, ip_version: IPVersion = IPVersion.V4Only,
apple_p2p: bool = False
+ bind_addr: Union[Tuple[str], Tuple[str, int, int]],
+ port: int = _MDNS_PORT,
+ ip_version: IPVersion = IPVersion.V4Only,
+ apple_p2p: bool = False,
) -> socket.socket:
+ log.debug(
+ 'Creating new socket with port %s, ip_version %s, apple_p2p %s and
bind_addr %r',
+ port,
+ ip_version,
+ apple_p2p,
+ bind_addr,
+ )
if ip_version == IPVersion.V4Only:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
else:
@@ -2145,19 +2167,25 @@
#
https://opensource.apple.com/source/xnu/xnu-4570.41.2/bsd/sys/socket.h
s.setsockopt(socket.SOL_SOCKET, 0x1104, 1)
- s.bind(('', port))
+ s.bind((bind_addr[0], port, *bind_addr[1:]))
+ log.debug('Created socket %s', s)
return s
def add_multicast_member(
- listen_socket: socket.socket, interface: Union[str, int], apple_p2p: bool
= False
+ listen_socket: socket.socket,
+ interface: Union[str, Tuple[Tuple[str, int, int], int]],
+ apple_p2p: bool = False,
) -> Optional[socket.socket]:
# This is based on assumptions in normalize_interface_choice
- is_v6 = isinstance(interface, int)
+ is_v6 = isinstance(interface, tuple)
+ err_einval = {errno.EINVAL}
+ if sys.platform == 'win32':
+ err_einval |= {errno.WSAEINVAL}
log.debug('Adding %r (socket %d) to multicast group', interface,
listen_socket.fileno())
try:
if is_v6:
- iface_bin = struct.pack('@I', cast(int, interface))
+ iface_bin = struct.pack('@I', cast(int, interface[1]))
_value = _MDNS_ADDR6_BYTES + iface_bin
listen_socket.setsockopt(_IPPROTO_IPV6, socket.IPV6_JOIN_GROUP,
_value)
else:
@@ -2179,16 +2207,18 @@
interface,
)
return None
- elif _errno == errno.EINVAL:
+ elif _errno in err_einval:
log.info('Interface of %s does not support multicast, ' 'it is
expected in WSL', interface)
return None
else:
raise
respond_socket = new_socket(
- ip_version=(IPVersion.V6Only if is_v6 else IPVersion.V4Only),
apple_p2p=apple_p2p
+ ip_version=(IPVersion.V6Only if is_v6 else IPVersion.V4Only),
+ apple_p2p=apple_p2p,
+ bind_addr=cast(Tuple[Tuple[str, int, int], int], interface)[0] if
is_v6 else (cast(str, interface),),
)
- log.debug('Configuring socket %d with multicast interface %s',
respond_socket, interface)
+ log.debug('Configuring socket %s with multicast interface %s',
respond_socket, interface)
if is_v6:
respond_socket.setsockopt(_IPPROTO_IPV6, socket.IPV6_MULTICAST_IF,
iface_bin)
else:
@@ -2207,17 +2237,22 @@
if unicast:
listen_socket = None
else:
- listen_socket = new_socket(ip_version=ip_version, apple_p2p=apple_p2p)
+ listen_socket = new_socket(ip_version=ip_version, apple_p2p=apple_p2p,
bind_addr=('',))
- interfaces = normalize_interface_choice(interfaces, ip_version)
+ normalized_interfaces = normalize_interface_choice(interfaces, ip_version)
respond_sockets = []
- for i in interfaces:
+ for i in normalized_interfaces:
if not unicast:
respond_socket = add_multicast_member(cast(socket.socket,
listen_socket), i, apple_p2p=apple_p2p)
else:
- respond_socket = new_socket(port=0, ip_version=ip_version,
apple_p2p=apple_p2p)
+ respond_socket = new_socket(
+ port=0,
+ ip_version=ip_version,
+ apple_p2p=apple_p2p,
+ bind_addr=i[0] if isinstance(i, tuple) else (i,),
+ )
if respond_socket is not None:
respond_sockets.append(respond_socket)
@@ -2256,7 +2291,6 @@
(IPv4 and IPv6) and interface indexes (IPv6 only).
IPv6 notes for non-POSIX systems:
- * IPv6 addresses are not supported, use indexes instead.
* `InterfaceChoice.All` is an alias for `InterfaceChoice.Default`
on Python versions before 3.8.
@@ -2289,6 +2323,7 @@
self._listen_socket, self._respond_sockets = create_sockets(
interfaces, unicast, ip_version, apple_p2p=apple_p2p
)
+ log.debug('Listen socket %s, respond sockets %s', self._listen_socket,
self._respond_sockets)
self.listeners = [] # type: List[RecordUpdateListener]
self.browsers = {} # type: Dict[ServiceListener, ServiceBrowser]
@@ -2308,9 +2343,8 @@
self.listener = Listener(self)
if not unicast:
self.engine.add_reader(self.listener, cast(socket.socket,
self._listen_socket))
- else:
- for s in self._respond_sockets:
- self.engine.add_reader(self.listener, s)
+ for s in self._respond_sockets:
+ self.engine.add_reader(self.listener, s)
self.reaper = Reaper(self)
self.debug = None # type: Optional[DNSOutgoing]
@@ -2818,9 +2852,8 @@
if not self.unicast:
self.engine.del_reader(cast(socket.socket,
self._listen_socket))
cast(socket.socket, self._listen_socket).close()
- else:
- for s in self._respond_sockets:
- self.engine.del_reader(s)
+ for s in self._respond_sockets:
+ self.engine.del_reader(s)
self.engine.join()
# shutdown the rest
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-zeroconf-0.27.1/zeroconf/test.py
new/python-zeroconf-0.28.0/zeroconf/test.py
--- old/python-zeroconf-0.27.1/zeroconf/test.py 2020-06-05 11:09:58.000000000
+0200
+++ new/python-zeroconf-0.28.0/zeroconf/test.py 2020-07-07 13:22:12.000000000
+0200
@@ -1488,19 +1488,44 @@
assert info.addresses == [address, address]
+ info = ServiceInfo(
+ type_,
+ registration_name,
+ 80,
+ 0,
+ 0,
+ desc,
+ "ash-2.local.",
+ parsed_addresses=[address_parsed, address_parsed],
+ )
+ assert info.addresses == [address, address]
+
if socket.has_ipv6 and not os.environ.get('SKIP_IPV6'):
address_v6_parsed = "2001:db8::1"
address_v6 = socket.inet_pton(socket.AF_INET6, address_v6_parsed)
- info = ServiceInfo(
- type_, registration_name, 80, 0, 0, desc, "ash-2.local.",
addresses=[address, address_v6],
- )
- assert info.addresses == [address]
- assert info.addresses_by_version(r.IPVersion.All) == [address,
address_v6]
- assert info.addresses_by_version(r.IPVersion.V4Only) == [address]
- assert info.addresses_by_version(r.IPVersion.V6Only) == [address_v6]
- assert info.parsed_addresses() == [address_parsed, address_v6_parsed]
- assert info.parsed_addresses(r.IPVersion.V4Only) == [address_parsed]
- assert info.parsed_addresses(r.IPVersion.V6Only) == [address_v6_parsed]
+ infos = [
+ ServiceInfo(
+ type_, registration_name, 80, 0, 0, desc, "ash-2.local.",
addresses=[address, address_v6],
+ ),
+ ServiceInfo(
+ type_,
+ registration_name,
+ 80,
+ 0,
+ 0,
+ desc,
+ "ash-2.local.",
+ parsed_addresses=[address_parsed, address_v6_parsed],
+ ),
+ ]
+ for info in infos:
+ assert info.addresses == [address]
+ assert info.addresses_by_version(r.IPVersion.All) == [address,
address_v6]
+ assert info.addresses_by_version(r.IPVersion.V4Only) == [address]
+ assert info.addresses_by_version(r.IPVersion.V6Only) ==
[address_v6]
+ assert info.parsed_addresses() == [address_parsed,
address_v6_parsed]
+ assert info.parsed_addresses(r.IPVersion.V4Only) ==
[address_parsed]
+ assert info.parsed_addresses(r.IPVersion.V6Only) ==
[address_v6_parsed]
def test_ptr_optimization():