diff --git a/README.md b/README.md index 97c35e3..46cb4aa 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ It can: - call your own remote LanguageTool server, - be used from Python code and from a CLI. -Default local download target: `latest` snapshot (currently `6.9-SNAPSHOT`). +Default local download target: LanguageTool `6.8`. ## Documentation @@ -90,9 +90,9 @@ with language_tool_python.LanguageTool( ``` Accepted formats: -- `latest` (default): latest snapshot configured by this package (`6.9-SNAPSHOT` at the moment) +- `latest`: latest snapshot available from the snapshot server - `YYYYMMDD`: snapshot by date (example: `20260201`) -- `X.Y`: release version (example: `6.7`, `4.0`) +- `X.Y`: release version (default: `6.8`. Examples: `6.7`, `4.0`) Notes: - Only relevant when using a local server (no `remote_server`). @@ -341,8 +341,8 @@ Example: ```bash export LTP_PATH=/path/to/cache -export LTP_JAR_DIR_PATH=/path/to/LanguageTool-6.9-SNAPSHOT -export LTP_DOWNLOAD_SHA256_6_9_SNAPSHOT= +export LTP_JAR_DIR_PATH=/path/to/LanguageTool-6.8 +export LTP_DOWNLOAD_SHA256_6_8= # export LTP_BYPASS_VERIFIED_DOWNLOADS=true ``` diff --git a/language_tool_python/download_lt.py b/language_tool_python/download_lt.py index 2f78144..fc27800 100755 --- a/language_tool_python/download_lt.py +++ b/language_tool_python/download_lt.py @@ -57,8 +57,8 @@ ) FILENAME_RELEASE = "LanguageTool-{version}.zip" -LTP_DOWNLOAD_VERSION = "latest" -LT_SNAPSHOT_CURRENT_VERSION = "6.9-SNAPSHOT" +LTP_DOWNLOAD_VERSION = "6.8" +LT_SNAPSHOT_LATEST_VERSION = "latest" LTP_DOWNLOAD_SHA256_ENV_VAR = "LTP_DOWNLOAD_SHA256" LTP_BYPASS_VERIFIED_DOWNLOADS_ENV_VAR = "LTP_BYPASS_VERIFIED_DOWNLOADS" LTP_MAX_DOWNLOAD_BYTES_ENV_VAR = "LTP_MAX_DOWNLOAD_BYTES" @@ -399,13 +399,16 @@ def from_version_name( This factory method determines the appropriate subclass (ReleaseLocalLanguageTool or SnapshotLocalLanguageTool) based on the version name format. - :param version_name: The version name (e.g., '6.0', '20240101', or 'latest'). + :param version_name: The version name (e.g., '6.8', '20240101', or 'latest'). :type version_name: str :return: An instance of the appropriate LocalLanguageTool subclass. :rtype: LocalLanguageTool :raises ValueError: If the version name format is not recognized. """ - if re.match(r"^\d{8}$", version_name) or version_name == LTP_DOWNLOAD_VERSION: + if ( + re.match(r"^\d{8}$", version_name) + or version_name == LT_SNAPSHOT_LATEST_VERSION + ): return SnapshotLocalLanguageTool(version_name) if re.match(r"^\d+\.\d+$", version_name): return ReleaseLocalLanguageTool(version_name) @@ -430,11 +433,7 @@ def from_path(cls, path: Path) -> "LocalLanguageTool": err = f"Could not determine LanguageTool version from path: {path}" raise ValueError(err) version_name = match.group(1) - return cls.from_version_name( - version_name - if version_name != LT_SNAPSHOT_CURRENT_VERSION - else LTP_DOWNLOAD_VERSION - ) + return cls.from_version_name(version_name) @abstractmethod def download(self) -> None: @@ -856,6 +855,11 @@ def __init__(self, version_name: str) -> None: Initialize a SnapshotLocalLanguageTool instance. """ self._version_name = version_name + self._install_version_name = ( + datetime.now().strftime("%Y%m%d") + if version_name == LT_SNAPSHOT_LATEST_VERSION + else version_name + ) def download(self) -> None: """ @@ -915,15 +919,13 @@ def version_name(self) -> str: """ Get the snapshot version name. - Returns the current snapshot version string if 'latest' was specified, - otherwise returns the specified date string. + Returns the current date if 'latest' was specified, otherwise returns the + specified date string. :return: The snapshot version string. :rtype: str """ - if self._version_name == LTP_DOWNLOAD_VERSION: - return LT_SNAPSHOT_CURRENT_VERSION - return self._version_name + return self._install_version_name @property def version_into(self) -> datetime: @@ -936,11 +938,7 @@ def version_into(self) -> datetime: :return: A datetime object representing the snapshot date. :rtype: datetime """ - if self._version_name == LTP_DOWNLOAD_VERSION: - date_str = datetime.now().strftime("%Y%m%d") - else: - date_str = self._version_name - return datetime.strptime(date_str, "%Y%m%d") + return datetime.strptime(self.version_name, "%Y%m%d") @property def download_url(self) -> str: diff --git a/tests/test_download.py b/tests/test_download.py index ed6beae..ee3cefa 100644 --- a/tests/test_download.py +++ b/tests/test_download.py @@ -264,6 +264,29 @@ def test_http_get_rejects_invalid_content_length( LocalLanguageTool.from_version_name()._get_remote_zip(io.BytesIO()) +def test_latest_snapshot_uses_latest_download_url_and_current_date( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """ + Test that latest remains a snapshot alias installed under the current date. + """ + monkeypatch.setattr( + download_lt, + "BASE_URL_SNAPSHOT", + "https://example.test/snapshots/", + ) + + with patch("language_tool_python.download_lt.datetime") as datetime_mock: + datetime_mock.now.return_value.strftime.return_value = "20240514" + local_language_tool = LocalLanguageTool.from_version_name("latest") + + assert local_language_tool.version_name == "20240514" + assert ( + local_language_tool.download_url + == "https://example.test/snapshots/LanguageTool-latest-snapshot.zip" + ) + + @pytest.mark.parametrize("release_version", ["6.7", "6.8"]) # type: ignore[untyped-decorator] def test_release_download_url_uses_new_release_base_from_6_7( release_version: str, @@ -497,6 +520,44 @@ def test_snapshot_download_renames_archive_root_to_requested_date( get_mock.assert_not_called() +def test_latest_snapshot_download_renames_archive_root_to_current_date( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """ + Test that latest snapshots are installed under the current date name. + """ + current_snapshot_date = "20240514" + payload = make_zip_payload( + {"LanguageTool-6.9-SNAPSHOT/languagetool-server.jar": b"jar"} + ) + with patch("language_tool_python.download_lt.datetime") as datetime_mock: + datetime_mock.now.return_value.strftime.return_value = current_snapshot_date + local_language_tool = LocalLanguageTool.from_version_name("latest") + monkeypatch.setattr(download_lt, "confirm_java_compatibility", lambda _: None) + + with ( + workspace_temp_dir() as temp_dir, + patch( + "language_tool_python.download_lt.requests.get", + return_value=MockDownloadResponse(payload), + ), + ): + monkeypatch.setattr( + download_lt, "get_language_tool_download_path", lambda: temp_dir + ) + local_language_tool.download() + + expected_dir = temp_dir / f"LanguageTool-{current_snapshot_date}" + assert (expected_dir / "languagetool-server.jar").read_bytes() == b"jar" + assert not (temp_dir / "LanguageTool-6.9-SNAPSHOT").exists() + assert local_language_tool.get_directory_path() == expected_dir + + with patch("language_tool_python.download_lt.requests.get") as get_mock: + local_language_tool.download() + + get_mock.assert_not_called() + + def test_install_oldest_supported_version() -> None: """ Test that downloading the oldest supported LanguageTool version works correctly. diff --git a/tests/test_server_local.py b/tests/test_server_local.py index 2221eed..bfad711 100644 --- a/tests/test_server_local.py +++ b/tests/test_server_local.py @@ -82,12 +82,11 @@ def test_session_only_new_spellings() -> None: import hashlib import language_tool_python - from language_tool_python.download_lt import LT_SNAPSHOT_CURRENT_VERSION + from language_tool_python.download_lt import LTP_DOWNLOAD_VERSION from language_tool_python.utils import get_language_tool_download_path library_path = ( - get_language_tool_download_path() - / f"LanguageTool-{LT_SNAPSHOT_CURRENT_VERSION}" + get_language_tool_download_path() / f"LanguageTool-{LTP_DOWNLOAD_VERSION}" ) spelling_file_path = ( library_path