From 828b04650c6a58319d9e482543449766ab91e222 Mon Sep 17 00:00:00 2001 From: Arun Babu Neelicattu Date: Fri, 31 Jan 2025 18:37:01 +0100 Subject: [PATCH] feat(ux): improve connection error reporting --- src/poetry/utils/authenticator.py | 27 +++++++++++++++++++++++++-- tests/utils/test_authenticator.py | 4 +++- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/poetry/utils/authenticator.py b/src/poetry/utils/authenticator.py index b51a44c0c59..05b83a5400b 100644 --- a/src/poetry/utils/authenticator.py +++ b/src/poetry/utils/authenticator.py @@ -22,6 +22,8 @@ from poetry.__version__ import __version__ from poetry.config.config import Config +from poetry.console.exceptions import ConsoleMessage +from poetry.console.exceptions import PoetryRuntimeError from poetry.exceptions import PoetryError from poetry.utils.constants import REQUESTS_TIMEOUT from poetry.utils.constants import RETRY_AFTER_HEADER @@ -230,7 +232,28 @@ def request( resp = session.send(prepared_request, **send_kwargs) except (requests.exceptions.ConnectionError, OSError) as e: if is_last_attempt: - raise e + parsed_url = urllib.parse.urlsplit(url) + exc = PoetryRuntimeError.create( + reason=f"All attempts to connect to {parsed_url.netloc} failed.", + exception=e, + ) + exc.append( + ConsoleMessage( + "the server is not responding to requests at the moment\n" + "the hostname cannot be resolved by your DNS\n" + "your network is not connected to the internet\n" + ) + .indent(" - ") + .make_section("Probable Causes") + .wrap("warning") + ) + exc.append( + ConsoleMessage( + f"Note: The path requested was {parsed_url.path}.", + debug=True, + ) + ) + raise exc else: if resp.status_code not in STATUS_FORCELIST or is_last_attempt: if raise_for_status: @@ -245,7 +268,7 @@ def request( continue # this should never really be hit under any sane circumstance - raise PoetryError("Failed HTTP {} request", method.upper()) + raise PoetryError(f"Failed HTTP request: {method.upper()} {url}") def _get_backoff(self, response: requests.Response | None, attempt: int) -> float: if response is not None: diff --git a/tests/utils/test_authenticator.py b/tests/utils/test_authenticator.py index 9bdc6eed55d..7644af20fb3 100644 --- a/tests/utils/test_authenticator.py +++ b/tests/utils/test_authenticator.py @@ -16,6 +16,7 @@ from cleo.io.null_io import NullIO from keyring.credentials import SimpleCredential +from poetry.console.exceptions import PoetryRuntimeError from poetry.utils.authenticator import Authenticator from poetry.utils.authenticator import RepositoryCertificateConfig from poetry.utils.password_manager import PoetryKeyring @@ -279,9 +280,10 @@ def callback(*_: Any, **___: Any) -> None: http.register_uri(httpretty.GET, sdist_uri, body=callback) authenticator = Authenticator(config, NullIO()) - with pytest.raises(requests.exceptions.ConnectionError): + with pytest.raises(PoetryRuntimeError) as e: authenticator.request("get", sdist_uri) + assert str(e.value) == "All attempts to connect to foo.bar failed." assert sleep.call_count == 5