Skip to content

Commit 9dcf09f

Browse files
authored
Merge pull request #399 from igerber/feature/docs-tests-workflow
Add docs-tests.yml; remove test_doc_snippets.py from rust-test.yml
2 parents f1d7674 + d9ad1e0 commit 9dcf09f

3 files changed

Lines changed: 78 additions & 4 deletions

File tree

.github/workflows/docs-tests.yml

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
name: Documentation Tests
2+
3+
on:
4+
push:
5+
branches: [main]
6+
paths:
7+
- 'docs/**'
8+
- 'diff_diff/**'
9+
- 'tests/test_doc_snippets.py'
10+
# tests/conftest.py is auto-loaded by pytest for the snippet
11+
# test run and mutates sys.path + MPLBACKEND (conftest.py:14, 18);
12+
# changes there can break snippet exec without touching the test
13+
# file itself.
14+
- 'tests/conftest.py'
15+
- 'pyproject.toml'
16+
- '.github/workflows/docs-tests.yml'
17+
pull_request:
18+
branches: [main]
19+
types: [opened, synchronize, reopened, labeled, unlabeled]
20+
paths:
21+
- 'docs/**'
22+
- 'diff_diff/**'
23+
- 'tests/test_doc_snippets.py'
24+
- 'tests/conftest.py'
25+
- 'pyproject.toml'
26+
- '.github/workflows/docs-tests.yml'
27+
schedule:
28+
# Weekly Sunday 6am UTC - smoke test that snippets still execute
29+
# against current upstream deps (mirrors notebooks.yml schedule).
30+
- cron: '0 6 * * 0'
31+
32+
permissions:
33+
contents: read
34+
35+
jobs:
36+
doc-snippets:
37+
name: Validate RST code snippets
38+
if: >-
39+
github.event_name != 'pull_request'
40+
|| contains(github.event.pull_request.labels.*.name, 'ready-for-ci')
41+
runs-on: ubuntu-latest
42+
43+
steps:
44+
- uses: actions/checkout@v6
45+
46+
- name: Set up Python
47+
uses: actions/setup-python@v6
48+
with:
49+
# 3.14 to mirror Pure Python Fallback (the only existing job
50+
# that actually ran these tests). notebooks.yml uses 3.11 for
51+
# nbmake compat, not relevant here.
52+
python-version: '3.14'
53+
54+
- name: Install dependencies
55+
# Keep in sync with pyproject.toml [project.dependencies] and [project.optional-dependencies.dev]
56+
run: pip install numpy pandas scipy pytest
57+
58+
- name: Run doc snippet tests in pure Python mode
59+
# PYTHONPATH=. lets the test import diff_diff directly from
60+
# source without invoking the maturin/Rust build (mirrors Pure
61+
# Python Fallback at rust-test.yml:189-193).
62+
run: PYTHONPATH=. DIFF_DIFF_BACKEND=python pytest tests/test_doc_snippets.py -v

.github/workflows/rust-test.yml

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ on:
77
- 'rust/**'
88
- 'diff_diff/**'
99
- 'tests/**'
10+
# tests/test_doc_snippets.py is owned by docs-tests.yml; exclude it
11+
# so a harness-only edit does not fan out into the Rust matrix.
12+
- '!tests/test_doc_snippets.py'
1013
- 'pyproject.toml'
1114
- '.github/workflows/rust-test.yml'
1215
pull_request:
@@ -16,6 +19,7 @@ on:
1619
- 'rust/**'
1720
- 'diff_diff/**'
1821
- 'tests/**'
22+
- '!tests/test_doc_snippets.py'
1923
- 'pyproject.toml'
2024
- '.github/workflows/rust-test.yml'
2125

