From 9411c83a457d24b894c280fc44ebe8f66ea620c7 Mon Sep 17 00:00:00 2001 From: dschlueter Date: Tue, 7 Jul 2026 10:28:32 +0200 Subject: [PATCH] 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 --- .forgejo/workflows/ci.yml | 18 ++++++++++++++++++ pyproject.toml | 13 +++++++++++++ requirements-dev.txt | 3 +++ src/llamacppctl/docker_ops.py | 1 - src/llamacppctl/lock_ops.py | 3 ++- src/llamacppctl/prompt_io.py | 5 ++++- 6 files changed, 40 insertions(+), 3 deletions(-) diff --git a/.forgejo/workflows/ci.yml b/.forgejo/workflows/ci.yml index 931d0ea..181a577 100644 --- a/.forgejo/workflows/ci.yml +++ b/.forgejo/workflows/ci.yml @@ -6,6 +6,24 @@ on: pull_request: 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: runs-on: ubuntu-latest strategy: diff --git a/pyproject.toml b/pyproject.toml index 4a3e8d0..55eea2a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,6 +17,9 @@ dependencies = [ [project.optional-dependencies] dev = [ "pytest>=7.4", + "ruff>=0.6", + "mypy>=1.11", + "types-requests", ] [project.scripts] @@ -27,3 +30,13 @@ where = ["src"] [tool.setuptools.package-data] 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 diff --git a/requirements-dev.txt b/requirements-dev.txt index 167e7b9..697d674 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -2,3 +2,6 @@ # in pyproject.toml). -r requirements.txt pytest>=7.4 +ruff>=0.6 +mypy>=1.11 +types-requests diff --git a/src/llamacppctl/docker_ops.py b/src/llamacppctl/docker_ops.py index 08e8f44..40c0669 100644 --- a/src/llamacppctl/docker_ops.py +++ b/src/llamacppctl/docker_ops.py @@ -11,7 +11,6 @@ import shlex import subprocess from dataclasses import dataclass from pathlib import Path -from typing import Optional from .schema import ServerConfig diff --git a/src/llamacppctl/lock_ops.py b/src/llamacppctl/lock_ops.py index dd177d4..c3b7d57 100644 --- a/src/llamacppctl/lock_ops.py +++ b/src/llamacppctl/lock_ops.py @@ -8,6 +8,7 @@ from __future__ import annotations import fcntl from pathlib import Path +from typing import TextIO class LockError(RuntimeError): @@ -17,7 +18,7 @@ class LockError(RuntimeError): class FileLock: def __init__(self, path: Path): self.path = Path(path) - self.fd = None + self.fd: TextIO | None = None def __enter__(self) -> "FileLock": self.path.parent.mkdir(parents=True, exist_ok=True) diff --git a/src/llamacppctl/prompt_io.py b/src/llamacppctl/prompt_io.py index ee04ac5..32a8b67 100644 --- a/src/llamacppctl/prompt_io.py +++ b/src/llamacppctl/prompt_io.py @@ -202,7 +202,10 @@ def _pin_dns(host: str, allowed_ips: list): ) 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: yield finally: