Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-aiodns for openSUSE:Factory checked in at 2025-12-10 15:32:38 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-aiodns (Old) and /work/SRC/openSUSE:Factory/.python-aiodns.new.1939 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-aiodns" Wed Dec 10 15:32:38 2025 rev:12 rq:1321786 version:3.6.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-aiodns/python-aiodns.changes 2025-07-18 15:58:15.285780502 +0200 +++ /work/SRC/openSUSE:Factory/.python-aiodns.new.1939/python-aiodns.changes 2025-12-10 15:33:03.892664113 +0100 @@ -1,0 +2,23 @@ +Tue Dec 9 11:19:10 UTC 2025 - John Paul Adrian Glaubitz <[email protected]> + +- Update to 3.6.0 + * Fix resolver garbage collection during pending queries (#211) + * Prevents resolver from being garbage collected while queries are in progress + * Socket callback optimizations (#172) + * Improved performance for socket state handling + * Fixed RTD links (#176) + * Added Python 3.14 to the CI (#212) + * Updated dependencies + * Bumped pycares from 4.9.0 to 4.11.0 (#186, #194) + * Bumped pytest-asyncio from 1.0.0 to 1.2.0 (#181, #196) + * Bumped pytest-cov from 6.2.1 to 7.0.0 (#193) + * Bumped pytest from 8.4.0 to 8.4.2 (#171, #190) + * Bumped mypy from 1.16.0 to 1.19.0 (#170, #179, #185, #195, #197, #207) + * Bumped uvloop from 0.21.0 to 0.22.1 (#202) + * Bumped winloop from 0.1.8 to 0.3.1 (#182, #183, #184, #187, #200, #201, #203) + * Bumped actions/setup-python from 5 to 6 (#199) + * Bumped actions/checkout from 4 to 6 (#188, #208) + * Bumped actions/upload-artifact from 4 to 5 (#204) + * Bumped actions/download-artifact from 4.3.0 to 6.0.0 (#205) + +------------------------------------------------------------------- Old: ---- aiodns-3.5.0.tar.gz New: ---- aiodns-3.6.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-aiodns.spec ++++++ --- /var/tmp/diff_new_pack.wLOBHA/_old 2025-12-10 15:33:04.900706614 +0100 +++ /var/tmp/diff_new_pack.wLOBHA/_new 2025-12-10 15:33:04.900706614 +0100 @@ -20,7 +20,7 @@ %bcond_with tests %{?sle15_python_module_pythons} Name: python-aiodns -Version: 3.5.0 +Version: 3.6.0 Release: 0 Summary: Simple DNS resolver for asyncio License: MIT ++++++ aiodns-3.5.0.tar.gz -> aiodns-3.6.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aiodns-3.5.0/.github/workflows/ci.yml new/aiodns-3.6.0/.github/workflows/ci.yml --- old/aiodns-3.5.0/.github/workflows/ci.yml 2025-06-13 18:19:58.000000000 +0200 +++ new/aiodns-3.6.0/.github/workflows/ci.yml 2025-12-05 18:44:54.000000000 +0100 @@ -15,9 +15,9 @@ timeout-minutes: 5 steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: 3.11 - name: Run pre-commit @@ -29,9 +29,9 @@ timeout-minutes: 5 steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: 3.11 cache: 'pip' @@ -59,7 +59,7 @@ strategy: matrix: os: [ubuntu-latest, windows-latest, macos-latest] - python-version: [ '3.9', '3.10', '3.11', '3.12', '3.13' ] + python-version: [ '3.9', '3.10', '3.11', '3.12', '3.13', '3.14' ] exclude: - os: macos-latest python-version: 3.9 @@ -69,9 +69,9 @@ timeout-minutes: 15 steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} cache: 'pip' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aiodns-3.5.0/.github/workflows/release-wheels.yml new/aiodns-3.6.0/.github/workflows/release-wheels.yml --- old/aiodns-3.5.0/.github/workflows/release-wheels.yml 2025-06-13 18:19:58.000000000 +0200 +++ new/aiodns-3.6.0/.github/workflows/release-wheels.yml 2025-12-05 18:44:54.000000000 +0100 @@ -11,15 +11,15 @@ runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - uses: actions/checkout@v6 + - uses: actions/setup-python@v6 name: Install Python with: python-version: '3.13' - run: pip install setuptools wheel - name: Build wheels run: python setup.py bdist_wheel - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v5 with: path: dist/*.whl name: artifact-wheels @@ -28,15 +28,15 @@ name: Build source distribution runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - uses: actions/checkout@v6 + - uses: actions/setup-python@v6 name: Install Python with: python-version: '3.13' - run: pip install setuptools - name: Build sdist run: python setup.py sdist - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v5 with: path: dist/*.tar.gz name: artifact-sdist @@ -54,7 +54,7 @@ # upload to PyPI when a GitHub Release is created if: github.event_name == 'release' && github.event.action == 'published' steps: - - uses: actions/[email protected] + - uses: actions/[email protected] with: pattern: artifact-* path: dist diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aiodns-3.5.0/ChangeLog new/aiodns-3.6.0/ChangeLog --- old/aiodns-3.5.0/ChangeLog 2025-06-13 18:19:58.000000000 +0200 +++ new/aiodns-3.6.0/ChangeLog 2025-12-05 18:44:54.000000000 +0100 @@ -1,3 +1,24 @@ +3.6.0 +===== +- Fix resolver garbage collection during pending queries (#211) + - Prevents resolver from being garbage collected while queries are in progress +- Socket callback optimizations (#172) + - Improved performance for socket state handling +- Fixed RTD links (#176) +- Added Python 3.14 to the CI (#212) +- Updated dependencies + - Bumped pycares from 4.9.0 to 4.11.0 (#186, #194) + - Bumped pytest-asyncio from 1.0.0 to 1.2.0 (#181, #196) + - Bumped pytest-cov from 6.2.1 to 7.0.0 (#193) + - Bumped pytest from 8.4.0 to 8.4.2 (#171, #190) + - Bumped mypy from 1.16.0 to 1.19.0 (#170, #179, #185, #195, #197, #207) + - Bumped uvloop from 0.21.0 to 0.22.1 (#202) + - Bumped winloop from 0.1.8 to 0.3.1 (#182, #183, #184, #187, #200, #201, #203) + - Bumped actions/setup-python from 5 to 6 (#199) + - Bumped actions/checkout from 4 to 6 (#188, #208) + - Bumped actions/upload-artifact from 4 to 5 (#204) + - Bumped actions/download-artifact from 4.3.0 to 6.0.0 (#205) + 3.5.0 ===== - Added explicit close method (#166) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aiodns-3.5.0/README.rst new/aiodns-3.6.0/README.rst --- old/aiodns-3.5.0/README.rst 2025-06-13 18:19:58.000000000 +0200 +++ new/aiodns-3.6.0/README.rst 2025-12-05 18:44:54.000000000 +0100 @@ -41,14 +41,14 @@ instance of ``asyncio.Future``. The actual result of the DNS query is taken directly from pycares. As of version 1.0.0 of aiodns (and pycares, for that matter) results are always namedtuple-like objects with different attributes. Please check the `documentation - <http://pycares.readthedocs.org/en/latest/channel.html#pycares.Channel.query>`_ + <http://pycares.readthedocs.org/latest/channel.html#pycares.Channel.query>`_ for the result fields. * ``gethostbyname(host, socket_family)``: Do a DNS resolution for the given hostname and the desired type of address family (i.e. ``socket.AF_INET``). While ``query()`` always performs a request to a DNS server, ``gethostbyname()`` first looks into ``/etc/hosts`` and thus can resolve local hostnames (such as ``localhost``). Please check `the documentation - <http://pycares.readthedocs.io/en/latest/channel.html#pycares.Channel.gethostbyname>`_ + <http://pycares.readthedocs.io/latest/channel.html#pycares.Channel.gethostbyname>`_ for the result fields. The actual result of the call is a ``asyncio.Future``. * ``gethostbyaddr(name)``: Make a reverse lookup for an address. * ``cancel()``: Cancel all pending DNS queries. All futures will get ``DNSError`` exception set, with diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aiodns-3.5.0/aiodns/__init__.py new/aiodns-3.6.0/aiodns/__init__.py --- old/aiodns-3.5.0/aiodns/__init__.py 2025-06-13 18:19:58.000000000 +0200 +++ new/aiodns-3.6.0/aiodns/__init__.py 2025-12-05 18:44:54.000000000 +0100 @@ -20,7 +20,7 @@ from . import error -__version__ = '3.5.0' +__version__ = '3.6.0' __all__ = ('DNSResolver', 'error') @@ -33,9 +33,6 @@ _LOGGER = logging.getLogger(__name__) -READ = 1 -WRITE = 2 - query_type_map = { 'A': pycares.QUERY_TYPE_A, 'AAAA': pycares.QUERY_TYPE_AAAA, @@ -131,9 +128,8 @@ def nameservers(self, value: Iterable[Union[str, bytes]]) -> None: self._channel.servers = value - @staticmethod def _callback( - fut: asyncio.Future[_T], result: _T, errorno: Optional[int] + self, fut: asyncio.Future[_T], result: _T, errorno: Optional[int] ) -> None: if fut.cancelled(): return @@ -271,10 +267,14 @@ def _sock_state_cb(self, fd: int, readable: bool, writable: bool) -> None: if readable or writable: if readable: - self.loop.add_reader(fd, self._handle_event, fd, READ) + self.loop.add_reader( + fd, self._channel.process_fd, fd, pycares.ARES_SOCKET_BAD + ) self._read_fds.add(fd) if writable: - self.loop.add_writer(fd, self._handle_event, fd, WRITE) + self.loop.add_writer( + fd, self._channel.process_fd, pycares.ARES_SOCKET_BAD, fd + ) self._write_fds.add(fd) if self._timer is None: self._start_timer() @@ -296,15 +296,6 @@ self._timer.cancel() self._timer = None - def _handle_event(self, fd: int, event: int) -> None: - read_fd = pycares.ARES_SOCKET_BAD - write_fd = pycares.ARES_SOCKET_BAD - if event == READ: - read_fd = fd - elif event == WRITE: - write_fd = fd - self._channel.process_fd(read_fd, write_fd) - def _timer_cb(self) -> None: if self._read_fds or self._write_fds: self._channel.process_fd( @@ -335,9 +326,9 @@ self._timer = None # Remove all file descriptors - for fd in list(self._read_fds): + for fd in self._read_fds: self.loop.remove_reader(fd) - for fd in list(self._write_fds): + for fd in self._write_fds: self.loop.remove_writer(fd) self._read_fds.clear() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aiodns-3.5.0/requirements-dev.txt new/aiodns-3.6.0/requirements-dev.txt --- old/aiodns-3.5.0/requirements-dev.txt 2025-06-13 18:19:58.000000000 +0200 +++ new/aiodns-3.6.0/requirements-dev.txt 2025-12-05 18:44:54.000000000 +0100 @@ -1,3 +1,3 @@ -r requirements.txt -mypy==1.16.0 +mypy==1.19.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aiodns-3.5.0/requirements.txt new/aiodns-3.6.0/requirements.txt --- old/aiodns-3.5.0/requirements.txt 2025-06-13 18:19:58.000000000 +0200 +++ new/aiodns-3.6.0/requirements.txt 2025-12-05 18:44:54.000000000 +0100 @@ -1,7 +1,7 @@ -e . -pycares==4.9.0 -pytest==8.4.0 -pytest-asyncio==1.0.0 -pytest-cov==6.2.1 -uvloop==0.21.0; platform_system != "Windows" and implementation_name == "cpython" -winloop==0.1.8; platform_system == "Windows" +pycares==4.11.0 +pytest==8.4.2 +pytest-asyncio==1.2.0 +pytest-cov==7.0.0 +uvloop==0.22.1; platform_system != "Windows" and implementation_name == "cpython" +winloop==0.3.1; platform_system == "Windows" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aiodns-3.5.0/tests/test_aiodns.py new/aiodns-3.6.0/tests/test_aiodns.py --- old/aiodns-3.5.0/tests/test_aiodns.py 2025-06-13 18:19:58.000000000 +0200 +++ new/aiodns-3.6.0/tests/test_aiodns.py 2025-12-05 18:44:54.000000000 +0100 @@ -28,14 +28,24 @@ except ModuleNotFoundError: skip_uvloop = True +# Skip uvloop tests on Python 3.14+ due to EventLoopPolicy deprecation +if sys.version_info >= (3, 14): + skip_uvloop = True + class DNSTest(unittest.TestCase): def setUp(self) -> None: if sys.platform == 'win32': - asyncio.set_event_loop_policy( - asyncio.WindowsSelectorEventLoopPolicy() - ) - self.loop = asyncio.new_event_loop() + if sys.version_info >= (3, 14): + # Policy deprecated in 3.14, create SelectorEventLoop directly + self.loop = asyncio.SelectorEventLoop() + else: + asyncio.set_event_loop_policy( + asyncio.WindowsSelectorEventLoopPolicy() + ) + self.loop = asyncio.new_event_loop() + else: + self.loop = asyncio.new_event_loop() self.addCleanup(self.loop.close) self.resolver = aiodns.DNSResolver(loop=self.loop, timeout=5.0) self.resolver.nameservers = ['8.8.8.8'] @@ -217,10 +227,15 @@ def setUp(self) -> None: if sys.platform == 'win32': - asyncio.set_event_loop_policy( - asyncio.WindowsSelectorEventLoopPolicy() - ) - self.loop = asyncio.new_event_loop() + if sys.version_info >= (3, 14): + self.loop = asyncio.SelectorEventLoop() + else: + asyncio.set_event_loop_policy( + asyncio.WindowsSelectorEventLoopPolicy() + ) + self.loop = asyncio.new_event_loop() + else: + self.loop = asyncio.new_event_loop() self.addCleanup(self.loop.close) self.resolver = aiodns.DNSResolver(loop=self.loop) self.resolver.nameservers = ['1.1.1.1'] @@ -236,10 +251,15 @@ def setUp(self) -> None: if sys.platform == 'win32': - asyncio.set_event_loop_policy( - asyncio.WindowsSelectorEventLoopPolicy() - ) - self.loop = asyncio.new_event_loop() + if sys.version_info >= (3, 14): + self.loop = asyncio.SelectorEventLoop() + else: + asyncio.set_event_loop_policy( + asyncio.WindowsSelectorEventLoopPolicy() + ) + self.loop = asyncio.new_event_loop() + else: + self.loop = asyncio.new_event_loop() self.addCleanup(self.loop.close) self.resolver = aiodns.DNSResolver( timeout=0.1, tries=1, loop=self.loop @@ -262,7 +282,7 @@ self.assertLess(time.monotonic() - started, 1) [email protected](skip_uvloop, "We don't have a uvloop or winloop module") [email protected](skip_uvloop, 'uvloop/winloop unavailable or Python 3.14+') class TestUV_DNS(DNSTest): def setUp(self) -> None: asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) @@ -282,7 +302,7 @@ super().setUp() [email protected](skip_uvloop, "We don't have a uvloop or winloop module") [email protected](skip_uvloop, 'uvloop/winloop unavailable or Python 3.14+') class TestUV_QueryTxtChaos(TestQueryTxtChaos): """Test DNS queries with CHAOS class using uvloop.""" @@ -294,7 +314,7 @@ self.resolver.nameservers = ['1.1.1.1'] [email protected](skip_uvloop, "We don't have a uvloop or winloop module") [email protected](skip_uvloop, 'uvloop/winloop unavailable or Python 3.14+') class TestUV_QueryTimeout(TestQueryTimeout): """Test DNS queries with timeout configuration using uvloop.""" @@ -571,7 +591,9 @@ @pytest.mark.asyncio async def test_close_resolver() -> None: """Test that DNSResolver.close() properly shuts down the resolver.""" + # Use a non-routable IP to ensure the query doesn't complete before close resolver = aiodns.DNSResolver() + resolver.nameservers = ['192.0.2.1'] # TEST-NET-1, non-routable # Create a query to ensure resolver is active query_future = resolver.query('google.com', 'A') @@ -801,5 +823,31 @@ assert close_count == 2 [email protected] +async def test_temporary_resolver_not_garbage_collected() -> None: + """Test temporary resolver is not garbage collected before query completes. + + Regression test for https://github.com/aio-libs/aiodns/issues/209 + + When calling query() on a temporary resolver (not stored in a variable), + the resolver should not be garbage collected before the query completes. + Previously, the callback was a @staticmethod which didn't hold a reference + to self, causing the resolver to be garbage collected and the query + cancelled. + """ + # Force garbage collection to ensure any weak references are cleared + gc.collect() + + # This pattern previously failed with DNSError(24, 'DNS query cancelled') + # because the resolver was garbage collected before the query completed + result = await aiodns.DNSResolver(nameservers=['8.8.8.8']).query( + 'google.com', 'A' + ) + + # Query should succeed + assert result + assert len(result) > 0 + + if __name__ == '__main__': # pragma: no cover unittest.main(verbosity=2)
