This is an automated email from the ASF dual-hosted git repository.

cgivre pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/drill-mcp.git


The following commit(s) were added to refs/heads/main by this push:
     new a89c7ce  Add Python 3.14, mypy, and lowest-deps jobs to CI
a89c7ce is described below

commit a89c7ce87f323ea1e20fb2b0271251ea22cbc8f8
Author: cgivre <[email protected]>
AuthorDate: Tue Oct 6 09:46:17 2026 -0400

    Add Python 3.14, mypy, and lowest-deps jobs to CI
    
    Test on 3.14, type-check with mypy in the lint job, and add a job that
    installs the oldest version each dependency floor allows so a stale
    floor fails CI. Annotate guard helpers with exp.Expr, which sqlglot 30
    returns from parse().
---
 .github/workflows/ci.yml | 25 ++++++++++++++++++++++++-
 drill_mcp/guard.py       |  6 +++---
 pyproject.toml           | 17 ++++++++++++++++-
 3 files changed, 43 insertions(+), 5 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index d49a5d7..cc99e51 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -34,7 +34,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        python-version: ["3.11", "3.12", "3.13"]
+        python-version: ["3.11", "3.12", "3.13", "3.14"]
     steps:
       - uses: actions/checkout@v7
 
@@ -52,6 +52,26 @@ jobs:
       - name: Test
         run: pytest
 
+  # Installs the oldest version of every direct dependency the floors in
+  # pyproject.toml allow, so a floor that has quietly stopped being true
+  # fails here instead of on a user's machine.
+  lowest-deps:
+    name: tests (lowest deps)
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v7
+
+      - uses: astral-sh/setup-uv@v10
+        with:
+          python-version: "3.11"
+          activate-environment: true
+
+      - name: Install
+        run: uv pip install --resolution lowest-direct -e ".[dev]"
+
+      - name: Test
+        run: pytest
+
   lint:
     name: lint
     runs-on: ubuntu-latest
@@ -71,3 +91,6 @@ jobs:
 
       - name: Ruff format check
         run: ruff format --check .
+
+      - name: Type check
+        run: mypy
diff --git a/drill_mcp/guard.py b/drill_mcp/guard.py
index a5919d5..d264ecb 100644
--- a/drill_mcp/guard.py
+++ b/drill_mcp/guard.py
@@ -183,7 +183,7 @@ def _check(sql: str, policy: Policy, depth: int) -> None:
     _check_write(statement, policy, depth)
 
 
-def _check_write(statement: exp.Expression, policy: Policy, depth: int) -> 
None:
+def _check_write(statement: exp.Expr, policy: Policy, depth: int) -> None:
     if isinstance(statement, _READ_TYPES):
         embedded = statement.find(*_EMBEDDED_WRITE_TYPES)
         if embedded is not None:
@@ -251,7 +251,7 @@ def _explain_body(statement: exp.Command) -> str:
     return re.sub(r"^\s*PLAN\s+FOR\s+", "", text, flags=re.IGNORECASE)
 
 
-def _write_targets(statement: exp.Expression) -> list[exp.Table]:
+def _write_targets(statement: exp.Expr) -> list[exp.Table]:
     # sqlglot >= 30.20 puts DROP targets in a `tables` list (DROP TABLE a, b);
     # older releases and CREATE use `this`. Any non-Table target fails closed.
     targets = statement.args.get("tables") or [statement.this]
@@ -263,7 +263,7 @@ def _schema_prefix(table: exp.Table) -> str:
     return ".".join(part for part in (table.catalog, table.db) if part)
 
 
-def _check_hidden(statement: exp.Expression, policy: Policy) -> None:
+def _check_hidden(statement: exp.Expr, policy: Policy) -> None:
     if not policy.hidden_schemas:
         return
 
diff --git a/pyproject.toml b/pyproject.toml
index 75bb62b..c0aeeba 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -46,10 +46,25 @@ dependencies = [
 [project.optional-dependencies]
 jdbc = ["jaydebeapi>=1.2.3", "JPype1>=1.5"]
 kerberos = ["httpx-gssapi>=0.3"]
-dev = ["pytest>=8.0", "respx>=0.21", "pytest-cov>=5.0", "ruff>=0.6"]
+dev = [
+    "pytest>=8.0",
+    "respx>=0.21",
+    "pytest-cov>=5.0",
+    "ruff>=0.6",
+    "mypy>=1.10",
+    "types-PyYAML>=6.0",
+]
 
 [project.scripts]
 drill-mcp = "drill_mcp.server:main"
 
+[tool.mypy]
+files = ["drill_mcp"]
+
+# Optional extras (jdbc, kerberos) are not installed in CI and ship no stubs.
+[[tool.mypy.overrides]]
+module = ["jaydebeapi", "httpx_gssapi"]
+ignore_missing_imports = true
+
 [tool.pytest.ini_options]
 testpaths = ["tests"]

Reply via email to