Skip to content

Commit d465cb0

Browse files
authored
Handle non-standard HTTP status codes (#1309)
* Handle non-standard HTTP status codes Signed-off-by: William Woodruff <william@astral.sh> * Parametrize HTTP exception test Signed-off-by: William Woodruff <william@astral.sh> * Parametrize reason too Signed-off-by: William Woodruff <william@astral.sh> --------- Signed-off-by: William Woodruff <william@astral.sh>
1 parent cab618f commit d465cb0

2 files changed

Lines changed: 25 additions & 7 deletions

File tree

tests/test_main.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import sys
1414

1515
import pretend
16+
import pytest
1617
import requests
1718

1819
from twine import __main__ as dunder_main
@@ -42,17 +43,21 @@ def test_exception_handling(monkeypatch, capsys):
4243
)
4344

4445

45-
def test_http_exception_handling(monkeypatch, capsys):
46+
@pytest.mark.parametrize(
47+
("status_code", "status_phrase", "reason"),
48+
[(400, "Bad Request", "Error reason"), (599, "Unknown Status", "Another reason")],
49+
)
50+
def test_http_exception_handling(
51+
monkeypatch, capsys, status_code, status_phrase, reason
52+
):
4653
monkeypatch.setattr(sys, "argv", ["twine", "upload", "test.whl"])
4754
monkeypatch.setattr(
4855
upload,
4956
"upload",
5057
pretend.raiser(
5158
requests.HTTPError(
5259
response=pretend.stub(
53-
url="https://example.org",
54-
status_code=400,
55-
reason="Error reason",
60+
url="https://example.org", status_code=status_code, reason=reason
5661
)
5762
)
5863
),
@@ -64,8 +69,8 @@ def test_http_exception_handling(monkeypatch, capsys):
6469
captured = capsys.readouterr()
6570

6671
assert _unwrap_lines(captured.out) == (
67-
f"{RED_ERROR} HTTPError: 400 Bad Request from https://example.org "
68-
"Error reason"
72+
f"{RED_ERROR} HTTPError: {status_code} {status_phrase} "
73+
f"from https://example.org {reason}"
6974
)
7075

7176

twine/__main__.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,20 @@ def main() -> Any:
3737

3838
error = True
3939
status_code = response.status_code
40-
status_phrase = http.HTTPStatus(status_code).phrase
40+
41+
try:
42+
status_phrase = http.HTTPStatus(status_code).phrase
43+
except ValueError:
44+
# HTTPStatus will raise ValueError if the server responds with
45+
# a non-standard status code. This is almost certainly an upstream
46+
# index error since non-standard status codes should only be used
47+
# for internal signaling, but there's nothing we can do about that
48+
# here other than avoid propagating it as an unhandled exception.
49+
#
50+
# See: <https://github.com/pypa/twine/issues/1304>
51+
# See: <https://github.com/pypi/warehouse/issues/19713>
52+
status_phrase = "Unknown Status"
53+
4154
logger.error(
4255
f"{exc.__class__.__name__}: {status_code} {status_phrase} "
4356
f"from {response.url}\n"

0 commit comments

Comments
 (0)