Skip to content

Commit e5afadd

Browse files
rustyconoverclaude
andcommitted
docs: clear pydoclint baseline; run docstring gate via pytest
Finishes the docstring-consistency work: burn the pydoclint baseline down to zero and move the gate into the pytest suite. - Document all remaining flagged class attributes (ClassVar constants via `Attributes:`, exception/marker instance attrs via bare annotations) and add the missing `Args:`/`Returns:`/`Yields:` sections across vgi/. - Delete `.pydoclint-baseline` and drop the `baseline` setting — vgi/ now passes the full gate with no grandfathered exceptions. - Add tests/test_docstrings.py: runs `pydoclint vgi/` (same [tool.pydoclint] config) inside pytest, so `uv run pytest` catches docstring drift like it already catches ruff via pytest-ruff. - Remove the standalone CI "Docstring lint (pydoclint)" step now that the pytest test covers it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 0df6f8a commit e5afadd

28 files changed

Lines changed: 225 additions & 153 deletions

.github/workflows/ci.yml

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,8 @@ jobs:
5555
- name: Check linting
5656
run: uv run ruff check .
5757

58-
# Docstring consistency: documented args/attributes must match the code.
59-
# Config + frozen baseline live in pyproject.toml ([tool.pydoclint]); only
60-
# NEW violations fail. Regenerate the baseline with:
61-
# uv run pydoclint --generate-baseline=True --baseline=.pydoclint-baseline vgi/
62-
- name: Docstring lint (pydoclint)
63-
run: uv run pydoclint vgi/
58+
# Docstring consistency (pydoclint) runs inside the pytest suite via
59+
# tests/test_docstrings.py, so it's not a separate CI lint step.
6460

6561
- name: Type check (mypy)
6662
run: uv run mypy vgi/

.pydoclint-baseline

Lines changed: 0 additions & 140 deletions
This file was deleted.

pyproject.toml

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -145,12 +145,10 @@ skip_checking_raises = true
145145
# DOC301). pydoclint 0.8.x has no per-code ignore, so the noisy families are
146146
# turned off via these family switches instead.
147147
allow_init_docstring = true
148-
# Everything else still flagged (ClassVar constants like ARROW_SCHEMA /
149-
# FIXED_SCHEMA, undocumented args on private helpers + exception __init__s, and
150-
# the handful of DOC201 missing-Returns on multi-line docstrings) is frozen in
151-
# the baseline; only NEW violations fail. Regenerate after intentional changes:
148+
# No baseline: vgi/ is fully clean under this config, so the gate enforces
149+
# every in-scope violation with no grandfathered exceptions. If a future change
150+
# needs to defer cleanup, freeze the current state and re-add `baseline`:
152151
# uv run pydoclint --generate-baseline=True --baseline=.pydoclint-baseline vgi/
153-
baseline = ".pydoclint-baseline"
154152

155153
[tool.mypy]
156154
python_version = "3.13"

tests/test_docstrings.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Copyright 2025, 2026 Query Farm LLC - https://query.farm
2+
3+
"""Docstring-consistency gate.
4+
5+
Runs pydoclint over the ``vgi/`` package as part of the test suite, mirroring
6+
the CI ``Docstring lint (pydoclint)`` step. pydoclint complements ruff's ``D``
7+
rules: ruff checks docstring *shape*, while pydoclint verifies that documented
8+
arguments, return values, yields, and dataclass attributes actually match the
9+
code. Configuration lives in ``[tool.pydoclint]`` in ``pyproject.toml`` — this
10+
test invokes the same CLI, so there is no duplicated rule set.
11+
"""
12+
13+
import shutil
14+
import subprocess
15+
from pathlib import Path
16+
17+
import pytest
18+
19+
_REPO_ROOT = Path(__file__).resolve().parents[1]
20+
21+
22+
def test_pydoclint_clean() -> None:
23+
"""``vgi/`` must pass the pydoclint docstring gate (config in pyproject.toml)."""
24+
pydoclint = shutil.which("pydoclint")
25+
if pydoclint is None: # pragma: no cover - dev dependency should always be present
26+
pytest.skip("pydoclint is not installed")
27+
28+
result = subprocess.run(
29+
[pydoclint, "vgi/"],
30+
cwd=_REPO_ROOT,
31+
capture_output=True,
32+
text=True,
33+
)
34+
35+
assert result.returncode == 0, (
36+
"pydoclint found docstring violations:\n\n"
37+
f"{result.stdout}{result.stderr}\n"
38+
"Fix the docstrings, or run "
39+
"`uv run pydoclint --generate-baseline=True --baseline=.pydoclint-baseline vgi/` "
40+
"to defer them (see [tool.pydoclint] in pyproject.toml)."
41+
)

vgi/_duckdb.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ def engine_module() -> ModuleType:
3232
3333
The result is cached for the life of the process.
3434
35+
Returns:
36+
The resolved engine module (``haybarn`` if available, else ``duckdb``).
37+
3538
Raises:
3639
ImportError: if neither engine is installed.
3740

