Skip to content

Commit 1f1bc8f

Browse files
[PR #10946/3c88f811 backport][3.12] Ensure AsyncResolver.close() can be called multiple times (#10947)
Co-authored-by: J. Nick Koston <[email protected]>
1 parent 9bd43ed commit 1f1bc8f

File tree

3 files changed

+40
-1
lines changed

3 files changed

+40
-1
lines changed

CHANGES/10946.feature.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
10847.feature.rst

aiohttp/resolver.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,8 @@ async def close(self) -> None:
198198
self._resolver = None # type: ignore[assignment] # Clear reference to resolver
199199
return
200200
# Otherwise cancel our dedicated resolver
201-
self._resolver.cancel()
201+
if self._resolver is not None:
202+
self._resolver.cancel()
202203
self._resolver = None # type: ignore[assignment] # Clear reference
203204

204205

tests/test_resolver.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -674,3 +674,40 @@ async def test_dns_resolver_manager_missing_loop_data() -> None:
674674

675675
# Verify no exception was raised
676676
assert loop not in manager._loop_data
677+
678+
679+
@pytest.mark.skipif(not getaddrinfo, reason="aiodns >=3.2.0 required")
680+
@pytest.mark.usefixtures("check_no_lingering_resolvers")
681+
async def test_async_resolver_close_multiple_times() -> None:
682+
"""Test that AsyncResolver.close() can be called multiple times without error."""
683+
with patch("aiodns.DNSResolver") as mock_dns_resolver:
684+
mock_resolver = Mock()
685+
mock_resolver.cancel = Mock()
686+
mock_dns_resolver.return_value = mock_resolver
687+
688+
# Create a resolver with custom args (dedicated resolver)
689+
resolver = AsyncResolver(nameservers=["8.8.8.8"])
690+
691+
# Close it once
692+
await resolver.close()
693+
mock_resolver.cancel.assert_called_once()
694+
695+
# Close it again - should not raise AttributeError
696+
await resolver.close()
697+
# cancel should still only be called once
698+
mock_resolver.cancel.assert_called_once()
699+
700+
701+
@pytest.mark.skipif(not getaddrinfo, reason="aiodns >=3.2.0 required")
702+
@pytest.mark.usefixtures("check_no_lingering_resolvers")
703+
async def test_async_resolver_close_with_none_resolver() -> None:
704+
"""Test that AsyncResolver.close() handles None resolver gracefully."""
705+
with patch("aiodns.DNSResolver"):
706+
# Create a resolver with custom args (dedicated resolver)
707+
resolver = AsyncResolver(nameservers=["8.8.8.8"])
708+
709+
# Manually set resolver to None to simulate edge case
710+
resolver._resolver = None # type: ignore[assignment]
711+
712+
# This should not raise AttributeError
713+
await resolver.close()

0 commit comments

Comments
 (0)