@@ -155,14 +159,18 @@ jobs:
155159
- name: Run tests with Rust backend (Unix)
156160
if: runner.os != 'Windows'
157161
working-directory: /tmp
158-
run: DIFF_DIFF_BACKEND=rust pytest tests/ -q -n auto --dist worksteal -m ''
162+
# Doc snippet tests own .github/workflows/docs-tests.yml; ignore
163+
# them here to keep one workflow per surface and avoid double
164+
# execution (the matrix copies tests/ to /tmp/tests without
165+
# docs/, so this ignore is defensive on the Rust path).
166+
run: DIFF_DIFF_BACKEND=rust pytest tests/ --ignore=tests/test_doc_snippets.py -q -n auto --dist worksteal -m ''
159167

160168
- name: Run tests with Rust backend (Windows)
161169
if: runner.os == 'Windows'
162170
working-directory: ${{ runner.temp }}
163171
run: |
164172
$env:DIFF_DIFF_BACKEND="rust"
165-
pytest tests/ -q -n auto --dist worksteal -m ''
173+
pytest tests/ --ignore=tests/test_doc_snippets.py -q -n auto --dist worksteal -m ''
166174
shell: pwsh
167175

168176
# Test pure Python fallback (without Rust extension)
@@ -190,4 +198,8 @@ jobs:
190198
PYTHONPATH=. python -c "from diff_diff import HAS_RUST_BACKEND; print(f'HAS_RUST_BACKEND: {HAS_RUST_BACKEND}'); assert not HAS_RUST_BACKEND"
191199
192200
- name: Run tests in pure Python mode
193-
run: PYTHONPATH=. DIFF_DIFF_BACKEND=python pytest tests/ -q --ignore=tests/test_rust_backend.py -n auto --dist worksteal
201+
# Doc snippet tests own .github/workflows/docs-tests.yml; ignore
202+
# them here to keep one workflow per surface (this is the only
203+
# invocation that actually executes test_doc_snippets.py since
204+
# this job runs from the repo root, not /tmp/tests).
205+
run: PYTHONPATH=. DIFF_DIFF_BACKEND=python pytest tests/ -q --ignore=tests/test_rust_backend.py --ignore=tests/test_doc_snippets.py -n auto --dist worksteal

TODO.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ Deferred items from PR reviews that were not addressed before merge.
120120
|-------|----------|----|----------|
121121
| ImputationDiD event-study SEs recompute full conservative variance per horizon (should cache A0/A1 factorization) | `imputation.py` | #141 | Low |
122122
| Rust faer SVD ndarray-to-faer conversion overhead (minimal vs SVD cost) | `rust/src/linalg.rs:67` | #115 | Low |
123-
| Unrelated label events (e.g., adding `bug` label) re-trigger CI workflows when `ready-for-ci` is already present; filter `labeled`/`unlabeled` events to only `ready-for-ci` transitions | `.github/workflows/rust-test.yml`, `notebooks.yml` | #269 | Low |
123+
| Unrelated label events (e.g., adding `bug` label) re-trigger CI workflows when `ready-for-ci` is already present; filter `labeled`/`unlabeled` events to only `ready-for-ci` transitions | `.github/workflows/rust-test.yml`, `notebooks.yml`, `docs-tests.yml` | #269 | Low |
124124
| `bread_inv` as a performance kwarg on `compute_robust_vcov` to avoid re-inverting `(X'WX)` when the caller already has it. Deferred from Phase 1a for scope. HC2 and HC2+BM both need the bread inverse, so a shared hint would save one `np.linalg.solve` per sandwich. | `linalg.py::compute_robust_vcov` | Phase 1a | Low |
125125
| Rust-backend HC2 implementation. Current Rust path only supports HC1; HC2 and CR2 Bell-McCaffrey fall through to the NumPy backend. For large-n fits this is noticeable. | `rust/src/linalg.rs` | Phase 1a | Low |
126126
| CR2 Bell-McCaffrey DOF uses a naive `O(n² k)` per-coefficient loop over cluster pairs. Pustejovsky-Tipton (2018) Appendix B has a scores-based formulation that avoids the full `n × n` `M` matrix. Switch when a user hits a large-`n` cluster-robust design. | `linalg.py::_compute_cr2_bm` | Phase 1a | Low |

0 commit comments

Comments
 (0)