vgi/_test_fixtures/aggregate/dynamic.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,19 +190,25 @@ def combine(cls, source: DynamicState, target: DynamicState, params: ProcessPara
190190
# when DuckDB batches many partitions into shared buffers.
191191

192192
@staticmethod
193-
def _slice_to_frame( # noqa: D417
193+
def _slice_to_frame(
194194
partition: WindowPartition,
195195
subframes: list[tuple[int, int]],
196196
data_start: int,
197197
) -> pa.Table:
198198
"""Slice all partition columns to the frame rows.
199199
200200
Args:
201+
partition: The window partition whose columns are sliced.
202+
subframes: List of ``(begin, end)`` index tuples describing the
203+
row ranges to include in the frame.
201204
data_start: Index where data columns begin (header columns are
202205
``[0 .. data_start)``). NULL-drop is applied on data columns
203206
only — matches the filtering ``_do_update`` performs in the
204207
non-window path.
205208
209+
Returns:
210+
A table containing only the frame rows across all partition columns.
211+
206212
"""
207213
num_cols = partition.inputs.num_columns
208214
cols = [partition.inputs.column(i) for i in range(num_cols)]

vgi/_test_fixtures/table/filters.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,9 @@ class FilterEchoFunction(TableFunctionGenerator[FilterEchoFunctionArgs, FilterEc
116116
SELECT * FROM filter_echo(10) WHERE n >= 8
117117
Returns: rows 8-9 with pushed_filters showing "n >= 8"
118118
119+
Attributes:
120+
FIXED_SCHEMA: The fixed Arrow output schema this function always produces.
121+
119122
"""
120123

121124
class Meta:
@@ -473,6 +476,9 @@ class DictFilterEchoFunction(TableFunctionGenerator[_DictFilterEchoArgs, _DictFi
473476
SELECT * FROM dict_filter_echo(6) WHERE s = 'green'
474477
Returns: rows 1 and 4.
475478
479+
Attributes:
480+
FIXED_SCHEMA: The fixed Arrow output schema this function always produces.
481+
476482
"""
477483

478484
class Meta:
@@ -602,6 +608,9 @@ class SpatialFilterExampleFunction(TableFunctionGenerator[_SpatialFilterArgs, _S
602608
SELECT * FROM spatial_filter_example(100) WHERE geom && ST_MakeEnvelope(0, 0, 0.5, 0.5)
603609
Returns: points in the lower-left quadrant of the unit square.
604610
611+
Attributes:
612+
FIXED_SCHEMA: The fixed Arrow output schema this function always produces.
613+
605614
"""
606615

607616
class Meta:

vgi/_test_fixtures/table/late_materialization.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,9 @@ class LateMaterializationFunction(TableFunctionGenerator[LateMaterializationFunc
148148
-------
149149
SELECT row_id, payload FROM late_materialization(100000) ORDER BY ord LIMIT 10
150150
151+
Attributes:
152+
FunctionArguments: The argument dataclass type bound to this function.
153+
151154
"""
152155

153156
FunctionArguments = LateMaterializationFunctionArgs

vgi/_test_fixtures/table/make_series.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ class MakeSeriesCountFunction(TableFunctionGenerator[MakeSeriesCountArgs, MakeSe
7777
SELECT * FROM make_series(5)
7878
Returns: 0, 1, 2, 3, 4
7979
80+
Attributes:
81+
FIXED_SCHEMA: The fixed Arrow output schema this function always produces.
82+
8083
"""
8184

8285
FIXED_SCHEMA: ClassVar[pa.Schema] = MAKE_SERIES_SCHEMA
@@ -113,6 +116,9 @@ class MakeSeriesRangeFunction(TableFunctionGenerator[MakeSeriesRangeArgs, MakeSe
113116
SELECT * FROM make_series(3, 7)
114117
Returns: 3, 4, 5, 6
115118
119+
Attributes:
120+
FIXED_SCHEMA: The fixed Arrow output schema this function always produces.
121+
116122
"""
117123

118124
FIXED_SCHEMA: ClassVar[pa.Schema] = MAKE_SERIES_SCHEMA
@@ -149,6 +155,9 @@ class MakeSeriesStepFunction(TableFunctionGenerator[MakeSeriesStepArgs, MakeSeri
149155
SELECT * FROM make_series(0, 10, 3)
150156
Returns: 0, 3, 6, 9
151157
158+
Attributes:
159+
FIXED_SCHEMA: The fixed Arrow output schema this function always produces.
160+
152161
"""
153162

154163
FIXED_SCHEMA: ClassVar[pa.Schema] = MAKE_SERIES_SCHEMA
@@ -195,6 +204,9 @@ class MakeSeriesCsvFunction(TableFunctionGenerator[MakeSeriesCsvArgs, MakeSeries
195204
SELECT * FROM make_series('10,20,30')
196205
Returns: 10, 20, 30
197206
207+
Attributes:
208+
FIXED_SCHEMA: The fixed Arrow output schema this function always produces.
209+
198210
"""
199211

200212
FIXED_SCHEMA: ClassVar[pa.Schema] = MAKE_SERIES_SCHEMA
@@ -243,6 +255,9 @@ class MakeSeriesFloatFunction(TableFunctionGenerator[MakeSeriesFloatArgs, MakeSe
243255
SELECT * FROM make_series(0.5)
244256
Returns: 0.0, 0.5, 1.0, ..., 4.5
245257
258+
Attributes:
259+
FIXED_SCHEMA: The fixed Arrow output schema this function always produces.
260+
246261
"""
247262

248263
FIXED_SCHEMA: ClassVar[pa.Schema] = MAKE_SERIES_FLOAT_SCHEMA

vgi/_test_fixtures/table/misc.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,10 @@ class ProjectedDataFunction(TableFunctionGenerator[ProjectedDataFunctionArgument
178178
SELECT id, value FROM projected_data(10) -- Only computes id and value
179179
Returns: 10 rows with id and value columns only
180180
181+
Attributes:
182+
FIXED_SCHEMA: The fixed Arrow output schema this function always produces.
183+
BATCH_SIZE: Number of rows emitted per output batch.
184+
181185
"""
182186

183187
class Meta:

0 commit comments

Comments
 (0)