Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion language_tool_python/download_lt.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,8 @@ def http_get(
:param proxies: Optional dictionary of proxies to use for the request.
:type proxies: Optional[Dict[str, str]]
:raises TimeoutError: If the request times out.
:raises PathError: If the file could not be found at the given URL (HTTP 404).
:raises PathError: If the file could not be found at the given URL (HTTP 404),
if access is forbidden (HTTP 403), or for other HTTP errors.
"""
logger.info("Starting download from %s", url)
try:
Expand All @@ -185,6 +186,12 @@ def http_get(
if req.status_code == 404:
err = f"Could not find at URL {url}. The given version may not exist or is no longer available."
raise PathError(err)
if req.status_code == 403:
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)."
raise PathError(err)
if req.status_code != 200:
err = f"Failed to download from {url}. HTTP status code: {req.status_code}."
raise PathError(err)
version = (
url.split("/")[-1].split("-")[1].replace("-snapshot", "").replace(".zip", "")
)
Expand Down
54 changes: 53 additions & 1 deletion tests/test_download.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
"""Tests for the download/language functionality of LanguageTool."""

import io
from unittest.mock import MagicMock, patch

import pytest

from language_tool_python.exceptions import LanguageToolError
from language_tool_python.download_lt import http_get
from language_tool_python.exceptions import LanguageToolError, PathError


def test_install_inexistent_version() -> None:
Expand Down Expand Up @@ -31,3 +35,51 @@ def test_inexistent_language() -> None:

with language_tool_python.LanguageTool("en-US") as tool, pytest.raises(ValueError):
language_tool_python.LanguageTag("xx-XX", tool._get_languages())


def test_http_get_403_forbidden() -> None:
"""
Test that http_get raises PathError when receiving a 403 Forbidden status code.
This test verifies that the function correctly handles forbidden access errors
when attempting to download files.

:raises AssertionError: If PathError is not raised for a 403 status code.
"""
mock_response = MagicMock()
mock_response.status_code = 403
mock_response.headers = {}

with (
patch(
"language_tool_python.download_lt.requests.get", return_value=mock_response
),
pytest.raises(PathError, match="Access forbidden to URL"),
):
out_file = io.BytesIO()
http_get("https://example.com/test.zip", out_file)


def test_http_get_other_error_codes() -> None:
"""
Test that http_get raises PathError for various HTTP error codes (other than 404 and 403).
This test verifies that the function correctly handles different HTTP error codes
like 500 (Internal Server Error), 503 (Service Unavailable), etc.

:raises AssertionError: If PathError is not raised for error status codes.
"""
error_codes = [500, 502, 503, 504]

for error_code in error_codes:
mock_response = MagicMock()
mock_response.status_code = error_code
mock_response.headers = {}

with (
patch(
"language_tool_python.download_lt.requests.get",
return_value=mock_response,
),
pytest.raises(PathError, match=f"Failed to download.*{error_code}"),
):
out_file = io.BytesIO()
http_get("https://example.com/test.zip", out_file)