diff --git a/language_tool_python/download_lt.py b/language_tool_python/download_lt.py index fd75d13..0a66595 100755 --- a/language_tool_python/download_lt.py +++ b/language_tool_python/download_lt.py @@ -23,7 +23,7 @@ import requests import tqdm -from ._compat import deprecated, toml_loads +from ._compat import toml_loads from .exceptions import JavaError, PathError from .safe_zip import SafeZipExtractor from .utils import ( @@ -308,159 +308,6 @@ def confirm_java_compatibility( raise SystemError(err) -@deprecated( - "This function is no longer used internally and will be removed in 4.0.", - stacklevel=2, -) -def get_common_prefix(z: zipfile.ZipFile) -> str | None: - """Determine the common prefix of all file names in a zip archive. - - :param z: A ZipFile object representing the zip archive. - :type z: zipfile.ZipFile - :return: The common prefix of all file names in the zip archive, or None if there - is no common prefix. - :rtype: str | None - - .. deprecated:: 3.3.0 - This function is no longer used internally and will be removed in 4.0. - """ - name_list = z.namelist() - if name_list and all(n.startswith(name_list[0]) for n in name_list[1:]): - return name_list[0] - return None - - -@deprecated( - "This function is no longer used internally and will be removed in 4.0.", - stacklevel=2, -) -def http_get( - url: str, - out_file: IO[bytes], - proxies: dict[str, str] | None = None, -) -> None: - """.. deprecated:: 3.3.0. - - This function is no longer used internally and will be removed in 4.0. - - :raises TimeoutError: If the download request times out. - :raises PathError: If the download fails or checksum validation fails. - """ - version_match = re.search(r"LanguageTool-(.+)\.zip", url) - if version_match: - version_start, version_end = version_match.span(1) - version_name = url[version_start:version_end] - else: - version_name = LTP_DOWNLOAD_VERSION - - # Normalize snapshot-style version names (e.g. "6.8-SNAPSHOT", "latest-snapshot") - if version_name.lower().endswith("-snapshot"): - version_name = version_name[: -len("-snapshot")] - try: - local_lt = LocalLanguageTool.from_version_name(version_name) - except ValueError: - # Fallback to default behavior if the extracted version is not supported - local_lt = LocalLanguageTool.from_version_name(LTP_DOWNLOAD_VERSION) - - with local_lt._get_remote_zip(out_file, proxies=proxies): # noqa: SLF001 # Accessing protected member temporarily for backward compatibility with the deprecated http_get function - pass - - -@deprecated( - "This function is no longer used internally and will be removed in 4.0.", - stacklevel=2, -) -def unzip_file(temp_file_name: str, directory_to_extract_to: Path) -> None: - """Unzips a zip file to a specified directory. - - :param temp_file_name: Path to the zip file to be extracted. - :type temp_file_name: str - :param directory_to_extract_to: The directory where the contents of the zip file - will be extracted. - :type directory_to_extract_to: Path - :raises PathError: If the ZIP archive or extraction destination is unsafe. - - .. deprecated:: 3.3.0 - This function is no longer used internally and will be removed in 4.0. - """ - logger.info("Unzipping %s to %s", temp_file_name, directory_to_extract_to) - with ( - tempfile.TemporaryDirectory(dir=directory_to_extract_to.parent) as temp_dir, - zipfile.ZipFile(temp_file_name, "r") as zip_ref, - ): - _SAFE_ZIP_EXTRACTOR.extractall( - zip_ref, - directory_to_extract_to, - work_dir=Path(temp_dir), - ) - - -@deprecated( - "This function is no longer used internally and will be removed in 4.0.", - stacklevel=2, -) -def download_zip(url: str, directory: Path) -> None: - """Download a ZIP file from the given URL and extract it to the specified directory. - - :param url: The URL of the ZIP file to download. - :type url: str - :param directory: The directory where the ZIP file should be extracted. - :type directory: Path - :raises TimeoutError: If the download request times out. - :raises PathError: If the download fails or the ZIP extraction is unsafe. - - .. deprecated:: 3.3.0 - This function is no longer used internally and will be removed in 4.0. - """ - logger.info("Downloading from %s to %s", url, directory) - # Download file using a context manager. - with tempfile.NamedTemporaryFile(suffix=".zip", delete=False) as downloaded_file: - http_get(url, downloaded_file) - temp_name = downloaded_file.name - # Extract zip file to path. - unzip_file(temp_name, directory) - # Remove the temporary file. - Path(temp_name).unlink(missing_ok=True) - - -@deprecated( - ( - "This function is no longer used internally and will be removed in 4.0.\n" - "Use instead language_tool_python.download_lt.LocalLanguageTool.download." - ), - stacklevel=2, -) -def download_lt(language_tool_version: str = LTP_DOWNLOAD_VERSION) -> None: - """Download and extract the specified version of LanguageTool. - - This function checks for Java compatibility, and downloads the specified version of - LanguageTool if it is not already present. - - :param language_tool_version: The version of LanguageTool to download. If not - specified, the default version defined by - LTP_DOWNLOAD_VERSION is used. - :type language_tool_version: str - :raises ValueError: If the specified version format is invalid. - :raises ModuleNotFoundError: If no Java installation is detected. - :raises SystemError: If the detected Java version is incompatible. - :raises TimeoutError: If the download request times out. - :raises PathError: If the version is unsupported, the download fails, checksum - validation fails, or ZIP extraction is unsafe. - - .. deprecated:: 3.3.0 - This function is no longer used internally and will be removed in 4.0. - """ - # Use the env var to the jar directory if it is defined - # otherwise look in the download directory - if os.environ.get(LTP_JAR_DIR_PATH_ENV_VAR): - return - - local_lt = LocalLanguageTool.from_version_name(language_tool_version) - - if local_lt not in local_lt.get_installed_versions(): - local_lt.download() - - @total_ordering class LocalLanguageTool(ABC): """Abstract base class for managing local LanguageTool installations. diff --git a/language_tool_python/match.py b/language_tool_python/match.py index 8808a7d..acc4030 100644 --- a/language_tool_python/match.py +++ b/language_tool_python/match.py @@ -9,9 +9,6 @@ from functools import total_ordering from typing import TYPE_CHECKING -from ._compat import deprecated -from .utils import SupportsFloat, SupportsInt - if TYPE_CHECKING: from collections.abc import Iterator @@ -61,28 +58,6 @@ def get_match_ordered_dict() -> OrderedDictType[str, type]: ) -@deprecated( - "This function is no longer used internally and will be removed in 4.0.", - stacklevel=2, -) -def auto_type(obj: SupportsInt | SupportsFloat | object) -> int | float | object: - """Attempt to automatically convert the input object to an integer or float. - - If the conversion to an integer fails, it tries to convert to a float. If both - conversions fail, it returns the original object. - - :param obj: The object to be converted. - :type obj: SupportsInt | SupportsFloat | object - :return: The converted object as an integer, float, or the original object. - :rtype: int | float | object - """ - if isinstance(obj, SupportsInt): - return int(obj) - if isinstance(obj, SupportsFloat): - return float(obj) - return obj - - def four_byte_char_positions(text: str) -> list[int]: """Identify positions of 4-byte encoded characters in a UTF-8 string. diff --git a/language_tool_python/utils.py b/language_tool_python/utils.py index 4cfeab6..34c9294 100644 --- a/language_tool_python/utils.py +++ b/language_tool_python/utils.py @@ -10,16 +10,13 @@ import urllib.parse from enum import Enum from pathlib import Path -from shutil import which from typing import TYPE_CHECKING, Protocol, runtime_checkable import psutil -from ._compat import deprecated -from .exceptions import JavaError, PathError +from .exceptions import PathError if TYPE_CHECKING: - from .config_file import LanguageToolConfig from .match import Match logger = logging.getLogger(__name__) @@ -192,201 +189,6 @@ def get_language_tool_download_path() -> Path: return path -@deprecated( - ( - "This function is no longer used internally and will be removed in 4.0.\n" - "Replace its usage by an inline alternative." - ), - stacklevel=2, -) -def find_existing_language_tool_downloads(download_folder: Path) -> list[Path]: - """Find existing LanguageTool downloads in the specified folder. - - This function searches for directories in the given download folder - that match the pattern 'LanguageTool*' and returns a list of their paths. - - :param download_folder: The folder where LanguageTool downloads are stored. - :type download_folder: Path - :return: A list of paths to the existing LanguageTool download directories. - :rtype: list[Path] - - .. deprecated:: 3.3.0 - This function is no longer used internally and will be removed in 4.0. - """ - return [path for path in download_folder.glob("LanguageTool*") if path.is_dir()] - - -@deprecated( - "This function is no longer used internally and will be removed in 4.0.", - stacklevel=2, -) -def _extract_version(path: Path) -> tuple[int, int]: - """Extract the version number from a LanguageTool directory path. - - This function parses the directory name to extract the version information - from LanguageTool installation folders that follow the naming convention - 'LanguageTool-X.Y-SNAPSHOT'. - - :param path: The path to the LanguageTool directory - :type path: Path - :return: The parsed version tuple extracted from the directory name - :rtype: tuple[int, int] - :raises ValueError: If the directory name doesn't start with 'LanguageTool-' - - .. deprecated:: 3.3.0 - This function is no longer used internally and will be removed in 4.0. - """ - if not path.name.startswith("LanguageTool-"): - err = f"Invalid LanguageTool folder name: {path.name}" - raise ValueError(err) - # Handle LanguageTool- prefix - version_str = path.name.removeprefix("LanguageTool-") - # Handle both -SNAPSHOT and -snapshot suffixes - version_str = version_str.removesuffix("-SNAPSHOT").removesuffix("-snapshot") - return version_tuple(version_str) - - -@deprecated( - ( - "This function is no longer used internally and will be removed in 4.0.\n" - "Use instead " - "language_tool_python.download_lt.LocalLanguageTool." - "get_latest_installed_version." - ), - stacklevel=2, -) -def get_language_tool_directory() -> Path: - """Get the directory path of the LanguageTool installation. - - This function checks the download folder for LanguageTool installations, - verifies that the folder exists and is a directory, and returns the path - to the latest version of LanguageTool found in the directory. - - :raises NotADirectoryError: If the download folder path is not a valid directory. - :raises FileNotFoundError: If no LanguageTool installation is found in the download - folder. - :return: The path to the latest version of LanguageTool found in the directory. - :rtype: Path - - .. deprecated:: 3.3.0 - This function is no longer used internally and will be removed in 4.0. - """ - download_folder = get_language_tool_download_path() - if not download_folder.is_dir(): - err = f"LanguageTool directory path is not a valid directory {download_folder}." - raise NotADirectoryError(err) - language_tool_path_list = find_existing_language_tool_downloads(download_folder) - - if not len(language_tool_path_list): - err = f"LanguageTool not found in {download_folder}." - raise FileNotFoundError(err) - - # Return the latest version found in the directory. - latest: Path = max( - language_tool_path_list, - key=_extract_version, - ) - logger.debug("Using LanguageTool directory: %s", latest) - return latest - - -@deprecated( - ( - "This function is no longer used internally and will be removed in 4.0.\n" - "Use instead language_tool_python.download_lt.LocalLanguageTool.get_server_cmd." - ), - stacklevel=2, -) -def get_server_cmd( - port: int | None = None, - config: LanguageToolConfig | None = None, -) -> list[str]: - """Generate the command to start the LanguageTool HTTP server. - - :param port: Optional; The port number on which the server should run. If not - provided, the default port will be used. - :type port: int | None - :param config: Optional; The configuration for the LanguageTool server. If not - provided, default configuration will be used. - :type config: LanguageToolConfig | None - :return: A list of command line arguments to start the LanguageTool HTTP server. - :rtype: list[str] - :raises JavaError: If the Java executable cannot be found. - :raises PathError: If the LanguageTool JAR file cannot be found in the specified - directory. - - .. deprecated:: 3.3.0 - This function is no longer used internally and will be removed in 4.0. - """ - java_path, jar_path = get_jar_info() - cmd = [ - str(java_path), - "-cp", - str(jar_path), - "org.languagetool.server.HTTPServer", - ] - - if port is not None: - cmd += ["-p", str(port)] - - if config is not None: - cmd += ["--config", config.path] - - logger.debug("LanguageTool server command: %r", cmd) - return cmd - - -@deprecated( - "This function is no longer used internally and will be removed in 4.0.", - stacklevel=2, -) -def get_jar_info() -> tuple[Path, Path]: - """Retrieve the path to the Java executable and the LanguageTool JAR file. - - This function searches for the Java executable in the system's PATH and - locates the LanguageTool JAR file either in a directory specified by an - environment variable or in a default download directory. - - :raises JavaError: If the Java executable cannot be found. - :raises PathError: If the LanguageTool JAR file cannot be found in the specified - directory. - :return: A tuple containing the path to the Java executable and the path to the - LanguageTool JAR file. - :rtype: tuple[Path, Path] - - .. deprecated:: 3.3.0 - This function is no longer used internally and will be removed in 4.0. - """ - java_path_str = which("java") - if not java_path_str: - err = "can't find Java" - raise JavaError(err) - java_path = Path(java_path_str) - - # Use the env var to the jar directory if it is defined - # otherwise look in the download directory - configured_jar_dir = os.environ.get(LTP_JAR_DIR_PATH_ENV_VAR) - jar_dir_path = ( - Path(configured_jar_dir) - if configured_jar_dir is not None - else get_language_tool_directory() - ) - jar_path = None - for jar_name in JAR_NAMES: - for jar_path in jar_dir_path.glob(jar_name): - if jar_path.is_file(): - logger.debug("Found LanguageTool JAR: %s", jar_path) - break - else: - jar_path = None - if jar_path: - break - else: - err = f"can't find languagetool-standalone in {jar_dir_path!r}" - raise PathError(err) - return java_path, jar_path - - def get_locale_language() -> str: """Get the current locale language. @@ -446,32 +248,6 @@ def __bool__(self) -> bool: ... -@deprecated( - "This protocol is no longer used internally and will be removed in 4.0.", - stacklevel=2, -) -@runtime_checkable -class SupportsInt(Protocol): - """Protocol for types that can be converted to an integer value.""" - - def __int__(self) -> int: - """Define the interface for types that can be converted to an integer.""" - ... - - -@deprecated( - "This protocol is no longer used internally and will be removed in 4.0.", - stacklevel=2, -) -@runtime_checkable -class SupportsFloat(Protocol): - """Protocol for types that can be converted to a float value.""" - - def __float__(self) -> float: - """Define the interface for types that can be converted to a float.""" - ... - - def version_tuple(v: str) -> tuple[int, int]: """Convert a version string into a tuple of integers. diff --git a/tests/test_deprecated.py b/tests/test_deprecated.py deleted file mode 100644 index 4d312f2..0000000 --- a/tests/test_deprecated.py +++ /dev/null @@ -1,100 +0,0 @@ -"""Tests for the deprecated decorator.""" - -from __future__ import annotations - -import warnings - -from language_tool_python._compat import deprecated - -EXPECTED_CUSTOM_WARNING_RESULT = 42 -EXPECTED_FUNCTION_SUM = 5 -EXPECTED_WARNING_COUNT = 3 - - -def test_deprecated_emits_warning() -> None: - """Test that the deprecated decorator emits a DeprecationWarning.""" - - @deprecated("This function is deprecated") - def old_function() -> str: - return "result" - - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") - result: str = old_function() - - assert len(w) == 1 - assert issubclass(w[0].category, DeprecationWarning) - assert "This function is deprecated" in str(w[0].message) - assert result == "result" - - -def test_deprecated_with_custom_category() -> None: - """Test that the deprecated decorator can use a custom warning category.""" - - @deprecated("This is a user warning", category=UserWarning) - def old_function() -> int: - return 42 - - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") - result: int = old_function() - - assert len(w) == 1 - assert issubclass(w[0].category, UserWarning) - assert "This is a user warning" in str(w[0].message) - assert result == EXPECTED_CUSTOM_WARNING_RESULT - - -def test_deprecated_preserves_function_signature() -> None: - """Test that the deprecated decorator preserves function metadata.""" - - @deprecated("Old function") - def my_function(x: int, y: int) -> int: - """Add two numbers.""" - return x + y - - with warnings.catch_warnings(record=True): - assert my_function.__name__ == "my_function" - assert my_function.__doc__ is not None - assert "Add two numbers" in my_function.__doc__ - assert my_function(2, 3) == EXPECTED_FUNCTION_SUM - - -def test_deprecated_with_multiple_calls() -> None: - """Test that warning is emitted on each call.""" - - @deprecated("Deprecated function") - def func() -> str: - return "value" - - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") - func() - func() - func() - - assert len(w) == EXPECTED_WARNING_COUNT - assert all(issubclass(warning.category, DeprecationWarning) for warning in w) - - -def test_deprecated_with_args_and_kwargs() -> None: - """Test that deprecated decorator works with functions that have args and kwargs.""" - - @deprecated("This function is obsolete") - def complex_function( - a: int, - b: int, - *args: int, - c: int | None = None, - **kwargs: int, - ) -> tuple[int, int, tuple[int, ...], int | None, dict[str, int]]: - return (a, b, args, c, kwargs) - - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") - result: tuple[int, int, tuple[int, ...], int | None, dict[str, int]] = ( - complex_function(1, 2, 3, 4, c=5, d=6, e=7) - ) - - assert len(w) == 1 - assert result == (1, 2, (3, 4), 5, {"d": 6, "e": 7})