11"""Utility functions for the LanguageTool library."""
22
33import contextlib
4- import glob
54import locale
65import logging
76import os
87import subprocess
98import urllib .parse
109from enum import Enum
10+ from pathlib import Path
1111from shutil import which
1212from typing import Any , List , Optional , Tuple
1313
1414import psutil
15+ from packaging import version
1516
1617from .config_file import LanguageToolConfig
1718from .exceptions import JavaError , PathError
@@ -120,42 +121,65 @@ def correct(text: str, matches: List[Match]) -> str:
120121 return "" .join (ltext )
121122
122123
123- def get_language_tool_download_path () -> str :
124+ def get_language_tool_download_path () -> Path :
124125 """
125126 Get the download path for LanguageTool.
126127 This function retrieves the download path for LanguageTool from the environment variable
127128 specified by ``LTP_PATH_ENV_VAR``. If the environment variable is not set, it defaults to
128129 a path in the user's home directory under ``.cache/language_tool_python``.
130+ The function ensures that the directory exists before returning it.
129131
130132 :return: The download path for LanguageTool.
131- :rtype: str
133+ :rtype: Path
132134 """
133135 # Get download path from environment or use default.
134- return os .environ .get (
136+ path_str = os .environ .get (
135137 LTP_PATH_ENV_VAR ,
136- os . path . join ( os . path . expanduser ( "~" ), ".cache" , "language_tool_python" ),
138+ str ( Path . home () / ".cache" / "language_tool_python" ),
137139 )
140+ path = Path (path_str )
141+ path .mkdir (parents = True , exist_ok = True )
142+ return path
138143
139144
140- def find_existing_language_tool_downloads (download_folder : str ) -> List [str ]:
145+ def find_existing_language_tool_downloads (download_folder : Path ) -> List [Path ]:
141146 """
142147 Find existing LanguageTool downloads in the specified folder.
143148 This function searches for directories in the given download folder
144149 that match the pattern 'LanguageTool*' and returns a list of their paths.
145150
146151 :param download_folder: The folder where LanguageTool downloads are stored.
147- :type download_folder: str
152+ :type download_folder: Path
148153 :return: A list of paths to the existing LanguageTool download directories.
149- :rtype: List[str ]
154+ :rtype: List[Path ]
150155 """
151- return [
152- path
153- for path in glob .glob (os .path .join (download_folder , "LanguageTool*" ))
154- if os .path .isdir (path )
155- ]
156+ return [path for path in download_folder .glob ("LanguageTool*" ) if path .is_dir ()]
157+
158+
159+ def _extract_version (path : Path ) -> version .Version :
160+ """
161+ Extract the version number from a LanguageTool directory path.
156162
163+ This function parses the directory name to extract the version information
164+ from LanguageTool installation folders that follow the naming convention
165+ 'LanguageTool-X.Y-SNAPSHOT'.
157166
158- def get_language_tool_directory () -> str :
167+ :param path: The path to the LanguageTool directory
168+ :type path: Path
169+ :return: The parsed version object extracted from the directory name
170+ :rtype: version.Version
171+ :raises ValueError: If the directory name doesn't start with 'LanguageTool-'
172+ """
173+ if not path .name .startswith ("LanguageTool-" ):
174+ raise ValueError (f"Invalid LanguageTool folder name: { path .name } " )
175+ # Handle LanguageTool- prefix
176+ version_str = path .name .removeprefix ("LanguageTool-" )
177+ # Handle both -SNAPSHOT and -snapshot suffixes
178+ version_str = version_str .removesuffix ("-SNAPSHOT" ).removesuffix ("-snapshot" )
179+ return version .parse (version_str )
180+
181+
182+ def get_language_tool_directory () -> Path :
159183 """
160184 Get the directory path of the LanguageTool installation.
161185 This function checks the download folder for LanguageTool installations,
@@ -165,11 +189,11 @@ def get_language_tool_directory() -> str:
165189 :raises NotADirectoryError: If the download folder path is not a valid directory.
166190 :raises FileNotFoundError: If no LanguageTool installation is found in the download folder.
167191 :return: The path to the latest version of LanguageTool found in the directory.
168- :rtype: str
192+ :rtype: Path
169193 """
170194
171195 download_folder = get_language_tool_download_path ()
172- if not os . path . isdir ( download_folder ):
196+ if not download_folder . is_dir ( ):
173197 err = f"LanguageTool directory path is not a valid directory { download_folder } ."
174198 raise NotADirectoryError (err )
175199 language_tool_path_list = find_existing_language_tool_downloads (download_folder )
@@ -179,7 +203,10 @@ def get_language_tool_directory() -> str:
179203 raise FileNotFoundError (err )
180204
181205 # Return the latest version found in the directory.
182- latest = max (language_tool_path_list )
206+ latest = max (
207+ language_tool_path_list ,
208+ key = _extract_version ,
209+ )
183210 logger .debug ("Using LanguageTool directory: %s" , latest )
184211 return latest
185212
@@ -199,7 +226,12 @@ def get_server_cmd(
199226 :rtype: List[str]
200227 """
201228 java_path , jar_path = get_jar_info ()
202- cmd = [java_path , "-cp" , jar_path , "org.languagetool.server.HTTPServer" ]
229+ cmd = [
230+ str (java_path ),
231+ "-cp" ,
232+ str (jar_path ),
233+ "org.languagetool.server.HTTPServer" ,
234+ ]
203235
204236 if port is not None :
205237 cmd += ["-p" , str (port )]
@@ -211,7 +243,7 @@ def get_server_cmd(
211243 return cmd
212244
213245
214- def get_jar_info () -> Tuple [str , str ]:
246+ def get_jar_info () -> Tuple [Path , Path ]:
215247 """
216248 Retrieve the path to the Java executable and the LanguageTool JAR file.
217249 This function searches for the Java executable in the system's PATH and
@@ -221,13 +253,14 @@ def get_jar_info() -> Tuple[str, str]:
221253 :raises JavaError: If the Java executable cannot be found.
222254 :raises PathError: If the LanguageTool JAR file cannot be found in the specified directory.
223255 :return: A tuple containing the path to the Java executable and the path to the LanguageTool JAR file.
224- :rtype: Tuple[str, str ]
256+ :rtype: Tuple[Path, Path ]
225257 """
226258
227- java_path = which ("java" )
228- if not java_path :
259+ java_path_str = which ("java" )
260+ if not java_path_str :
229261 err = "can't find Java"
230262 raise JavaError (err )
263+ java_path = Path (java_path_str )
231264
232265 # Use the env var to the jar directory if it is defined
233266 # otherwise look in the download directory
@@ -237,8 +270,8 @@ def get_jar_info() -> Tuple[str, str]:
237270 )
238271 jar_path = None
239272 for jar_name in JAR_NAMES :
240- for jar_path in glob .glob (os . path . join ( jar_dir_name , jar_name ) ):
241- if os . path . isfile ( jar_path ):
273+ for jar_path in Path ( jar_dir_name ) .glob (jar_name ):
274+ if jar_path . is_file ( ):
242275 logger .debug ("Found LanguageTool JAR: %s" , jar_path )
243276 break
244277 else :
0 commit comments