Skip to content

Commit 862fbbf

Browse files
authored
refactor: use linters in the env, fix mypy warns, improve makefiles (jxmorris12#172)
1 parent e08131f commit 862fbbf

17 files changed

Lines changed: 1095 additions & 720 deletions

.github/workflows/test.yml

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -41,21 +41,9 @@ jobs:
4141
distribution: 'temurin'
4242
java-version: '26'
4343

44-
- name: Install dependencies
45-
run: |
46-
uv sync --group tests --frozen
47-
48-
- name: Verify installed packages
49-
run: |
50-
uv pip list
51-
52-
- name: Import language_tool_python
53-
run: |
54-
printf "import language_tool_python\n" | uv run python
55-
5644
- name: Test with pytest
5745
run: |
58-
uv run pytest
46+
make test
5947
6048
lint:
6149
timeout-minutes: 10
@@ -71,8 +59,7 @@ jobs:
7159
# Keep in sync ruff version with .pre-commit-config.yaml
7260
- name: Run Ruff Linter
7361
run: |
74-
uvx ruff@0.15.12 check .
75-
uvx ruff@0.15.12 format --check .
62+
make ruff-check
7663
7764
type_check:
7865
timeout-minutes: 10
@@ -84,12 +71,8 @@ jobs:
8471

8572
- name: Install uv
8673
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # 8.1.0
87-
88-
- name: Install dependencies
89-
run: |
90-
uv sync --group types --frozen
9174

9275
# Keep in sync mypy version with .pre-commit-config.yaml
9376
- name: Run Mypy Type Checker
9477
run: |
95-
uvx mypy@2.0.0
78+
make mypy-check

.pre-commit-config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
repos:
22
# Ruff for linting and formatting
33
- repo: https://github.com/astral-sh/ruff-pre-commit
4-
rev: v0.15.12 # Keep in sync with GitHub Actions workflow
4+
rev: v0.15.12 # Keep in sync ruff version with pyproject.toml
55
hooks:
66
# Run the linter
77
- id: ruff
@@ -11,7 +11,7 @@ repos:
1111

1212
# mypy for type checking
1313
- repo: https://github.com/pre-commit/mirrors-mypy
14-
rev: v2.0.0 # Keep in sync with GitHub Actions workflow
14+
rev: v2.0.0 # Keep in sync mypy version with pyproject.toml
1515
hooks:
1616
- id: mypy
1717
args: [--config-file=pyproject.toml]

CONTRIBUTING.md

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ If you want to contribute, you first need to fork the repo (and preferably creat
2020

2121
To start developing, you can install all the necessary packages in your python environment with this command (optional dependencies will be installed):
2222
```shell
23-
uv sync --group tests --group docs --group types
23+
make install
2424
```
2525

2626
When pushing commits, please use the project naming conventions, which are available in [this guide](https://www.conventionalcommits.org/en/v1.0.0/).
@@ -30,17 +30,14 @@ The documentation style used in the project is **ReStructuredText**. Please, if
3030

3131
Before creating your pull request, when you have made all your commits, you need to run this:
3232
```shell
33-
# Run linters (maybe you will have to fix some issues)
34-
uvx ruff@0.15.12 check language_tool_python tests
33+
# Format your code
34+
make format
3535

36-
# Format code
37-
uvx ruff@0.15.12 format language_tool_python tests
36+
# Run linters, check code formatting and types (maybe you will have to fix some issues)
37+
make check
3838

39-
# Check types
40-
uvx mypy@2.0.0
41-
42-
# Tests
43-
pytest
39+
# Run tests (if you have added or modified some, make sure they are passing)
40+
make test
4441
```
4542

4643
Please do not manually bump the version number in [pyproject.toml](./pyproject.toml), this will be handled by the maintainers during release.

Makefile

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,48 @@
1-
.PHONY: default check test doc publish
1+
.PHONY: default install format fix ruff-check mypy-check check test doc publish
2+
3+
UV := $(shell command -v uv 2>/dev/null || true)
4+
ifeq ($(UV),)
5+
$(warning uv not found. Install uv (curl -LsSf https://astral.sh/uv/install.sh | sh) to use Makefile targets)
6+
endif
27

38
default:
4-
@echo "Usage: make [check|test|doc|publish]"
9+
@echo "Usage: make [install|format|fix|ruff-check|mypy-check|check|test|doc|publish]"
510
@exit 1
611

12+
install:
13+
uv sync --all-groups --locked
14+
15+
format:
16+
uv run --group quality --locked ruff format language_tool_python tests
17+
18+
fix:
19+
uv run --group quality --locked ruff check --fix language_tool_python tests
20+
21+
ruff-check:
22+
uv run --group quality --locked ruff check language_tool_python tests
23+
uv run --group quality --locked ruff format --check language_tool_python tests
24+
25+
mypy-check:
26+
@if uv run --locked python -c 'import sys; raise SystemExit(0 if sys.version_info >= (3, 10) else 1)'; then \
27+
uv run --group tests --group types --group quality --locked mypy; \
28+
else \
29+
echo "Skipping mypy: Python 3.10 or newer is required."; \
30+
fi
31+
732
check:
8-
uvx ruff@0.15.12 check language_tool_python tests
9-
uvx ruff@0.15.12 format --check language_tool_python tests
10-
uvx mypy@2.0.0
33+
make ruff-check
34+
make mypy-check
1135

1236
test:
13-
pytest
37+
uv run --group tests --locked pytest
1438
uvx --with defusedxml genbadge coverage --input-file coverage.xml --silent
39+
1540
doc:
16-
source ./.venv/bin/activate && uv run sphinx-apidoc -o docs/source/references language_tool_python
17-
source ./.venv/bin/activate && cd ./docs && make html
41+
uv run --group docs --locked sphinx-apidoc -o docs/source/references language_tool_python
42+
uv run --group docs --locked sphinx-build -M html docs/source docs/build
1843

1944
publish:
20-
rm -rf dist/ language_tool_python.egg-info/
45+
rm -rf dist/
2146
uv build
2247
uvx twine check dist/*
2348
uv publish

README.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -388,15 +388,16 @@ Main exceptions in `language_tool_python.exceptions`:
388388

389389
```bash
390390
# Install dev dependencies
391-
uv sync --group tests --group docs --group types
391+
make install
392+
393+
# Format code
394+
make format
392395

393396
# Lint / format / types
394-
uvx ruff@0.15.12 check .
395-
uvx ruff@0.15.12 format .
396-
uvx mypy@2.0.0
397+
make check
397398

398399
# Tests
399-
pytest
400+
make test
400401
```
401402

402403
## License

docs/requirements.txt

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,47 +2,112 @@
22
# uv export --format requirements-txt --no-hashes --group docs -o docs/requirements.txt
33
-e .
44
accessible-pygments==0.0.5
5+
# via furo
56
alabaster==0.7.16 ; python_full_version < '3.10'
7+
# via sphinx
68
alabaster==1.0.0 ; python_full_version >= '3.10'
9+
# via sphinx
710
babel==2.18.0
11+
# via sphinx
812
beautifulsoup4==4.14.3
13+
# via furo
914
certifi==2026.4.22
15+
# via requests
1016
charset-normalizer==3.4.7
17+
# via requests
1118
colorama==0.4.6 ; sys_platform == 'win32'
19+
# via
20+
# sphinx
21+
# tqdm
1222
docutils==0.21.2 ; python_full_version < '3.11'
23+
# via sphinx
1324
docutils==0.22.4 ; python_full_version >= '3.11'
25+
# via sphinx
1426
furo==2025.12.19
15-
idna==3.13
27+
idna==3.15
28+
# via requests
1629
imagesize==1.5.0 ; python_full_version < '3.10'
30+
# via sphinx
1731
imagesize==2.0.0 ; python_full_version >= '3.10'
32+
# via sphinx
1833
importlib-metadata==8.7.1 ; python_full_version < '3.10'
34+
# via sphinx
1935
jinja2==3.1.6
36+
# via sphinx
2037
markupsafe==3.0.3
38+
# via jinja2
2139
packaging==26.2
40+
# via
41+
# language-tool-python
42+
# sphinx
2243
psutil==7.2.2
44+
# via language-tool-python
2345
pygments==2.20.0
46+
# via
47+
# accessible-pygments
48+
# furo
49+
# sphinx
2450
requests==2.32.5 ; python_full_version < '3.10'
25-
requests==2.33.1 ; python_full_version >= '3.10'
51+
# via
52+
# language-tool-python
53+
# sphinx
54+
requests==2.34.2 ; python_full_version >= '3.10'
55+
# via
56+
# language-tool-python
57+
# sphinx
2658
roman-numerals==4.1.0 ; python_full_version >= '3.11'
59+
# via sphinx
2760
snowballstemmer==3.0.1
61+
# via sphinx
2862
soupsieve==2.8.3
63+
# via beautifulsoup4
2964
sphinx==7.4.7 ; python_full_version < '3.10'
65+
# via
66+
# furo
67+
# sphinx-basic-ng
68+
# sphinx-design
3069
sphinx==8.1.3 ; python_full_version == '3.10.*'
70+
# via
71+
# furo
72+
# sphinx-basic-ng
73+
# sphinx-design
3174
sphinx==9.0.4 ; python_full_version == '3.11.*'
75+
# via
76+
# furo
77+
# sphinx-basic-ng
78+
# sphinx-design
3279
sphinx==9.1.0 ; python_full_version >= '3.12'
80+
# via
81+
# furo
82+
# sphinx-basic-ng
83+
# sphinx-design
3384
sphinx-basic-ng==1.0.0b2
85+
# via furo
3486
sphinx-design==0.6.1 ; python_full_version < '3.11'
3587
sphinx-design==0.7.0 ; python_full_version >= '3.11'
3688
sphinxcontrib-applehelp==2.0.0
89+
# via sphinx
3790
sphinxcontrib-devhelp==2.0.0
91+
# via sphinx
3892
sphinxcontrib-htmlhelp==2.1.0
93+
# via sphinx
3994
sphinxcontrib-jsmath==1.0.1
95+
# via sphinx
4096
sphinxcontrib-qthelp==2.0.0
97+
# via sphinx
4198
sphinxcontrib-serializinghtml==2.0.0
99+
# via sphinx
42100
toml==0.10.2
101+
# via language-tool-python
43102
tomli==2.4.1 ; python_full_version < '3.11'
103+
# via sphinx
44104
tqdm==4.67.3
105+
# via language-tool-python
45106
typing-extensions==4.15.0
107+
# via beautifulsoup4
46108
urllib3==2.6.3 ; python_full_version < '3.10'
109+
# via requests
47110
urllib3==2.7.0 ; python_full_version >= '3.10'
111+
# via requests
48112
zipp==3.23.1 ; python_full_version < '3.10'
113+
# via importlib-metadata

language_tool_python/_deprecated.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@
88
"""
99

1010
try:
11-
from warnings import deprecated # type: ignore [attr-defined]
11+
from warnings import deprecated # type: ignore [attr-defined, unused-ignore]
1212
except ImportError:
1313
import functools
1414
from typing import Any, Callable, Optional, Type, TypeVar, cast
1515
from warnings import warn
1616

1717
F = TypeVar("F", bound=Callable[..., Any])
1818

19-
def deprecated(
19+
def deprecated( # type: ignore [no-redef, unused-ignore]
2020
message: str,
2121
/,
2222
*,

language_tool_python/download_lt.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ def confirm_java_compatibility(
242242
@deprecated(
243243
"This function is no longer used internally and will be removed in 4.0.",
244244
stacklevel=2,
245-
) # type: ignore
245+
) # type: ignore[untyped-decorator, unused-ignore]
246246
def get_common_prefix(z: zipfile.ZipFile) -> Optional[str]:
247247
"""
248248
Determine the common prefix of all file names in a zip archive.
@@ -265,7 +265,7 @@ def get_common_prefix(z: zipfile.ZipFile) -> Optional[str]:
265265
@deprecated(
266266
"This function is no longer used internally and will be removed in 4.0.",
267267
stacklevel=2,
268-
) # type: ignore
268+
) # type: ignore[untyped-decorator, unused-ignore]
269269
def http_get(
270270
url: str,
271271
out_file: IO[bytes],
@@ -287,14 +287,14 @@ def http_get(
287287
# Fallback to default behavior if the extracted version is not supported
288288
local_lt = LocalLanguageTool.from_version_name(LTP_DOWNLOAD_VERSION)
289289

290-
with local_lt._get_remote_zip(out_file, proxies=proxies): # type: ignore
290+
with local_lt._get_remote_zip(out_file, proxies=proxies):
291291
pass
292292

293293

294294
@deprecated(
295295
"This function is no longer used internally and will be removed in 4.0.",
296296
stacklevel=2,
297-
) # type: ignore
297+
) # type: ignore[untyped-decorator, unused-ignore]
298298
def unzip_file(temp_file_name: str, directory_to_extract_to: Path) -> None:
299299
"""
300300
Unzips a zip file to a specified directory.
@@ -323,7 +323,7 @@ def unzip_file(temp_file_name: str, directory_to_extract_to: Path) -> None:
323323
@deprecated(
324324
"This function is no longer used internally and will be removed in 4.0.",
325325
stacklevel=2,
326-
) # type: ignore
326+
) # type: ignore[untyped-decorator, unused-ignore]
327327
def download_zip(url: str, directory: Path) -> None:
328328
"""
329329
Downloads a ZIP file from the given URL and extracts it to the specified directory.
@@ -339,18 +339,18 @@ def download_zip(url: str, directory: Path) -> None:
339339
logger.info("Downloading from %s to %s", url, directory)
340340
# Download file using a context manager.
341341
with tempfile.NamedTemporaryFile(suffix=".zip", delete=False) as downloaded_file:
342-
http_get(url, downloaded_file) # type: ignore
342+
http_get(url, downloaded_file)
343343
temp_name = downloaded_file.name
344344
# Extract zip file to path.
345-
unzip_file(temp_name, directory) # type: ignore
345+
unzip_file(temp_name, directory)
346346
# Remove the temporary file.
347347
Path(temp_name).unlink(missing_ok=True)
348348

349349

350350
@deprecated(
351351
"This function is no longer used internally and will be removed in 4.0.\nUse instead language_tool_python.download_lt.LocalLanguageTool.download.",
352352
stacklevel=2,
353-
) # type: ignore
353+
) # type: ignore[untyped-decorator, unused-ignore]
354354
def download_lt(language_tool_version: str = LTP_DOWNLOAD_VERSION) -> None:
355355
"""
356356
Downloads and extracts the specified version of LanguageTool.
@@ -731,7 +731,11 @@ def __lt__(self, other: object) -> bool:
731731
# At this point, both objects are the same type, so version_into will be the same type
732732
self_version = self.version_into
733733
other_version = other.version_into
734-
return self_version < other_version # type: ignore
734+
if isinstance(self_version, Version) and isinstance(other_version, Version):
735+
return self_version < other_version
736+
if isinstance(self_version, datetime) and isinstance(other_version, datetime):
737+
return self_version < other_version
738+
return NotImplemented
735739

736740

737741
class ReleaseLocalLanguageTool(LocalLanguageTool):

0 commit comments

Comments
 (0)