Hello community,
here is the log from the commit of package python-zeroconf for
openSUSE:Leap:15.2 checked in at 2020-04-17 13:38:50
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Leap:15.2/python-zeroconf (Old)
and /work/SRC/openSUSE:Leap:15.2/.python-zeroconf.new.2738 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-zeroconf"
Fri Apr 17 13:38:50 2020 rev:19 rq:794800 version:0.25.1
Changes:
--------
--- /work/SRC/openSUSE:Leap:15.2/python-zeroconf/python-zeroconf.changes
2020-03-15 13:35:40.262813397 +0100
+++
/work/SRC/openSUSE:Leap:15.2/.python-zeroconf.new.2738/python-zeroconf.changes
2020-04-17 13:39:11.900294581 +0200
@@ -1,0 +2,11 @@
+Thu Apr 16 08:47:59 UTC 2020 - [email protected]
+
+- version update to 0.25.1
+ * Eliminated 5s hangup when calling Zeroconf.close(), thanks to Erik
Montnemery
+ * Reverted uniqueness assertions when browsing, they caused a regression
+ Backwards incompatible:
+ * Rationalized handling of TXT records.
+- deleted patches
+ - python-zeroconf-disable-some-tests.patch (not needed, replaced by -k 'not
...')
+
+-------------------------------------------------------------------
Old:
----
python-zeroconf-0.24.5.tar.gz
python-zeroconf-disable-some-tests.patch
New:
----
python-zeroconf-0.25.1.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-zeroconf.spec ++++++
--- /var/tmp/diff_new_pack.CBjprZ/_old 2020-04-17 13:39:12.232294830 +0200
+++ /var/tmp/diff_new_pack.CBjprZ/_new 2020-04-17 13:39:12.232294830 +0200
@@ -19,16 +19,15 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
%define skip_python2 1
Name: python-zeroconf
-Version: 0.24.5
+Version: 0.25.1
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
-Patch0: python-zeroconf-disable-some-tests.patch
BuildRequires: %{python_module ifaddr}
-BuildRequires: %{python_module nose}
+BuildRequires: %{python_module pytest}
BuildRequires: %{python_module setuptools}
BuildRequires: fdupes
BuildRequires: python-rpm-macros
@@ -45,7 +44,6 @@
%prep
%setup -q
-%patch0 -p1
%build
%python_build
@@ -56,8 +54,7 @@
%python_expand %fdupes %{buildroot}%{$python_sitelib}
%check
-# tests that do not run in an OBS chroot are disabled via
python-zeroconf-disable-some-tests.patch
-%python_expand PYTHONPATH=%{buildroot}%{$python_sitelib} %python_exec -m
unittest discover -v
+%pytest zeroconf/test.py -k 'not (test_integration_with_listener_ipv6 or
test_launch)'
%files %{python_files}
%doc README.rst
++++++ python-zeroconf-0.24.5.tar.gz -> python-zeroconf-0.25.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-zeroconf-0.24.5/.travis.yml
new/python-zeroconf-0.25.1/.travis.yml
--- old/python-zeroconf-0.24.5/.travis.yml 2020-03-08 00:39:22.000000000
+0100
+++ new/python-zeroconf-0.25.1/.travis.yml 2020-04-14 21:01:53.000000000
+0200
@@ -7,13 +7,13 @@
- "pypy3.5"
- "pypy3"
install:
- - pip install -r requirements-dev.txt
+ - pip install --upgrade -r requirements-dev.txt
# mypy can't be installed on pypy
- if [[ "${TRAVIS_PYTHON_VERSION}" != "pypy"* ]] ; then pip install mypy ;
fi
- if [[ "${TRAVIS_PYTHON_VERSION}" != *"3.5"* &&
"${TRAVIS_PYTHON_VERSION}" != "pypy"* ]] ; then
pip install black ; fi
script:
# no IPv6 support in Travis :(
- - make TEST_ARGS='-a "!IPv6"' ci
+ - SKIP_IPV6=1 make ci
after_success:
- coveralls
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-zeroconf-0.24.5/Makefile
new/python-zeroconf-0.25.1/Makefile
--- old/python-zeroconf-0.24.5/Makefile 2020-03-08 00:39:22.000000000 +0100
+++ new/python-zeroconf-0.25.1/Makefile 2020-04-14 21:01:53.000000000 +0200
@@ -2,7 +2,6 @@
MAX_LINE_LENGTH=110
PYTHON_IMPLEMENTATION:=$(shell python -c "import sys;import
platform;sys.stdout.write(platform.python_implementation())")
PYTHON_VERSION:=$(shell python -c "import sys;sys.stdout.write('%d.%d' %
sys.version_info[:2])")
-TEST_ARGS=
LINT_TARGETS:=flake8
@@ -40,10 +39,10 @@
mypy examples/*.py zeroconf/*.py
test:
- nosetests -v $(TEST_ARGS)
+ pytest -v zeroconf/test.py
test_coverage:
- nosetests -v --with-coverage --cover-package=zeroconf $(TEST_ARGS)
+ pytest -v --cov=zeroconf --cov-branch --cov-report html --cov-report
term-missing zeroconf/test.py
autopep8:
autopep8 --max-line-length=$(MAX_LINE_LENGTH) -i setup.py examples
zeroconf
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-zeroconf-0.24.5/README.rst
new/python-zeroconf-0.25.1/README.rst
--- old/python-zeroconf-0.24.5/README.rst 2020-03-08 00:39:22.000000000
+0100
+++ new/python-zeroconf-0.25.1/README.rst 2020-04-14 21:01:53.000000000
+0200
@@ -134,6 +134,23 @@
Changelog
=========
+0.25.1
+------
+
+* Eliminated 5s hangup when calling Zeroconf.close(), thanks to Erik Montnemery
+
+0.25.0
+------
+
+* Reverted uniqueness assertions when browsing, they caused a regression
+
+Backwards incompatible:
+
+* Rationalized handling of TXT records. Non-bytes values are converted to str
and encoded to bytes
+ using UTF-8 now, None values mean value-less attributes. When receiving TXT
records no decoding
+ is performed now, keys are always bytes and values are either bytes or None
in value-less
+ attributes.
+
0.24.5
------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-zeroconf-0.24.5/requirements-dev.txt
new/python-zeroconf-0.25.1/requirements-dev.txt
--- old/python-zeroconf-0.24.5/requirements-dev.txt 2020-03-08
00:39:22.000000000 +0100
+++ new/python-zeroconf-0.25.1/requirements-dev.txt 2020-04-14
21:01:53.000000000 +0200
@@ -5,5 +5,6 @@
flake8>=3.6.0
flake8-import-order
ifaddr
-nose
pep8-naming!=0.6.0
+pytest
+pytest-cov
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-zeroconf-0.24.5/zeroconf/__init__.py
new/python-zeroconf-0.25.1/zeroconf/__init__.py
--- old/python-zeroconf-0.24.5/zeroconf/__init__.py 2020-03-08
00:39:22.000000000 +0100
+++ new/python-zeroconf-0.25.1/zeroconf/__init__.py 2020-04-14
21:01:53.000000000 +0200
@@ -42,7 +42,7 @@
__author__ = 'Paul Scott-Murphy, William McBrine'
__maintainer__ = 'Jakub Stasiak <[email protected]>'
-__version__ = '0.24.5'
+__version__ = '0.25.1'
__license__ = 'LGPL'
@@ -902,10 +902,6 @@
def add_answer_at_time(self, record: Optional[DNSRecord], now:
Union[float, int]) -> None:
"""Adds an answer if it does not expire by a certain time"""
if record is not None:
-
- if self.is_type_unique(record.type):
- assert record.unique
-
if now == 0 or not record.is_expired(now):
self.answers.append((record, now))
@@ -949,9 +945,6 @@
o All address records (type "A" and "AAAA") named in the SRV rdata.
"""
- if self.is_type_unique(record.type):
- assert record.unique
-
self.additionals.append(record)
def pack(self, format_: Union[bytes, str], value: Any) -> None:
@@ -1210,12 +1203,13 @@
self.readers = {} # type: Dict[socket.socket, Listener]
self.timeout = 5
self.condition = threading.Condition()
+ self.socketpair = socket.socketpair()
self.start()
def run(self) -> None:
while not self.zc.done:
with self.condition:
- rs = self.readers.keys()
+ rs = list(self.readers.keys())
if len(rs) == 0:
# No sockets to manage, but we wait for the timeout
# or addition of a socket
@@ -1223,6 +1217,7 @@
if len(rs) != 0:
try:
+ rs = rs + [self.socketpair[0]]
rr, wr, er = select.select(cast(Sequence[Any], rs), [],
[], self.timeout)
if not self.zc.done:
for socket_ in rr:
@@ -1230,21 +1225,36 @@
if reader:
reader.handle_read(socket_)
+ if self.socketpair[0] in rr:
+ # Clear the socket's buffer
+ self.socketpair[0].recv(128)
+
except (select.error, socket.error) as e:
# If the socket was closed by another thread, during
# shutdown, ignore it and exit
if e.args[0] not in (errno.EBADF, errno.ENOTCONN) or not
self.zc.done:
raise
+ self.socketpair[0].close()
+ self.socketpair[1].close()
+
+ def _notify(self) -> None:
+ self.condition.notify()
+ try:
+ self.socketpair[1].send(b'x')
+ except socket.error:
+ # The socketpair may already be closed during shutdown, ignore it
+ if not self.zc.done:
+ raise
def add_reader(self, reader: 'Listener', socket_: socket.socket) -> None:
with self.condition:
self.readers[socket_] = reader
- self.condition.notify()
+ self._notify()
def del_reader(self, socket_: socket.socket) -> None:
with self.condition:
del self.readers[socket_]
- self.condition.notify()
+ self._notify()
class Listener(QuietLogger):
@@ -1504,9 +1514,28 @@
class ServiceInfo(RecordUpdateListener):
- text = b''
+ """Service information.
+
+ Constructor parameters are as follows:
- """Service information"""
+ * type_: fully qualified service type name
+ * name: fully qualified service name
+ * address: IP address as unsigned short, network byte order (deprecated,
use addresses)
+ * port: port that the service runs on
+ * weight: weight of the service
+ * priority: priority of the service
+ * properties: dictionary of properties (or a bytes object holding the
contents of the `text` field).
+ converted to str and then encoded to bytes using UTF-8. Keys with `None`
values are converted to
+ value-less attributes.
+ * 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
+
+ """
+
+ text = b''
# FIXME(dtantsur): black 19.3b0 produces code that is not valid syntax on
# Python 3.5: https://github.com/python/black/issues/759
@@ -1526,23 +1555,6 @@
*,
addresses: Optional[List[bytes]] = None
) -> None:
- """Create a service description.
-
- type_: fully qualified service type name
- name: fully qualified service name
- address: IP address as unsigned short, network byte order (deprecated,
use addresses)
- port: port that the service runs on
- weight: weight of the service
- priority: priority of the service
- properties: dictionary of properties (or a string holding the
- bytes for the text field)
- 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
- """
-
# Accept both none, or one, but not both.
if address is not None and addresses is not None:
raise TypeError("address and addresses cannot be provided
together")
@@ -1617,6 +1629,13 @@
@property
def properties(self) -> Dict:
+ """If properties were set in the constructor this property returns the
original dictionary
+ of type `Dict[Union[bytes, str], Any]`.
+
+ If properties are coming from the network, after decoding a TXT
record, the keys are always
+ bytes and the values are either bytes, if there was a value, even
empty, or `None`, if there
+ was none. No further decoding is attempted. The type returned is
`Dict[bytes, Optional[bytes]]`.
+ """
return self._properties
def addresses_by_version(self, version: IPVersion) -> List[bytes]:
@@ -1646,20 +1665,12 @@
if isinstance(key, str):
key = key.encode('utf-8')
- if value is None:
- suffix = b''
- elif isinstance(value, str):
- suffix = value.encode('utf-8')
- elif isinstance(value, bytes):
- suffix = value
- elif isinstance(value, int):
- if value:
- suffix = b'true'
- else:
- suffix = b'false'
- else:
- suffix = b''
- list_.append(b'='.join((key, suffix)))
+ record = key
+ if value is not None:
+ if not isinstance(value, bytes):
+ value = str(value).encode('utf-8')
+ record += b'=' + value
+ list_.append(record)
for item in list_:
result = b''.join((result, int2byte(len(item)), item))
self.text = result
@@ -1682,16 +1693,11 @@
for s in strs:
parts = s.split(b'=', 1)
try:
- key, value = parts # type: Tuple[bytes, Union[bool, bytes]]
+ key, value = parts # type: Tuple[bytes, Optional[bytes]]
except ValueError:
# No equals sign at all
key = s
- value = False
- else:
- if value == b'true':
- value = True
- elif value == b'false' or not value:
- value = False
+ value = None
# Only update non-existent properties
if key and result.get(key) is None:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python-zeroconf-0.24.5/zeroconf/test.py
new/python-zeroconf-0.25.1/zeroconf/test.py
--- old/python-zeroconf-0.24.5/zeroconf/test.py 2020-03-08 00:39:22.000000000
+0100
+++ new/python-zeroconf-0.25.1/zeroconf/test.py 2020-04-14 21:01:53.000000000
+0200
@@ -6,6 +6,7 @@
import copy
import logging
+import os
import socket
import struct
import time
@@ -14,8 +15,6 @@
from typing import Dict, Optional # noqa # used in type hints
from typing import cast
-from nose.plugins.attrib import attr
-
import zeroconf as r
from zeroconf import (
DNSHinfo,
@@ -169,17 +168,17 @@
0,
)
parsed = r.DNSIncoming(generated.packet())
- self.assertEqual(len(generated.answers), 1)
- self.assertEqual(len(generated.answers), len(parsed.answers))
+ assert len(generated.answers) == 1
+ assert len(generated.answers) == len(parsed.answers)
def test_match_question(self):
generated = r.DNSOutgoing(r._FLAGS_QR_QUERY)
question = r.DNSQuestion("testname.local.", r._TYPE_SRV, r._CLASS_IN)
generated.add_question(question)
parsed = r.DNSIncoming(generated.packet())
- self.assertEqual(len(generated.questions), 1)
- self.assertEqual(len(generated.questions), len(parsed.questions))
- self.assertEqual(question, parsed.questions[0])
+ assert len(generated.questions) == 1
+ assert len(generated.questions) == len(parsed.questions)
+ assert question == parsed.questions[0]
def test_suppress_answer(self):
query_generated = r.DNSOutgoing(r._FLAGS_QR_QUERY)
@@ -253,8 +252,8 @@
generated.add_additional_answer(DNSHinfo('irrelevant', r._TYPE_HINFO,
0, 0, 'cpu', 'os'))
parsed = r.DNSIncoming(generated.packet())
answer = cast(r.DNSHinfo, parsed.answers[0])
- self.assertEqual(answer.cpu, u'cpu')
- self.assertEqual(answer.os, u'os')
+ assert answer.cpu == u'cpu'
+ assert answer.os == u'os'
generated = r.DNSOutgoing(0)
generated.add_additional_answer(DNSHinfo('irrelevant', r._TYPE_HINFO,
0, 0, 'cpu', 'x' * 257))
@@ -267,28 +266,28 @@
generated = r.DNSOutgoing(r._FLAGS_QR_QUERY)
bytes = generated.packet()
id = bytes[0] << 8 | bytes[1]
- self.assertEqual(id, 0)
+ assert id == 0
def test_query_header_bits(self):
generated = r.DNSOutgoing(r._FLAGS_QR_QUERY)
bytes = generated.packet()
flags = bytes[2] << 8 | bytes[3]
- self.assertEqual(flags, 0x0)
+ assert flags == 0x0
def test_response_header_bits(self):
generated = r.DNSOutgoing(r._FLAGS_QR_RESPONSE)
bytes = generated.packet()
flags = bytes[2] << 8 | bytes[3]
- self.assertEqual(flags, 0x8000)
+ assert flags == 0x8000
def test_numbers(self):
generated = r.DNSOutgoing(r._FLAGS_QR_RESPONSE)
bytes = generated.packet()
(num_questions, num_answers, num_authorities, num_additionals) =
struct.unpack('!4H', bytes[4:12])
- self.assertEqual(num_questions, 0)
- self.assertEqual(num_answers, 0)
- self.assertEqual(num_authorities, 0)
- self.assertEqual(num_additionals, 0)
+ assert num_questions == 0
+ assert num_answers == 0
+ assert num_authorities == 0
+ assert num_additionals == 0
def test_numbers_questions(self):
generated = r.DNSOutgoing(r._FLAGS_QR_RESPONSE)
@@ -297,10 +296,10 @@
generated.add_question(question)
bytes = generated.packet()
(num_questions, num_answers, num_authorities, num_additionals) =
struct.unpack('!4H', bytes[4:12])
- self.assertEqual(num_questions, 10)
- self.assertEqual(num_answers, 0)
- self.assertEqual(num_authorities, 0)
- self.assertEqual(num_additionals, 0)
+ assert num_questions == 10
+ assert num_answers == 0
+ assert num_authorities == 0
+ assert num_additionals == 0
class Names(unittest.TestCase):
@@ -502,7 +501,7 @@
rv.close()
@unittest.skipIf(not socket.has_ipv6, 'Requires IPv6')
- @attr('IPv6')
+ @unittest.skipIf(os.environ.get('SKIP_IPV6'), 'IPv6 tests disabled')
def test_launch_and_close_v4_v6(self):
rv = r.Zeroconf(interfaces=r.InterfaceChoice.All,
ip_version=r.IPVersion.All)
rv.close()
@@ -510,7 +509,7 @@
rv.close()
@unittest.skipIf(not socket.has_ipv6, 'Requires IPv6')
- @attr('IPv6')
+ @unittest.skipIf(os.environ.get('SKIP_IPV6'), 'IPv6 tests disabled')
def test_launch_and_close_v6_only(self):
rv = r.Zeroconf(interfaces=r.InterfaceChoice.All,
ip_version=r.IPVersion.V6Only)
rv.close()
@@ -826,7 +825,7 @@
cache.add(record2)
entry = r.DNSEntry('a', r._TYPE_SOA, r._CLASS_IN)
cached_record = cache.get(entry)
- self.assertEqual(cached_record, record2)
+ assert cached_record == record2
def test_cache_empty_does_not_leak_memory_by_leaving_empty_list(self):
record1 = r.DNSAddress('a', r._TYPE_SOA, r._CLASS_IN, 1, b'a')
@@ -864,6 +863,7 @@
zeroconf_registrar.close()
@unittest.skipIf(not socket.has_ipv6, 'Requires IPv6')
+ @unittest.skipIf(os.environ.get('SKIP_IPV6'), 'IPv6 tests disabled')
def test_integration_with_listener_v6_records(self):
type_ = "_test-srvc-type._tcp.local."
@@ -888,7 +888,7 @@
zeroconf_registrar.close()
@unittest.skipIf(not socket.has_ipv6, 'Requires IPv6')
- @attr('IPv6')
+ @unittest.skipIf(os.environ.get('SKIP_IPV6'), 'IPv6 tests disabled')
def test_integration_with_listener_ipv6(self):
type_ = "_test-srvc-type._tcp.local."
@@ -904,9 +904,9 @@
try:
service_types =
ZeroconfServiceTypes.find(ip_version=r.IPVersion.V6Only, timeout=0.5)
- assert type_ in service_types, service_types
+ assert type_ in service_types
service_types = ZeroconfServiceTypes.find(zc=zeroconf_registrar,
timeout=0.5)
- assert type_ in service_types, service_types
+ assert type_ in service_types
finally:
zeroconf_registrar.close()
@@ -988,7 +988,7 @@
desc = {'path': '/~paulsm/'} # type: Dict
desc.update(properties)
addresses = [socket.inet_aton("10.0.1.2")]
- if socket.has_ipv6:
+ if socket.has_ipv6 and not os.environ.get('SKIP_IPV6'):
addresses.append(socket.inet_pton(socket.AF_INET6, "2001:db8::1"))
info_service = ServiceInfo(
subtype, registration_name, port=80, properties=desc,
server="ash-2.local.", addresses=addresses
@@ -1009,19 +1009,18 @@
# get service info without answer cache
info = zeroconf_browser.get_service_info(type_, registration_name)
assert info is not None
- assert info.properties[b'prop_none'] is False
+ assert info.properties[b'prop_none'] is None
assert info.properties[b'prop_string'] == properties['prop_string']
- assert info.properties[b'prop_float'] is False
+ assert info.properties[b'prop_float'] == b'1.0'
assert info.properties[b'prop_blank'] == properties['prop_blank']
- assert info.properties[b'prop_true'] is True
- assert info.properties[b'prop_false'] is False
+ assert info.properties[b'prop_true'] == b'1'
+ assert info.properties[b'prop_false'] == b'0'
assert info.addresses == addresses[:1] # no V6 by default
- all_addresses = info.addresses_by_version(r.IPVersion.All)
- assert all_addresses == addresses, all_addresses
+ assert info.addresses_by_version(r.IPVersion.All) == addresses
info = zeroconf_browser.get_service_info(subtype,
registration_name)
assert info is not None
- assert info.properties[b'prop_none'] is False
+ assert info.properties[b'prop_none'] is None
# test TXT record update
sublistener = MySubListener()
@@ -1359,7 +1358,7 @@
assert info.addresses == [address, address]
- if socket.has_ipv6:
+ 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, [address, address_v6],
80, 0, 0, desc, "ash-2.local.")