From 04484adffb79a3ffeb0ceac26bda97727f9cf4e4 Mon Sep 17 00:00:00 2001 From: Christophe Pettus Date: Sun, 21 Jun 2026 02:13:48 -0700 Subject: [PATCH] chore: parameterize bare list/tuple generics; enforce disallow_any_generics T1: A py.typed package should not leak implicit-Any element types to consumers. Parameterize the three bare generics in engine.py public signatures (format_params, create, _insert_rows) to list[Any] / list[tuple[Any, ...]], and enable mypy's disallow_any_generics so the generics cannot silently regress. Pure annotation change; no runtime effect. Closes T1. --- ISSUES.md | 2 +- coddpiece/engine.py | 6 +++--- pyproject.toml | 4 ++++ 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/ISSUES.md b/ISSUES.md index 81e8808..fedbd42 100644 --- a/ISSUES.md +++ b/ISSUES.md @@ -83,7 +83,7 @@ Legend: `[ ]` open · `[x]` resolved · `[~]` won't fix / by design. ## P5 — Typing -- [ ] **T1 · Bare `list`/`tuple` generics in public signatures.** +- [x] **T1 · Bare `list`/`tuple` generics in public signatures.** `Dialect.format_params(params: list)`, `Engine._insert_rows(rows: list[tuple])`, `Engine.create(rows: list[tuple] | None)`. A `py.typed` package leaks these. *Fix:* parameterize (`list[Any]`, `list[tuple[Any, ...]]`). diff --git a/coddpiece/engine.py b/coddpiece/engine.py index e455c5f..b7d1f47 100644 --- a/coddpiece/engine.py +++ b/coddpiece/engine.py @@ -140,7 +140,7 @@ def quote_identifier(self, name: str) -> str: escaped = name.replace(q, q + q) return f"{q}{escaped}{q}" - def format_params(self, params: list) -> Any: + def format_params(self, params: list[Any]) -> Any: """Format params for the connection's paramstyle.""" # named/pyformat styles require a dict keyed by placeholder name (p0, p1, ...), # matching the keys generated by placeholder(). All other styles use a list. @@ -179,7 +179,7 @@ def create( self, name: str, attrs: dict[str, type], - rows: list[tuple] | None = None, + rows: list[tuple[Any, ...]] | None = None, ) -> Relation: """Create a new table, optionally populating it with data. @@ -363,7 +363,7 @@ def _create_table(self, name: str, schema: Schema) -> None: cursor.execute(f"CREATE TABLE {table} ({col_defs})") self.connection.commit() - def _insert_rows(self, name: str, schema: Schema, rows: list[tuple]) -> None: + def _insert_rows(self, name: str, schema: Schema, rows: list[tuple[Any, ...]]) -> None: """Insert rows into a table.""" # Row values are always parameterized (upholding the no-literal # invariant). Rows are executed one at a time rather than via diff --git a/pyproject.toml b/pyproject.toml index 0b5d8a5..143fce8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -78,3 +78,7 @@ warn_redundant_casts = true warn_unreachable = true no_implicit_optional = true check_untyped_defs = true +# A py.typed package should not leak bare `list`/`dict`/`tuple` (implicit +# Any element types) to consumers. The whole package is clean under this, +# so enforce it in CI to stop the generics from silently regressing. +disallow_any_generics = true