Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-aiohappyeyeballs for
openSUSE:Factory checked in at 2025-03-05 13:39:29
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-aiohappyeyeballs (Old)
and /work/SRC/openSUSE:Factory/.python-aiohappyeyeballs.new.19136 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-aiohappyeyeballs"
Wed Mar 5 13:39:29 2025 rev:6 rq:1250024 version:2.4.8
Changes:
--------
---
/work/SRC/openSUSE:Factory/python-aiohappyeyeballs/python-aiohappyeyeballs.changes
2025-02-11 21:20:47.900765945 +0100
+++
/work/SRC/openSUSE:Factory/.python-aiohappyeyeballs.new.19136/python-aiohappyeyeballs.changes
2025-03-05 13:39:58.627183084 +0100
@@ -1,0 +2,13 @@
+Tue Mar 4 10:07:59 UTC 2025 - Martin Hauke <[email protected]>
+
+- Update to version 2.4.8
+ Bug Fixes
+ * Close runner up sockets in the event there are multiple winners
+- Update to version 2.4.7
+ Bug Fixes
+ * Resolve warnings when running tests.
+ * Instead of raising SystemExit which causes a RuntimeError,
+ mock out SystemExit to a new exception.
+ * Make sure the event loop is closed in tests.
+
+-------------------------------------------------------------------
Old:
----
aiohappyeyeballs-2.4.6.tar.gz
New:
----
aiohappyeyeballs-2.4.8.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-aiohappyeyeballs.spec ++++++
--- /var/tmp/diff_new_pack.X0FVam/_old 2025-03-05 13:39:59.207207416 +0100
+++ /var/tmp/diff_new_pack.X0FVam/_new 2025-03-05 13:39:59.211207584 +0100
@@ -18,7 +18,7 @@
%{?sle15_python_module_pythons}
Name: python-aiohappyeyeballs
-Version: 2.4.6
+Version: 2.4.8
Release: 0
Summary: Happy Eyeballs for asyncio
License: Python-2.0
++++++ aiohappyeyeballs-2.4.6.tar.gz -> aiohappyeyeballs-2.4.8.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/aiohappyeyeballs-2.4.6/PKG-INFO
new/aiohappyeyeballs-2.4.8/PKG-INFO
--- old/aiohappyeyeballs-2.4.6/PKG-INFO 1970-01-01 01:00:00.000000000 +0100
+++ new/aiohappyeyeballs-2.4.8/PKG-INFO 1970-01-01 01:00:00.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 2.3
Name: aiohappyeyeballs
-Version: 2.4.6
+Version: 2.4.8
Summary: Happy Eyeballs for asyncio
License: PSF-2.0
Author: J. Nick Koston
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/aiohappyeyeballs-2.4.6/pyproject.toml
new/aiohappyeyeballs-2.4.8/pyproject.toml
--- old/aiohappyeyeballs-2.4.6/pyproject.toml 1970-01-01 01:00:00.000000000
+0100
+++ new/aiohappyeyeballs-2.4.8/pyproject.toml 1970-01-01 01:00:00.000000000
+0100
@@ -1,6 +1,6 @@
[project]
name = "aiohappyeyeballs"
-version = "2.4.6"
+version = "2.4.8"
description = "Happy Eyeballs for asyncio"
authors = [{ name = "J. Nick Koston", email = "[email protected]" }]
readme = "README.md"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/aiohappyeyeballs-2.4.6/src/aiohappyeyeballs/__init__.py
new/aiohappyeyeballs-2.4.8/src/aiohappyeyeballs/__init__.py
--- old/aiohappyeyeballs-2.4.6/src/aiohappyeyeballs/__init__.py 1970-01-01
01:00:00.000000000 +0100
+++ new/aiohappyeyeballs-2.4.8/src/aiohappyeyeballs/__init__.py 1970-01-01
01:00:00.000000000 +0100
@@ -1,4 +1,4 @@
-__version__ = "2.4.6"
+__version__ = "2.4.8"
from .impl import start_connection
from .types import AddrInfoType
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/aiohappyeyeballs-2.4.6/src/aiohappyeyeballs/_staggered.py
new/aiohappyeyeballs-2.4.8/src/aiohappyeyeballs/_staggered.py
--- old/aiohappyeyeballs-2.4.6/src/aiohappyeyeballs/_staggered.py
1970-01-01 01:00:00.000000000 +0100
+++ new/aiohappyeyeballs-2.4.8/src/aiohappyeyeballs/_staggered.py
1970-01-01 01:00:00.000000000 +0100
@@ -16,6 +16,8 @@
_T = TypeVar("_T")
+RE_RAISE_EXCEPTIONS = (SystemExit, KeyboardInterrupt)
+
def _set_result(wait_next: "asyncio.Future[None]") -> None:
"""Set the result of a future if it is not already done."""
@@ -125,7 +127,7 @@
"""
try:
result = await coro_fn()
- except (SystemExit, KeyboardInterrupt):
+ except RE_RAISE_EXCEPTIONS:
raise
except BaseException as e:
exceptions[this_index] = e
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/aiohappyeyeballs-2.4.6/src/aiohappyeyeballs/impl.py
new/aiohappyeyeballs-2.4.8/src/aiohappyeyeballs/impl.py
--- old/aiohappyeyeballs-2.4.6/src/aiohappyeyeballs/impl.py 1970-01-01
01:00:00.000000000 +0100
+++ new/aiohappyeyeballs-2.4.8/src/aiohappyeyeballs/impl.py 1970-01-01
01:00:00.000000000 +0100
@@ -2,10 +2,11 @@
import asyncio
import collections
+import contextlib
import functools
import itertools
import socket
-from typing import List, Optional, Sequence, Union
+from typing import List, Optional, Sequence, Set, Union
from . import _staggered
from .types import AddrInfoType
@@ -75,15 +76,36 @@
except (RuntimeError, OSError):
continue
else: # using happy eyeballs
- sock, _, _ = await _staggered.staggered_race(
- (
- functools.partial(
- _connect_sock, current_loop, exceptions, addrinfo,
local_addr_infos
- )
- for addrinfo in addr_infos
- ),
- happy_eyeballs_delay,
- )
+ open_sockets: Set[socket.socket] = set()
+ try:
+ sock, _, _ = await _staggered.staggered_race(
+ (
+ functools.partial(
+ _connect_sock,
+ current_loop,
+ exceptions,
+ addrinfo,
+ local_addr_infos,
+ open_sockets,
+ )
+ for addrinfo in addr_infos
+ ),
+ happy_eyeballs_delay,
+ )
+ finally:
+ # If we have a winner, staggered_race will
+ # cancel the other tasks, however there is a
+ # small race window where any of the other tasks
+ # can be done before they are cancelled which
+ # will leave the socket open. To avoid this problem
+ # we pass a set to _connect_sock to keep track of
+ # the open sockets and close them here if there
+ # are any "runner up" sockets.
+ for s in open_sockets:
+ if s is not sock:
+ with contextlib.suppress(OSError):
+ s.close()
+ open_sockets = None # type: ignore[assignment]
if sock is None:
all_exceptions = [exc for sub in exceptions for exc in sub]
@@ -130,14 +152,26 @@
exceptions: List[List[Union[OSError, RuntimeError]]],
addr_info: AddrInfoType,
local_addr_infos: Optional[Sequence[AddrInfoType]] = None,
+ open_sockets: Optional[Set[socket.socket]] = None,
) -> socket.socket:
- """Create, bind and connect one socket."""
+ """
+ Create, bind and connect one socket.
+
+ If open_sockets is passed, add the socket to the set of open sockets.
+ Any failure caught here will remove the socket from the set and close it.
+
+ Callers can use this set to close any sockets that are not the winner
+ of all staggered tasks in the result there are runner up sockets aka
+ multiple winners.
+ """
my_exceptions: List[Union[OSError, RuntimeError]] = []
exceptions.append(my_exceptions)
family, type_, proto, _, address = addr_info
sock = None
try:
sock = socket.socket(family=family, type=type_, proto=proto)
+ if open_sockets is not None:
+ open_sockets.add(sock)
sock.setblocking(False)
if local_addr_infos is not None:
for lfamily, _, _, _, laddr in local_addr_infos:
@@ -165,6 +199,8 @@
except (RuntimeError, OSError) as exc:
my_exceptions.append(exc)
if sock is not None:
+ if open_sockets is not None:
+ open_sockets.remove(sock)
try:
sock.close()
except OSError as e:
@@ -173,6 +209,8 @@
raise
except:
if sock is not None:
+ if open_sockets is not None:
+ open_sockets.remove(sock)
try:
sock.close()
except OSError as e:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/aiohappyeyeballs-2.4.6/tests/test_impl.py
new/aiohappyeyeballs-2.4.8/tests/test_impl.py
--- old/aiohappyeyeballs-2.4.6/tests/test_impl.py 1970-01-01
01:00:00.000000000 +0100
+++ new/aiohappyeyeballs-2.4.8/tests/test_impl.py 1970-01-01
01:00:00.000000000 +0100
@@ -1,12 +1,12 @@
import asyncio
import socket
from types import ModuleType
-from typing import Tuple
+from typing import List, Optional, Sequence, Set, Tuple, Union
from unittest import mock
import pytest
-from aiohappyeyeballs import start_connection
+from aiohappyeyeballs import AddrInfoType, _staggered, impl, start_connection
def mock_socket_module():
@@ -181,6 +181,75 @@
@pytest.mark.asyncio
@patch_socket
+async def test_multiple_winners_cleaned_up(
+ m_socket: ModuleType,
+) -> None:
+ loop = asyncio.get_running_loop()
+ finish = loop.create_future()
+
+ def _socket(*args, **kw):
+ return mock.MagicMock(
+ family=socket.AF_INET,
+ type=socket.SOCK_STREAM,
+ proto=socket.IPPROTO_TCP,
+ fileno=mock.MagicMock(return_value=1),
+ )
+
+ async def _connect_sock(
+ loop: asyncio.AbstractEventLoop,
+ exceptions: List[List[Union[OSError, RuntimeError]]],
+ addr_info: AddrInfoType,
+ local_addr_infos: Optional[Sequence[AddrInfoType]] = None,
+ sockets: Optional[Set[socket.socket]] = None,
+ ) -> socket.socket:
+ await finish
+ sock = _socket()
+ assert sockets is not None
+ sockets.add(sock)
+ return sock
+
+ m_socket.socket = _socket # type: ignore
+ addr_info = [
+ (
+ socket.AF_INET,
+ socket.SOCK_STREAM,
+ socket.IPPROTO_TCP,
+ "",
+ ("107.6.106.82", 80),
+ ),
+ (
+ socket.AF_INET,
+ socket.SOCK_STREAM,
+ socket.IPPROTO_TCP,
+ "",
+ ("107.6.106.83", 80),
+ ),
+ (
+ socket.AF_INET,
+ socket.SOCK_STREAM,
+ socket.IPPROTO_TCP,
+ "",
+ ("107.6.106.84", 80),
+ ),
+ (
+ socket.AF_INET,
+ socket.SOCK_STREAM,
+ socket.IPPROTO_TCP,
+ "",
+ ("107.6.106.85", 80),
+ ),
+ ]
+ with mock.patch.object(impl, "_connect_sock", _connect_sock):
+ task = loop.create_task(
+ start_connection(addr_info, happy_eyeballs_delay=0.0001,
interleave=0)
+ )
+ await asyncio.sleep(0.1)
+ loop.call_soon(finish.set_result, None)
+ await task
+
+
[email protected]
+@patch_socket
async def test_multiple_addr_success_second_one_happy_eyeballs(
m_socket: ModuleType,
) -> None:
@@ -1652,11 +1721,14 @@
@patch_socket
@pytest.mark.asyncio
[email protected](reason="raises RuntimeError: coroutine ignored
GeneratorExit")
async def test_handling_system_exit(
m_socket: ModuleType,
) -> None:
"""Test handling SystemExit."""
+
+ class MockSystemExit(BaseException):
+ """Mock SystemExit."""
+
mock_socket = mock.MagicMock(
family=socket.AF_INET,
type=socket.SOCK_STREAM,
@@ -1674,7 +1746,7 @@
sock: socket.socket, address: Tuple[str, int, int, int]
) -> None:
create_calls.append(address)
- raise SystemExit
+ raise MockSystemExit
m_socket.socket = _socket # type: ignore
ipv6_addr_info = (
@@ -1716,9 +1788,9 @@
),
]
loop = asyncio.get_running_loop()
- with pytest.raises(SystemExit), mock.patch.object(
+ with pytest.raises(MockSystemExit), mock.patch.object(
loop, "sock_connect", _sock_connect
- ):
+ ), mock.patch.object(_staggered, "RE_RAISE_EXCEPTIONS", (MockSystemExit,)):
await start_connection(
addr_info,
happy_eyeballs_delay=0.3,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/aiohappyeyeballs-2.4.6/tests/test_staggered.py
new/aiohappyeyeballs-2.4.8/tests/test_staggered.py
--- old/aiohappyeyeballs-2.4.6/tests/test_staggered.py 1970-01-01
01:00:00.000000000 +0100
+++ new/aiohappyeyeballs-2.4.8/tests/test_staggered.py 1970-01-01
01:00:00.000000000 +0100
@@ -84,3 +84,4 @@
assert excs == [None, None, None, None]
loop.run_until_complete(run())
+ loop.close()