Skip to content

Commit

Permalink
[PR #8456/b09d7cc backport][3.10] Add ClientConnectorDNSError for dif…
Browse files Browse the repository at this point in the history
…ferentiating DNS errors from others (#9459)

Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: Marcus Stojcevich <129109254+mstojcevich-cisco@users.noreply.github.com>
  • Loading branch information
bdraco and mstojcevich-cisco authored Oct 10, 2024
1 parent cdf3dca commit 8b424c8
Show file tree
Hide file tree
Showing 8 changed files with 57 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGES/8455.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added :exc:`aiohttp.ClientConnectorDNSError` for differentiating DNS resolution errors from other connector errors -- by :user:`mstojcevich`.
1 change: 1 addition & 0 deletions CONTRIBUTORS.txt
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ Manuel Miranda
Marat Sharafutdinov
Marc Mueller
Marco Paolini
Marcus Stojcevich
Mariano Anaya
Mariusz Masztalerczuk
Marko Kohtala
Expand Down
2 changes: 2 additions & 0 deletions aiohttp/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
ClientConnectionError,
ClientConnectionResetError,
ClientConnectorCertificateError,
ClientConnectorDNSError,
ClientConnectorError,
ClientConnectorSSLError,
ClientError,
Expand Down Expand Up @@ -127,6 +128,7 @@
"ClientConnectionError",
"ClientConnectionResetError",
"ClientConnectorCertificateError",
"ClientConnectorDNSError",
"ClientConnectorError",
"ClientConnectorSSLError",
"ClientError",
Expand Down
2 changes: 2 additions & 0 deletions aiohttp/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
ClientConnectionError,
ClientConnectionResetError,
ClientConnectorCertificateError,
ClientConnectorDNSError,
ClientConnectorError,
ClientConnectorSSLError,
ClientError,
Expand Down Expand Up @@ -105,6 +106,7 @@
"ClientConnectionError",
"ClientConnectionResetError",
"ClientConnectorCertificateError",
"ClientConnectorDNSError",
"ClientConnectorError",
"ClientConnectorSSLError",
"ClientError",
Expand Down
9 changes: 9 additions & 0 deletions aiohttp/client_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"ClientConnectorError",
"ClientProxyConnectionError",
"ClientSSLError",
"ClientConnectorDNSError",
"ClientConnectorSSLError",
"ClientConnectorCertificateError",
"ConnectionTimeoutError",
Expand Down Expand Up @@ -206,6 +207,14 @@ def __str__(self) -> str:
__reduce__ = BaseException.__reduce__


class ClientConnectorDNSError(ClientConnectorError):
"""DNS resolution failed during client connection.
Raised in :class:`aiohttp.connector.TCPConnector` if
DNS resolution fails.
"""


class ClientProxyConnectionError(ClientConnectorError):
"""Proxy connection error.
Expand Down
3 changes: 2 additions & 1 deletion aiohttp/connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
from .client_exceptions import (
ClientConnectionError,
ClientConnectorCertificateError,
ClientConnectorDNSError,
ClientConnectorError,
ClientConnectorSSLError,
ClientHttpProxyError,
Expand Down Expand Up @@ -1319,7 +1320,7 @@ async def _create_direct_connection(
raise
# in case of proxy it is not ClientProxyConnectionError
# it is problem of resolving proxy ip itself
raise ClientConnectorError(req.connection_key, exc) from exc
raise ClientConnectorDNSError(req.connection_key, exc) from exc

last_exc: Optional[Exception] = None
addr_infos = self._convert_hosts_to_addr_infos(hosts)
Expand Down
8 changes: 8 additions & 0 deletions docs/client_reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2228,6 +2228,12 @@ Connection errors

Derived from :exc:`ClientOSError`

.. class:: ClientConnectorDNSError

DNS resolution error.

Derived from :exc:`ClientConnectorError`

.. class:: ClientProxyConnectionError

Derived from :exc:`ClientConnectorError`
Expand Down Expand Up @@ -2309,6 +2315,8 @@ Hierarchy of exceptions

* :exc:`ClientProxyConnectionError`

* :exc:`ClientConnectorDNSError`

* :exc:`ClientSSLError`

* :exc:`ClientConnectorCertificateError`
Expand Down
33 changes: 32 additions & 1 deletion tests/test_client_functional.py
Original file line number Diff line number Diff line change
Expand Up @@ -3116,7 +3116,38 @@ async def test_aiohttp_request_ctx_manager_not_found() -> None:
assert False, "never executed" # pragma: no cover


async def test_yield_from_in_session_request(aiohttp_client) -> None:
async def test_raising_client_connector_dns_error_on_dns_failure() -> None:
"""Verify that the exception raised when a DNS lookup fails is specific to DNS."""
with mock.patch(
"aiohttp.connector.TCPConnector._resolve_host", autospec=True, spec_set=True
) as mock_resolve_host:
mock_resolve_host.side_effect = OSError(None, "DNS lookup failed")
with pytest.raises(aiohttp.ClientConnectorDNSError, match="DNS lookup failed"):
async with aiohttp.request("GET", "http://wrong-dns-name.com"):
assert False, "never executed"


async def test_aiohttp_request_coroutine(aiohttp_server: AiohttpServer) -> None:
async def handler(request: web.Request) -> web.Response:
return web.Response()

app = web.Application()
app.router.add_get("/", handler)
server = await aiohttp_server(app)

not_an_awaitable = aiohttp.request("GET", server.make_url("/"))
with pytest.raises(
TypeError,
match="^object _SessionRequestContextManager "
"can't be used in 'await' expression$",
):
await not_an_awaitable # type: ignore[misc]

await not_an_awaitable._coro # coroutine 'ClientSession._request' was never awaited
await server.close()


async def test_yield_from_in_session_request(aiohttp_client: AiohttpClient) -> None:
# a test for backward compatibility with yield from syntax
async def handler(request):
return web.Response()
Expand Down

0 comments on commit 8b424c8

Please sign in to comment.