diff --git a/src/poetry/console/exceptions.py b/src/poetry/console/exceptions.py index 888a27e728f..21385251715 100644 --- a/src/poetry/console/exceptions.py +++ b/src/poetry/console/exceptions.py @@ -222,3 +222,9 @@ def create( ) return cls(reason, messages) + + def append(self, message: str | ConsoleMessage) -> PoetryRuntimeError: + if isinstance(message, str): + message = ConsoleMessage(message) + self._messages.append(message) + return self 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/console/test_exections_poetry_runtime_error.py b/tests/console/test_exections_poetry_runtime_error.py index db687c1f43b..e2acb93dfb3 100644 --- a/tests/console/test_exections_poetry_runtime_error.py +++ b/tests/console/test_exections_poetry_runtime_error.py @@ -139,3 +139,10 @@ def test_poetry_runtime_error_create( actual_texts = [msg.text for msg in error._messages] assert actual_texts == expected_message_texts + + +def test_poetry_runtime_error_append() -> None: + """Test the append method of PoetryRuntimeError.""" + error = PoetryRuntimeError.create("Error", info=["Hello"]).append("World") + actual_texts = [msg.text for msg in error._messages] + assert actual_texts == ["Error", "Hello", "World"] 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