Skip to content

Commit 22551c8

Browse files
authored
Merge pull request #136 from mdevolde/pathlib
refactor: replaced os.path uses by pathlib (breaking)
2 parents 1dabc06 + e53c7bc commit 22551c8

File tree

5 files changed

+88
-50
lines changed

5 files changed

+88
-50
lines changed

language_tool_python/config_file.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import atexit
44
import logging
5-
import os
65
import tempfile
76
from dataclasses import dataclass
87
from pathlib import Path
@@ -246,6 +245,6 @@ def _create_temp_file(self) -> str:
246245
logger.debug("Created temporary LanguageTool config file at %s", temp_name)
247246

248247
# Remove file when program exits.
249-
atexit.register(lambda: os.unlink(temp_name))
248+
atexit.register(lambda: Path(temp_name).unlink(missing_ok=True))
250249

251250
return temp_name

language_tool_python/download_lt.py

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import tempfile
88
import zipfile
99
from datetime import datetime
10+
from pathlib import Path
1011
from shutil import which
1112
from typing import IO, Dict, Optional, Tuple
1213
from urllib.parse import urljoin
@@ -200,29 +201,29 @@ def http_get(
200201
progress.close()
201202

202203

203-
def unzip_file(temp_file_name: str, directory_to_extract_to: str) -> None:
204+
def unzip_file(temp_file_name: str, directory_to_extract_to: Path) -> None:
204205
"""
205206
Unzips a zip file to a specified directory.
206207
207208
:param temp_file_name: A temporary file object representing the zip file to be extracted.
208209
:type temp_file_name: str
209210
:param directory_to_extract_to: The directory where the contents of the zip file will be extracted.
210-
:type directory_to_extract_to: str
211+
:type directory_to_extract_to: Path
211212
"""
212213

213214
logger.info("Unzipping %s to %s", temp_file_name, directory_to_extract_to)
214215
with zipfile.ZipFile(temp_file_name, "r") as zip_ref:
215216
zip_ref.extractall(directory_to_extract_to)
216217

217218

218-
def download_zip(url: str, directory: str) -> None:
219+
def download_zip(url: str, directory: Path) -> None:
219220
"""
220221
Downloads a ZIP file from the given URL and extracts it to the specified directory.
221222
222223
:param url: The URL of the ZIP file to download.
223224
:type url: str
224225
:param directory: The directory where the ZIP file should be extracted.
225-
:type directory: str
226+
:type directory: Path
226227
"""
227228
logger.info("Downloading from %s to %s", url, directory)
228229
# Download file using a context manager.
@@ -232,14 +233,13 @@ def download_zip(url: str, directory: str) -> None:
232233
# Extract zip file to path.
233234
unzip_file(temp_name, directory)
234235
# Remove the temporary file.
235-
os.remove(temp_name)
236+
Path(temp_name).unlink(missing_ok=True)
236237

237238

238239
def download_lt(language_tool_version: str = LTP_DOWNLOAD_VERSION) -> None:
239240
"""
240241
Downloads and extracts the specified version of LanguageTool.
241-
This function checks for Java compatibility, creates the necessary download
242-
directory if it does not exist, and downloads the specified version of
242+
This function checks for Java compatibility, and downloads the specified version of
243243
LanguageTool if it is not already present.
244244
245245
:param language_tool_version: The version of LanguageTool to download. If not
@@ -259,10 +259,7 @@ def download_lt(language_tool_version: str = LTP_DOWNLOAD_VERSION) -> None:
259259
if os.environ.get(LTP_JAR_DIR_PATH_ENV_VAR):
260260
return
261261

262-
# Make download path, if it doesn't exist.
263-
os.makedirs(download_folder, exist_ok=True)
264-
265-
if not os.path.isdir(download_folder):
262+
if not download_folder.is_dir():
266263
err = f"Download folder {download_folder} is not a directory."
267264
raise PathError(err)
268265
old_path_list = find_existing_language_tool_downloads(download_folder)
@@ -282,11 +279,11 @@ def download_lt(language_tool_version: str = LTP_DOWNLOAD_VERSION) -> None:
282279
f"You can also use 'latest' to download the latest snapshot of LanguageTool."
283280
)
284281
raise ValueError(err)
285-
dirname, _ = os.path.splitext(filename)
282+
dirname = Path(filename).stem
286283
dirname = dirname.replace("latest", LT_SNAPSHOT_CURRENT_VERSION)
287284
if version == "latest":
288285
dirname = f"LanguageTool-{LT_SNAPSHOT_CURRENT_VERSION}"
289-
extract_path = os.path.join(download_folder, dirname)
286+
extract_path = download_folder / dirname
290287

291288
if extract_path not in old_path_list:
292289
download_zip(language_tool_download_url, download_folder)

language_tool_python/server.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
import http.client
66
import json
77
import logging
8-
import os
98
import random
109
import socket
1110
import subprocess
1211
import time
1312
import urllib.parse
1413
import warnings
14+
from pathlib import Path
1515
from typing import Any, Dict, List, Literal, Optional, Set
1616

1717
import psutil
@@ -410,7 +410,7 @@ def disable_spellchecking(self) -> None:
410410
self.disabled_categories.update(self._spell_checking_categories)
411411

412412
@staticmethod
413-
def _get_valid_spelling_file_path() -> str:
413+
def _get_valid_spelling_file_path() -> Path:
414414
"""
415415
Retrieve the valid file path for the spelling file.
416416
This function constructs the file path for the spelling file used by the
@@ -420,14 +420,19 @@ def _get_valid_spelling_file_path() -> str:
420420
:raises FileNotFoundError: If the spelling file does not exist at the
421421
constructed path.
422422
:return: The valid file path for the spelling file.
423-
:rtype: str
423+
:rtype: Path
424424
"""
425425
library_path = get_language_tool_directory()
426-
spelling_file_path = os.path.join(
427-
library_path,
428-
"org/languagetool/resource/en/hunspell/spelling.txt",
426+
spelling_file_path = (
427+
library_path
428+
/ "org"
429+
/ "languagetool"
430+
/ "resource"
431+
/ "en"
432+
/ "hunspell"
433+
/ "spelling.txt"
429434
)
430-
if not os.path.exists(spelling_file_path):
435+
if not spelling_file_path.exists():
431436
err = (
432437
f"Failed to find the spellings file at {spelling_file_path}\n"
433438
" Please file an issue at "

language_tool_python/utils.py

Lines changed: 57 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
"""Utility functions for the LanguageTool library."""
22

33
import contextlib
4-
import glob
54
import locale
65
import logging
76
import os
87
import subprocess
98
import urllib.parse
109
from enum import Enum
10+
from pathlib import Path
1111
from shutil import which
1212
from typing import Any, List, Optional, Tuple
1313

1414
import psutil
15+
from packaging import version
1516

1617
from .config_file import LanguageToolConfig
1718
from .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:

tests/test_major_functionality.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -332,14 +332,18 @@ def test_spellcheck_en_gb():
332332

333333
def test_session_only_new_spellings():
334334
import hashlib
335-
import os
336335

337336
import language_tool_python
338337

339338
library_path = language_tool_python.utils.get_language_tool_directory()
340-
spelling_file_path = os.path.join(
341-
library_path,
342-
"org/languagetool/resource/en/hunspell/spelling.txt",
339+
spelling_file_path = (
340+
library_path
341+
/ "org"
342+
/ "languagetool"
343+
/ "resource"
344+
/ "en"
345+
/ "hunspell"
346+
/ "spelling.txt"
343347
)
344348
with open(spelling_file_path, "r") as spelling_file:
345349
initial_spelling_file_contents = spelling_file.read()

0 commit comments

Comments
 (0)