chore(ci): add ruff and mypy, wire them into CI
Back the shipped py.typed promise with an enforced type check and a linter: - Add ruff + mypy (+ types-requests) to the dev extras and dev requirements, with [tool.ruff]/[tool.mypy] config in pyproject.toml (mypy checks the package, not the tests). - Add a lint job to the Forgejo workflow running ruff check + mypy. - Fix the issues this surfaced: type FileLock.fd as TextIO | None, add a targeted type: ignore for the intentional socket.getaddrinfo monkeypatch, and drop an unused import. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
c553654aae
commit
9411c83a45
6 changed files with 40 additions and 3 deletions
|
|
@ -6,6 +6,24 @@ on:
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
lint:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: "3.12"
|
||||||
|
|
||||||
|
- name: Install package with dev extras
|
||||||
|
run: pip install -e ".[dev]"
|
||||||
|
|
||||||
|
- name: Ruff (lint)
|
||||||
|
run: ruff check src/ tests/
|
||||||
|
|
||||||
|
- name: Mypy (type check)
|
||||||
|
run: mypy
|
||||||
|
|
||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,9 @@ dependencies = [
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
dev = [
|
dev = [
|
||||||
"pytest>=7.4",
|
"pytest>=7.4",
|
||||||
|
"ruff>=0.6",
|
||||||
|
"mypy>=1.11",
|
||||||
|
"types-requests",
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.scripts]
|
[project.scripts]
|
||||||
|
|
@ -27,3 +30,13 @@ where = ["src"]
|
||||||
|
|
||||||
[tool.setuptools.package-data]
|
[tool.setuptools.package-data]
|
||||||
llamacppctl = ["py.typed"]
|
llamacppctl = ["py.typed"]
|
||||||
|
|
||||||
|
[tool.ruff]
|
||||||
|
target-version = "py310"
|
||||||
|
line-length = 100
|
||||||
|
|
||||||
|
[tool.mypy]
|
||||||
|
python_version = "3.10"
|
||||||
|
files = ["src/llamacppctl"]
|
||||||
|
warn_unused_ignores = true
|
||||||
|
warn_redundant_casts = true
|
||||||
|
|
|
||||||
|
|
@ -2,3 +2,6 @@
|
||||||
# in pyproject.toml).
|
# in pyproject.toml).
|
||||||
-r requirements.txt
|
-r requirements.txt
|
||||||
pytest>=7.4
|
pytest>=7.4
|
||||||
|
ruff>=0.6
|
||||||
|
mypy>=1.11
|
||||||
|
types-requests
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ import shlex
|
||||||
import subprocess
|
import subprocess
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
from .schema import ServerConfig
|
from .schema import ServerConfig
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ from __future__ import annotations
|
||||||
|
|
||||||
import fcntl
|
import fcntl
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import TextIO
|
||||||
|
|
||||||
|
|
||||||
class LockError(RuntimeError):
|
class LockError(RuntimeError):
|
||||||
|
|
@ -17,7 +18,7 @@ class LockError(RuntimeError):
|
||||||
class FileLock:
|
class FileLock:
|
||||||
def __init__(self, path: Path):
|
def __init__(self, path: Path):
|
||||||
self.path = Path(path)
|
self.path = Path(path)
|
||||||
self.fd = None
|
self.fd: TextIO | None = None
|
||||||
|
|
||||||
def __enter__(self) -> "FileLock":
|
def __enter__(self) -> "FileLock":
|
||||||
self.path.parent.mkdir(parents=True, exist_ok=True)
|
self.path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
|
||||||
|
|
@ -202,7 +202,10 @@ def _pin_dns(host: str, allowed_ips: list):
|
||||||
)
|
)
|
||||||
return results
|
return results
|
||||||
|
|
||||||
socket.getaddrinfo = pinned
|
# Intentional monkeypatch: pin DNS to the pre-validated addresses for the
|
||||||
|
# duration of the request (SSRF/rebinding defense). Signature differs from
|
||||||
|
# the stdlib function, hence the targeted ignore.
|
||||||
|
socket.getaddrinfo = pinned # type: ignore[assignment]
|
||||||
try:
|
try:
|
||||||
yield
|
yield
|
||||||
finally:
|
finally:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue