Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-zeroconf for openSUSE:Factory 
checked in at 2021-11-29 17:28:36
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-zeroconf (Old)
 and      /work/SRC/openSUSE:Factory/.python-zeroconf.new.31177 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-zeroconf"

Mon Nov 29 17:28:36 2021 rev:28 rq:934518 version:0.37.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-zeroconf/python-zeroconf.changes  
2021-11-07 00:19:30.208553109 +0100
+++ 
/work/SRC/openSUSE:Factory/.python-zeroconf.new.31177/python-zeroconf.changes   
    2021-12-02 02:25:12.420617506 +0100
@@ -1,0 +2,13 @@
+Mon Nov 29 11:04:03 UTC 2021 - Dirk M??ller <dmuel...@suse.com>
+
+- update to 0.37.0:
+  * Adding a listener that does not inherit from RecordUpdateListener now logs
+    an error
+  * The NotRunningException exception is now thrown when Zeroconf is not
+    running (#1033) @bdraco
+  * Before this change the consumer would get a timeout or an EventLoopBlocked
+    exception when calling ServiceInfo.*request when the instance had already 
been shutdown
+    or had failed to startup.
+  * The EventLoopBlocked exception is now thrown when a coroutine times out  
+
+-------------------------------------------------------------------

Old:
----
  python-zeroconf-0.36.12.obscpio

New:
----
  python-zeroconf-0.37.0.obscpio

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-zeroconf.spec ++++++
--- /var/tmp/diff_new_pack.8cd50M/_old  2021-12-02 02:25:12.880615901 +0100
+++ /var/tmp/diff_new_pack.8cd50M/_new  2021-12-02 02:25:12.884615887 +0100
@@ -19,7 +19,7 @@
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 %define skip_python2 1
 Name:           python-zeroconf
-Version:        0.36.12
+Version:        0.37.0
 Release:        0
 Summary:        Pure Python Multicast DNS Service Discovery Library 
(Bonjour/Avahi compatible)
 License:        LGPL-2.0-only

++++++ _service ++++++
--- /var/tmp/diff_new_pack.8cd50M/_old  2021-12-02 02:25:12.920615761 +0100
+++ /var/tmp/diff_new_pack.8cd50M/_new  2021-12-02 02:25:12.920615761 +0100
@@ -2,8 +2,8 @@
   <service name="obs_scm" mode="disabled">
     <param name="url">https://github.com/jstasiak/python-zeroconf</param>
     <param name="scm">git</param>
-    <param name="revision">0.36.12</param>
-    <param name="version">0.36.12</param>
+    <param name="revision">0.37.0</param>
+    <param name="version">0.37.0</param>
   </service>
   <service name="set_version" mode="disabled"/>
   <service mode="buildtime" name="tar" />

++++++ python-zeroconf-0.36.12.obscpio -> python-zeroconf-0.37.0.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-zeroconf-0.36.12/README.rst 
new/python-zeroconf-0.37.0/README.rst
--- old/python-zeroconf-0.36.12/README.rst      2021-11-05 06:11:04.000000000 
+0100
+++ new/python-zeroconf-0.37.0/README.rst       2021-11-18 21:30:53.000000000 
+0100
@@ -138,10 +138,36 @@
 Changelog
 =========
 
+0.37.0
+======
+
+Technically backwards incompatible:
+
+* Adding a listener that does not inherit from RecordUpdateListener now logs 
an error (#1034) @bdraco
+* The NotRunningException exception is now thrown when Zeroconf is not running 
(#1033) @bdraco
+
+  Before this change the consumer would get a timeout or an EventLoopBlocked
+  exception when calling `ServiceInfo.*request` when the instance had already 
been shutdown
+  or had failed to startup.
+
+* The EventLoopBlocked exception is now thrown when a coroutine times out 
(#1032) @bdraco
+
+  Previously `concurrent.futures.TimeoutError` would have been raised
+  instead. This is never expected to happen during normal operation.
+
+0.36.13
+=======
+
+*  Unavailable interfaces are now skipped during socket bind (#1028) @bdraco
+*  Downgraded incoming corrupt packet logging to debug (#1029) @bdraco
+
+   Warning about network traffic we have no control over is confusing
+   to users as they think there is something wrong with zeroconf
+
 0.36.12
 =======
 
-*  Prevent service lookups from deadlocking if time abruptly moves backwards 
(#1006) @bdraco
+*  Prevented service lookups from deadlocking if time abruptly moves backwards 
(#1006) @bdraco
 
    The typical reason time moves backwards is via an ntp update
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-zeroconf-0.36.12/setup.cfg 
new/python-zeroconf-0.37.0/setup.cfg
--- old/python-zeroconf-0.36.12/setup.cfg       2021-11-05 06:11:04.000000000 
+0100
+++ new/python-zeroconf-0.37.0/setup.cfg        2021-11-18 21:30:53.000000000 
+0100
@@ -1,5 +1,5 @@
 [bumpversion]
-current_version = 0.36.12
+current_version = 0.37.0
 commit = True
 tag = True
 tag_name = {new_version}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-zeroconf-0.36.12/tests/test_asyncio.py 
new/python-zeroconf-0.37.0/tests/test_asyncio.py
--- old/python-zeroconf-0.36.12/tests/test_asyncio.py   2021-11-05 
06:11:04.000000000 +0100
+++ new/python-zeroconf-0.37.0/tests/test_asyncio.py    2021-11-18 
21:30:53.000000000 +0100
@@ -23,6 +23,7 @@
     DNSService,
     DNSAddress,
     DNSText,
+    NotRunningException,
     ServiceStateChange,
     Zeroconf,
     const,
@@ -1091,6 +1092,15 @@
 
 
 @pytest.mark.asyncio
+async def test_async_request_non_running_instance():
+    """Test that the async_request throws when zeroconf is not running."""
+    aiozc = AsyncZeroconf(interfaces=['127.0.0.1'])
+    await aiozc.async_close()
+    with pytest.raises(NotRunningException):
+        await aiozc.async_get_service_info("_notfound.local.", 
"notthere._notfound.local.")
+
+
+@pytest.mark.asyncio
 async def test_legacy_unicast_response(run_isolated):
     """Verify legacy unicast responses include questions and correct id."""
     type_ = "_mservice._tcp.local."
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-zeroconf-0.36.12/tests/test_handlers.py 
new/python-zeroconf-0.37.0/tests/test_handlers.py
--- old/python-zeroconf-0.36.12/tests/test_handlers.py  2021-11-05 
06:11:04.000000000 +0100
+++ new/python-zeroconf-0.37.0/tests/test_handlers.py   2021-11-18 
21:30:53.000000000 +0100
@@ -1538,3 +1538,25 @@
 
     # But the one we have not sent yet shoudl still go out later
     assert info2.dns_pointer() in outgoing_queue.queue[0].answers
+
+
+@pytest.mark.asyncio
+async def 
test_add_listener_warns_when_not_using_record_update_listener(caplog):
+    """Log when a listener is added that is not using RecordUpdateListener as 
a base class."""
+
+    aiozc = AsyncZeroconf(interfaces=['127.0.0.1'])
+    zc: Zeroconf = aiozc.zeroconf
+    updated = []
+
+    class MyListener:
+        """A RecordUpdateListener that does not implement update_records."""
+
+        def async_update_records(self, zc: 'Zeroconf', now: float, records: 
List[r.RecordUpdate]) -> None:
+            """Update multiple records in one shot."""
+            updated.extend(records)
+
+    zc.add_listener(MyListener(), None)
+    await asyncio.sleep(0)  # flush out any call soons
+    assert "listeners passed to async_add_listener must inherit from 
RecordUpdateListener" in caplog.text
+
+    await aiozc.async_close()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-zeroconf-0.36.12/tests/test_logger.py 
new/python-zeroconf-0.37.0/tests/test_logger.py
--- old/python-zeroconf-0.36.12/tests/test_logger.py    2021-11-05 
06:11:04.000000000 +0100
+++ new/python-zeroconf-0.37.0/tests/test_logger.py     2021-11-18 
21:30:53.000000000 +0100
@@ -4,7 +4,7 @@
 """Unit tests for logger.py."""
 
 import logging
-from unittest.mock import patch
+from unittest.mock import call, patch
 from zeroconf._logger import QuietLogger, set_logger_level_if_unset
 
 
@@ -25,6 +25,7 @@
 
 def test_log_warning_once():
     """Test we only log with warning level once."""
+    QuietLogger._seen_logs = {}
     quiet_logger = QuietLogger()
     with patch("zeroconf._logger.log.warning") as mock_log_warning, patch(
         "zeroconf._logger.log.debug"
@@ -45,6 +46,7 @@
 
 def test_log_exception_warning():
     """Test we only log with warning level once."""
+    QuietLogger._seen_logs = {}
     quiet_logger = QuietLogger()
     with patch("zeroconf._logger.log.warning") as mock_log_warning, patch(
         "zeroconf._logger.log.debug"
@@ -63,8 +65,24 @@
     assert mock_log_debug.mock_calls
 
 
+def test_llog_exception_debug():
+    """Test we only log with a trace once."""
+    QuietLogger._seen_logs = {}
+    quiet_logger = QuietLogger()
+    with patch("zeroconf._logger.log.debug") as mock_log_debug:
+        quiet_logger.log_exception_debug("the exception")
+
+    assert mock_log_debug.mock_calls == [call('the exception', exc_info=True)]
+
+    with patch("zeroconf._logger.log.debug") as mock_log_debug:
+        quiet_logger.log_exception_debug("the exception")
+
+    assert mock_log_debug.mock_calls == [call('the exception', exc_info=False)]
+
+
 def test_log_exception_once():
     """Test we only log with warning level once."""
+    QuietLogger._seen_logs = {}
     quiet_logger = QuietLogger()
     exc = Exception()
     with patch("zeroconf._logger.log.warning") as mock_log_warning, patch(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-zeroconf-0.36.12/tests/utils/test_asyncio.py 
new/python-zeroconf-0.37.0/tests/utils/test_asyncio.py
--- old/python-zeroconf-0.36.12/tests/utils/test_asyncio.py     2021-11-05 
06:11:04.000000000 +0100
+++ new/python-zeroconf-0.37.0/tests/utils/test_asyncio.py      2021-11-18 
21:30:53.000000000 +0100
@@ -13,6 +13,7 @@
 
 import pytest
 
+from zeroconf import EventLoopBlocked
 from zeroconf._core import _CLOSE_TIMEOUT
 from zeroconf._utils import asyncio as aioutils
 from zeroconf.const import _LOADED_SYSTEM_TIMEOUT
@@ -112,3 +113,14 @@
     assert (
         aioutils._TASK_AWAIT_TIMEOUT + aioutils._GET_ALL_TASKS_TIMEOUT + 
aioutils._WAIT_FOR_LOOP_TASKS_TIMEOUT
     ) < 1 + _CLOSE_TIMEOUT + _LOADED_SYSTEM_TIMEOUT
+
+
+async def test_run_coro_with_timeout() -> None:
+    """Test running a coroutine with a timeout raises EventLoopBlocked."""
+    loop = asyncio.get_event_loop()
+
+    def _run_in_loop():
+        aioutils.run_coro_with_timeout(asyncio.sleep(0.3), loop, 0.1)
+
+    with pytest.raises(EventLoopBlocked), patch.object(aioutils, 
"_LOADED_SYSTEM_TIMEOUT", 0.0):
+        await loop.run_in_executor(None, _run_in_loop)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-zeroconf-0.36.12/tests/utils/test_net.py 
new/python-zeroconf-0.37.0/tests/utils/test_net.py
--- old/python-zeroconf-0.36.12/tests/utils/test_net.py 2021-11-05 
06:11:04.000000000 +0100
+++ new/python-zeroconf-0.37.0/tests/utils/test_net.py  2021-11-18 
21:30:53.000000000 +0100
@@ -3,7 +3,7 @@
 
 
 """Unit tests for zeroconf._utils.net."""
-from unittest.mock import Mock, patch
+from unittest.mock import MagicMock, Mock, patch
 
 import errno
 import ifaddr
@@ -198,3 +198,26 @@
     # No error should return True
     with patch("socket.socket.setsockopt"):
         assert netutils.add_multicast_member(sock, interface) is True
+
+
+def test_bind_raises_skips_address():
+    """Test bind failing in new_socket returns None on EADDRNOTAVAIL."""
+    err = errno.EADDRNOTAVAIL
+
+    def _mock_socket(*args, **kwargs):
+        sock = MagicMock()
+        sock.bind = MagicMock(side_effect=OSError(err, "Error: 
{}".format(err)))
+        return sock
+
+    with patch("socket.socket", _mock_socket):
+        assert netutils.new_socket(("0.0.0.0", 0)) is None
+
+    err = errno.EAGAIN
+    with pytest.raises(OSError), patch("socket.socket", _mock_socket):
+        netutils.new_socket(("0.0.0.0", 0))
+
+
+def test_new_respond_socket_new_socket_returns_none():
+    """Test new_respond_socket returns None if new_socket returns None."""
+    with patch.object(netutils, "new_socket", return_value=None):
+        assert netutils.new_respond_socket(("0.0.0.0", 0)) is None
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-zeroconf-0.36.12/zeroconf/__init__.py 
new/python-zeroconf-0.37.0/zeroconf/__init__.py
--- old/python-zeroconf-0.36.12/zeroconf/__init__.py    2021-11-05 
06:11:04.000000000 +0100
+++ new/python-zeroconf-0.37.0/zeroconf/__init__.py     2021-11-18 
21:30:53.000000000 +0100
@@ -23,7 +23,7 @@
 import sys
 
 from ._cache import DNSCache  # noqa # import needed for backwards compat
-from ._core import Zeroconf  # noqa # import needed for backwards compat
+from ._core import Zeroconf
 from ._dns import (  # noqa # import needed for backwards compat
     DNSAddress,
     DNSEntry,
@@ -36,16 +36,18 @@
     DNSText,
     DNSQuestionType,
 )
-from ._logger import QuietLogger, log  # noqa # import needed for backwards 
compat
-from ._exceptions import (  # noqa # import needed for backwards compat
+from ._exceptions import (
     AbstractMethodException,
     BadTypeInNameException,
     Error,
+    EventLoopBlocked,
     IncomingDecodeError,
     NamePartTooLongException,
     NonUniqueNameException,
+    NotRunningException,
     ServiceNameAlreadyRegistered,
 )
+from ._logger import QuietLogger, log  # noqa # import needed for backwards 
compat
 from ._protocol.incoming import DNSIncoming  # noqa # import needed for 
backwards compat
 from ._protocol.outgoing import DNSOutgoing  # noqa # import needed for 
backwards compat
 from ._services import (  # noqa # import needed for backwards compat
@@ -54,16 +56,14 @@
     ServiceListener,
     ServiceStateChange,
 )
-from ._services.browser import (  # noqa # import needed for backwards compat
-    ServiceBrowser,
-)
+from ._services.browser import ServiceBrowser
 from ._services.info import (  # noqa # import needed for backwards compat
     instance_name_from_service_info,
     ServiceInfo,
 )
 from ._services.registry import ServiceRegistry  # noqa # import needed for 
backwards compat
 from ._services.types import ZeroconfServiceTypes
-from ._updates import RecordUpdate, RecordUpdateListener  # noqa # import 
needed for backwards compat
+from ._updates import RecordUpdate, RecordUpdateListener
 from ._utils.name import service_type_name  # noqa # import needed for 
backwards compat
 from ._utils.net import (  # noqa # import needed for backwards compat
     add_multicast_member,
@@ -79,22 +79,34 @@
 
 __author__ = 'Paul Scott-Murphy, William McBrine'
 __maintainer__ = 'Jakub Stasiak <ja...@stasiak.at>'
-__version__ = '0.36.12'
+__version__ = '0.37.0'
 __license__ = 'LGPL'
 
 
 __all__ = [
     "__version__",
-    "DNSQuestionType",
     "Zeroconf",
     "ServiceInfo",
     "ServiceBrowser",
     "ServiceListener",
-    "Error",
+    "DNSQuestionType",
     "InterfaceChoice",
     "ServiceStateChange",
     "IPVersion",
     "ZeroconfServiceTypes",
+    "RecordUpdate",
+    "RecordUpdateListener",
+    "current_time_millis",
+    # Exceptions
+    "Error",
+    "AbstractMethodException",
+    "BadTypeInNameException",
+    "EventLoopBlocked",
+    "IncomingDecodeError",
+    "NamePartTooLongException",
+    "NonUniqueNameException",
+    "NotRunningException",
+    "ServiceNameAlreadyRegistered",
 ]
 
 if sys.version_info <= (3, 6):  # pragma: no cover
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-zeroconf-0.36.12/zeroconf/_core.py 
new/python-zeroconf-0.37.0/zeroconf/_core.py
--- old/python-zeroconf-0.36.12/zeroconf/_core.py       2021-11-05 
06:11:04.000000000 +0100
+++ new/python-zeroconf-0.37.0/zeroconf/_core.py        2021-11-18 
21:30:53.000000000 +0100
@@ -32,7 +32,7 @@
 
 from ._cache import DNSCache
 from ._dns import DNSQuestion, DNSQuestionType
-from ._exceptions import NonUniqueNameException
+from ._exceptions import NonUniqueNameException, NotRunningException
 from ._handlers import (
     MulticastOutgoingQueue,
     QueryHandler,
@@ -80,6 +80,7 @@
     _MDNS_PORT,
     _ONE_SECOND,
     _REGISTER_TIME,
+    _STARTUP_TIMEOUT,
     _TYPE_PTR,
     _UNREGISTER_TIME,
 )
@@ -118,15 +119,15 @@
         self.protocols: List[AsyncListener] = []
         self.readers: List[asyncio.DatagramTransport] = []
         self.senders: List[asyncio.DatagramTransport] = []
+        self.running_event: Optional[asyncio.Event] = None
         self._listen_socket = listen_socket
         self._respond_sockets = respond_sockets
         self._cleanup_timer: Optional[asyncio.TimerHandle] = None
-        self._running_event: Optional[asyncio.Event] = None
 
     def setup(self, loop: asyncio.AbstractEventLoop, loop_thread_ready: 
Optional[threading.Event]) -> None:
         """Set up the instance."""
         self.loop = loop
-        self._running_event = asyncio.Event()
+        self.running_event = asyncio.Event()
         self.loop.create_task(self._async_setup(loop_thread_ready))
 
     async def _async_setup(self, loop_thread_ready: Optional[threading.Event]) 
-> None:
@@ -136,16 +137,11 @@
             millis_to_seconds(_CACHE_CLEANUP_INTERVAL), 
self._async_cache_cleanup
         )
         await self._async_create_endpoints()
-        assert self._running_event is not None
-        self._running_event.set()
+        assert self.running_event is not None
+        self.running_event.set()
         if loop_thread_ready:
             loop_thread_ready.set()
 
-    async def async_wait_for_start(self) -> None:
-        """Wait for start up."""
-        assert self._running_event is not None
-        await self._running_event.wait()
-
     async def _async_create_endpoints(self) -> None:
         """Create endpoints to send and receive."""
         assert self.loop is not None
@@ -192,7 +188,12 @@
             transport.close()
 
     def close(self) -> None:
-        """Close from sync context."""
+        """Close from sync context.
+
+        While it is not expected during normal operation,
+        this function may raise EventLoopBlocked if the underlying
+        call to `_async_close` cannot be completed.
+        """
         assert self.loop is not None
         # Guard against Zeroconf.close() being called from the eventloop
         if get_running_loop() == self.loop:
@@ -490,8 +491,17 @@
         loop_thread_ready.wait()
 
     async def async_wait_for_start(self) -> None:
-        """Wait for start up."""
-        await self.engine.async_wait_for_start()
+        """Wait for start up for actions that require a running Zeroconf 
instance.
+
+        Throws NotRunningException if the instance is not running or could
+        not be started.
+        """
+        if self.done:  # If the instance was shutdown from under us, raise 
immediately
+            raise NotRunningException
+        assert self.engine.running_event is not None
+        await wait_event_or_timeout(self.engine.running_event, 
timeout=_STARTUP_TIMEOUT)
+        if not self.engine.running_event.is_set() or self.done:
+            raise NotRunningException
 
     @property
     def listeners(self) -> List[RecordUpdateListener]:
@@ -554,7 +564,12 @@
         service.  The name of the service may be changed if needed to make
         it unique on the network. Additionally multiple cooperating responders
         can register the same service on the network for resilience
-        (if you want this behavior set `cooperating_responders` to `True`)."""
+        (if you want this behavior set `cooperating_responders` to `True`).
+
+        While it is not expected during normal operation,
+        this function may raise EventLoopBlocked if the underlying
+        call to `register_service` cannot be completed.
+        """
         assert self.loop is not None
         run_coro_with_timeout(
             await_awaitable(
@@ -591,7 +606,12 @@
     def update_service(self, info: ServiceInfo) -> None:
         """Registers service information to the network with a default TTL.
         Zeroconf will then respond to requests for information for that
-        service."""
+        service.
+
+        While it is not expected during normal operation,
+        this function may raise EventLoopBlocked if the underlying
+        call to `async_update_service` cannot be completed.
+        """
         assert self.loop is not None
         run_coro_with_timeout(
             await_awaitable(self.async_update_service(info)), self.loop, 
_REGISTER_TIME * _REGISTER_BROADCASTS
@@ -662,7 +682,12 @@
                 out.add_answer_at_time(dns_address, 0)
 
     def unregister_service(self, info: ServiceInfo) -> None:
-        """Unregister a service."""
+        """Unregister a service.
+
+        While it is not expected during normal operation,
+        this function may raise EventLoopBlocked if the underlying
+        call to `async_unregister_service` cannot be completed.
+        """
         assert self.loop is not None
         run_coro_with_timeout(
             self.async_unregister_service(info), self.loop, _UNREGISTER_TIME * 
_REGISTER_BROADCASTS
@@ -708,7 +733,12 @@
             self.async_send(out)
 
     def unregister_all_services(self) -> None:
-        """Unregister all registered services."""
+        """Unregister all registered services.
+
+        While it is not expected during normal operation,
+        this function may raise EventLoopBlocked if the underlying
+        call to `async_unregister_all_services` cannot be completed.
+        """
         assert self.loop is not None
         run_coro_with_timeout(
             self.async_unregister_all_services(), self.loop, _UNREGISTER_TIME 
* _REGISTER_BROADCASTS
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-zeroconf-0.36.12/zeroconf/_exceptions.py 
new/python-zeroconf-0.37.0/zeroconf/_exceptions.py
--- old/python-zeroconf-0.36.12/zeroconf/_exceptions.py 2021-11-05 
06:11:04.000000000 +0100
+++ new/python-zeroconf-0.37.0/zeroconf/_exceptions.py  2021-11-18 
21:30:53.000000000 +0100
@@ -22,28 +22,46 @@
 
 
 class Error(Exception):
-    pass
+    """Base class for all zeroconf exceptions."""
 
 
 class IncomingDecodeError(Error):
-    pass
+    """Exception when there is invalid data in an incoming packet."""
 
 
 class NonUniqueNameException(Error):
-    pass
+    """Exception when the name is already registered."""
 
 
 class NamePartTooLongException(Error):
-    pass
+    """Exception when the name is too long."""
 
 
 class AbstractMethodException(Error):
-    pass
+    """Exception when a required method is not implemented."""
 
 
 class BadTypeInNameException(Error):
-    pass
+    """Exception when the type in a name is invalid."""
 
 
 class ServiceNameAlreadyRegistered(Error):
-    pass
+    """Exception when a service name is already registered."""
+
+
+class EventLoopBlocked(Error):
+    """Exception when the event loop is blocked.
+
+    This exception is never expected to be thrown
+    during normal operation. It should only happen
+    when the cpu is maxed out or there is something blocking
+    the event loop.
+    """
+
+
+class NotRunningException(Error):
+    """Exception when an action is called with a zeroconf instance that is not 
running.
+
+    The instance may not be running because it was already shutdown
+    or startup has failed in some unexpected way.
+    """
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-zeroconf-0.36.12/zeroconf/_handlers.py 
new/python-zeroconf-0.37.0/zeroconf/_handlers.py
--- old/python-zeroconf-0.36.12/zeroconf/_handlers.py   2021-11-05 
06:11:04.000000000 +0100
+++ new/python-zeroconf-0.37.0/zeroconf/_handlers.py    2021-11-18 
21:30:53.000000000 +0100
@@ -492,6 +492,12 @@
 
         This function is not threadsafe and must be called in the eventloop.
         """
+        if not isinstance(listener, RecordUpdateListener):
+            log.error(
+                "listeners passed to async_add_listener must inherit from 
RecordUpdateListener;"
+                " In the future this will fail"
+            )
+
         self.listeners.append(listener)
 
         if question is None:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-zeroconf-0.36.12/zeroconf/_logger.py 
new/python-zeroconf-0.37.0/zeroconf/_logger.py
--- old/python-zeroconf-0.36.12/zeroconf/_logger.py     2021-11-05 
06:11:04.000000000 +0100
+++ new/python-zeroconf-0.37.0/zeroconf/_logger.py      2021-11-18 
21:30:53.000000000 +0100
@@ -52,6 +52,20 @@
         logger(*(logger_data or ['Exception occurred']), exc_info=True)
 
     @classmethod
+    def log_exception_debug(cls, *logger_data: Any) -> None:
+        log_exc_info = False
+        exc_info = sys.exc_info()
+        exc_str = str(exc_info[1])
+        import pprint
+
+        pprint.pprint(cls._seen_logs)
+        if exc_str not in cls._seen_logs:
+            # log the trace only on the first time
+            cls._seen_logs[exc_str] = exc_info
+            log_exc_info = True
+        log.debug(*(logger_data or ['Exception occurred']), 
exc_info=log_exc_info)
+
+    @classmethod
     def log_warning_once(cls, *args: Any) -> None:
         msg_str = args[0]
         if msg_str not in cls._seen_logs:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python-zeroconf-0.36.12/zeroconf/_protocol/incoming.py 
new/python-zeroconf-0.37.0/zeroconf/_protocol/incoming.py
--- old/python-zeroconf-0.36.12/zeroconf/_protocol/incoming.py  2021-11-05 
06:11:04.000000000 +0100
+++ new/python-zeroconf-0.37.0/zeroconf/_protocol/incoming.py   2021-11-18 
21:30:53.000000000 +0100
@@ -110,7 +110,7 @@
         try:
             parser_call()
         except DECODE_EXCEPTIONS:
-            self.log_exception_warning(
+            self.log_exception_debug(
                 'Received invalid packet from %s at offset %d while unpacking 
%r',
                 self.source,
                 self.offset,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-zeroconf-0.36.12/zeroconf/_services/info.py 
new/python-zeroconf-0.37.0/zeroconf/_services/info.py
--- old/python-zeroconf-0.36.12/zeroconf/_services/info.py      2021-11-05 
06:11:04.000000000 +0100
+++ new/python-zeroconf-0.37.0/zeroconf/_services/info.py       2021-11-18 
21:30:53.000000000 +0100
@@ -451,6 +451,10 @@
     ) -> bool:
         """Returns true if the service could be discovered on the
         network, and updates this object with details discovered.
+
+        While it is not expected during normal operation,
+        this function may raise EventLoopBlocked if the underlying
+        call to `async_request` cannot be completed.
         """
         assert zc.loop is not None and zc.loop.is_running()
         if zc.loop == get_running_loop():
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-zeroconf-0.36.12/zeroconf/_updates.py 
new/python-zeroconf-0.37.0/zeroconf/_updates.py
--- old/python-zeroconf-0.36.12/zeroconf/_updates.py    2021-11-05 
06:11:04.000000000 +0100
+++ new/python-zeroconf-0.37.0/zeroconf/_updates.py     2021-11-18 
21:30:53.000000000 +0100
@@ -36,6 +36,12 @@
 
 
 class RecordUpdateListener:
+    """Base call for all record listeners.
+
+    All listeners passed to async_add_listener should use RecordUpdateListener
+    as a base class. In the future it will be required.
+    """
+
     def update_record(  # pylint: disable=no-self-use
         self, zc: 'Zeroconf', now: float, record: DNSRecord
     ) -> None:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-zeroconf-0.36.12/zeroconf/_utils/asyncio.py 
new/python-zeroconf-0.37.0/zeroconf/_utils/asyncio.py
--- old/python-zeroconf-0.36.12/zeroconf/_utils/asyncio.py      2021-11-05 
06:11:04.000000000 +0100
+++ new/python-zeroconf-0.37.0/zeroconf/_utils/asyncio.py       2021-11-18 
21:30:53.000000000 +0100
@@ -21,11 +21,13 @@
 """
 
 import asyncio
+import concurrent.futures
 import contextlib
 import queue
 from typing import Any, Awaitable, Coroutine, List, Optional, Set, cast
 
 from .time import millis_to_seconds
+from .._exceptions import EventLoopBlocked
 from ..const import _LOADED_SYSTEM_TIMEOUT
 
 # The combined timeouts should be lower than _CLOSE_TIMEOUT + 
_WAIT_FOR_LOOP_TASKS_TIMEOUT
@@ -91,10 +93,22 @@
 
 
 def run_coro_with_timeout(aw: Coroutine, loop: asyncio.AbstractEventLoop, 
timeout: float) -> Any:
-    """Run a coroutine with a timeout."""
-    return asyncio.run_coroutine_threadsafe(aw, loop).result(
-        millis_to_seconds(timeout) + _LOADED_SYSTEM_TIMEOUT
-    )
+    """Run a coroutine with a timeout.
+
+    The timeout should only be used as a safeguard to prevent
+    the program from blocking forever. The timeout should
+    never be expected to be reached during normal operation.
+
+    While not expected during normal operations, the
+    function raises `EventLoopBlocked` if the coroutine takes
+    longer to complete than the timeout.
+    """
+    try:
+        return asyncio.run_coroutine_threadsafe(aw, loop).result(
+            millis_to_seconds(timeout) + _LOADED_SYSTEM_TIMEOUT
+        )
+    except concurrent.futures.TimeoutError as ex:
+        raise EventLoopBlocked from ex
 
 
 def shutdown_loop(loop: asyncio.AbstractEventLoop) -> None:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-zeroconf-0.36.12/zeroconf/_utils/net.py 
new/python-zeroconf-0.37.0/zeroconf/_utils/net.py
--- old/python-zeroconf-0.36.12/zeroconf/_utils/net.py  2021-11-05 
06:11:04.000000000 +0100
+++ new/python-zeroconf-0.37.0/zeroconf/_utils/net.py   2021-11-18 
21:30:53.000000000 +0100
@@ -218,7 +218,7 @@
     port: int = _MDNS_PORT,
     ip_version: IPVersion = IPVersion.V4Only,
     apple_p2p: bool = False,
-) -> socket.socket:
+) -> Optional[socket.socket]:
     log.debug(
         'Creating new socket with port %s, ip_version %s, apple_p2p %s and 
bind_addr %r',
         port,
@@ -243,7 +243,17 @@
         # 
https://opensource.apple.com/source/xnu/xnu-4570.41.2/bsd/sys/socket.h
         s.setsockopt(socket.SOL_SOCKET, 0x1104, 1)
 
-    s.bind((bind_addr[0], port, *bind_addr[1:]))
+    bind_tup = (bind_addr[0], port, *bind_addr[1:])
+    try:
+        s.bind(bind_tup)
+    except OSError as ex:
+        if ex.errno == errno.EADDRNOTAVAIL:
+            log.warning(
+                'Address not available when binding to %s, ' 'it is expected 
to happen on some systems',
+                bind_tup,
+            )
+            return None
+        raise
     log.debug('Created socket %s', s)
     return s
 
@@ -323,6 +333,8 @@
         apple_p2p=apple_p2p,
         bind_addr=cast(Tuple[Tuple[str, int, int], int], interface)[0] if 
is_v6 else (cast(str, interface),),
     )
+    if not respond_socket:
+        return None
     log.debug('Configuring socket %s with multicast interface %s', 
respond_socket, interface)
     if is_v6:
         iface_bin = struct.pack('@I', cast(int, interface[1]))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-zeroconf-0.36.12/zeroconf/_utils/time.py 
new/python-zeroconf-0.37.0/zeroconf/_utils/time.py
--- old/python-zeroconf-0.36.12/zeroconf/_utils/time.py 2021-11-05 
06:11:04.000000000 +0100
+++ new/python-zeroconf-0.37.0/zeroconf/_utils/time.py  2021-11-18 
21:30:53.000000000 +0100
@@ -25,7 +25,11 @@
 
 
 def current_time_millis() -> float:
-    """Current system time in milliseconds"""
+    """Current time in milliseconds.
+
+    The current implemention uses `time.monotonic`
+    but may change in the future.
+    """
     return time.monotonic() * 1000
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-zeroconf-0.36.12/zeroconf/asyncio.py 
new/python-zeroconf-0.37.0/zeroconf/asyncio.py
--- old/python-zeroconf-0.36.12/zeroconf/asyncio.py     2021-11-05 
06:11:04.000000000 +0100
+++ new/python-zeroconf-0.37.0/zeroconf/asyncio.py      2021-11-18 
21:30:53.000000000 +0100
@@ -218,8 +218,9 @@
     async def async_close(self) -> None:
         """Ends the background threads, and prevent this instance from
         servicing further queries."""
-        with contextlib.suppress(asyncio.TimeoutError):
-            await asyncio.wait_for(self.zeroconf.async_wait_for_start(), 
timeout=1)
+        if not self.zeroconf.done:
+            with contextlib.suppress(asyncio.TimeoutError):
+                await asyncio.wait_for(self.zeroconf.async_wait_for_start(), 
timeout=1)
         await self.async_remove_all_service_listeners()
         await self.async_unregister_all_services()
         await self.zeroconf._async_close()  # pylint: disable=protected-access
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-zeroconf-0.36.12/zeroconf/const.py 
new/python-zeroconf-0.37.0/zeroconf/const.py
--- old/python-zeroconf-0.36.12/zeroconf/const.py       2021-11-05 
06:11:04.000000000 +0100
+++ new/python-zeroconf-0.37.0/zeroconf/const.py        2021-11-18 
21:30:53.000000000 +0100
@@ -34,6 +34,7 @@
 _BROWSER_BACKOFF_LIMIT = 3600  # s
 _CACHE_CLEANUP_INTERVAL = 10000  # ms
 _LOADED_SYSTEM_TIMEOUT = 10  # s
+_STARTUP_TIMEOUT = 9  # s must be lower than _LOADED_SYSTEM_TIMEOUT
 _ONE_SECOND = 1000  # ms
 
 # If the system is loaded or the event

++++++ python-zeroconf.obsinfo ++++++
--- /var/tmp/diff_new_pack.8cd50M/_old  2021-12-02 02:25:13.104615120 +0100
+++ /var/tmp/diff_new_pack.8cd50M/_new  2021-12-02 02:25:13.104615120 +0100
@@ -1,5 +1,5 @@
 name: python-zeroconf
-version: 0.36.12
-mtime: 1636089064
-commit: 8b0dc48ed42d8edc78750122eb5685a50c3cdc11
+version: 0.37.0
+mtime: 1637267453
+commit: 2996e642f6b1abba1dbb8242ccca4cd4b96696f6
 

Reply via email to