Skip to content

Commit 17bc25b

Browse files
authored
fix: error handling when http error during LT download (jxmorris12#150)
1 parent e7a0fb2 commit 17bc25b

File tree

2 files changed

+61
-2
lines changed

2 files changed

+61
-2
lines changed

language_tool_python/download_lt.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,8 @@ def http_get(
172172
:param proxies: Optional dictionary of proxies to use for the request.
173173
:type proxies: Optional[Dict[str, str]]
174174
:raises TimeoutError: If the request times out.
175-
:raises PathError: If the file could not be found at the given URL (HTTP 404).
175+
:raises PathError: If the file could not be found at the given URL (HTTP 404),
176+
if access is forbidden (HTTP 403), or for other HTTP errors.
176177
"""
177178
logger.info("Starting download from %s", url)
178179
try:
@@ -185,6 +186,12 @@ def http_get(
185186
if req.status_code == 404:
186187
err = f"Could not find at URL {url}. The given version may not exist or is no longer available."
187188
raise PathError(err)
189+
if req.status_code == 403:
190+
err = f"Access forbidden to URL {url}. You may not have permission to access this resource. It may be related to network restrictions (e.g., firewall, proxy settings)."
191+
raise PathError(err)
192+
if req.status_code != 200:
193+
err = f"Failed to download from {url}. HTTP status code: {req.status_code}."
194+
raise PathError(err)
188195
version = (
189196
url.split("/")[-1].split("-")[1].replace("-snapshot", "").replace(".zip", "")
190197
)

tests/test_download.py

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
"""Tests for the download/language functionality of LanguageTool."""
22

3+
import io
4+
from unittest.mock import MagicMock, patch
5+
36
import pytest
47

5-
from language_tool_python.exceptions import LanguageToolError
8+
from language_tool_python.download_lt import http_get
9+
from language_tool_python.exceptions import LanguageToolError, PathError
610

711

812
def test_install_inexistent_version() -> None:
@@ -31,3 +35,51 @@ def test_inexistent_language() -> None:
3135

3236
with language_tool_python.LanguageTool("en-US") as tool, pytest.raises(ValueError):
3337
language_tool_python.LanguageTag("xx-XX", tool._get_languages())
38+
39+
40+
def test_http_get_403_forbidden() -> None:
41+
"""
42+
Test that http_get raises PathError when receiving a 403 Forbidden status code.
43+
This test verifies that the function correctly handles forbidden access errors
44+
when attempting to download files.
45+
46+
:raises AssertionError: If PathError is not raised for a 403 status code.
47+
"""
48+
mock_response = MagicMock()
49+
mock_response.status_code = 403
50+
mock_response.headers = {}
51+
52+
with (
53+
patch(
54+
"language_tool_python.download_lt.requests.get", return_value=mock_response
55+
),
56+
pytest.raises(PathError, match="Access forbidden to URL"),
57+
):
58+
out_file = io.BytesIO()
59+
http_get("https://example.com/test.zip", out_file)
60+
61+
62+
def test_http_get_other_error_codes() -> None:
63+
"""
64+
Test that http_get raises PathError for various HTTP error codes (other than 404 and 403).
65+
This test verifies that the function correctly handles different HTTP error codes
66+
like 500 (Internal Server Error), 503 (Service Unavailable), etc.
67+
68+
:raises AssertionError: If PathError is not raised for error status codes.
69+
"""
70+
error_codes = [500, 502, 503, 504]
71+
72+
for error_code in error_codes:
73+
mock_response = MagicMock()
74+
mock_response.status_code = error_code
75+
mock_response.headers = {}
76+
77+
with (
78+
patch(
79+
"language_tool_python.download_lt.requests.get",
80+
return_value=mock_response,
81+
),
82+
pytest.raises(PathError, match=f"Failed to download.*{error_code}"),
83+
):
84+
out_file = io.BytesIO()
85+
http_get("https://example.com/test.zip", out_file)

0 commit comments

Comments
 (0)