Skip to content

Commit 21fd61a

Browse files
igerberclaude
andcommitted
Narrow ImportError suppression, fix Bacon docs, and fix plot return types
Restrict test harness ImportError handling to known third-party modules (pyfixest, linearmodels, differences) so broken diff_diff imports fail CI. Fix REGISTRY.md Bacon never-treated assumption to match implementation. Fix fig= to ax= across visualization docs (plot_* returns Axes, not Figure). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 70e09bd commit 21fd61a

5 files changed

Lines changed: 27 additions & 14 deletions

File tree

docs/api/power.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,8 @@ Complete Example
157157
158158
# Power curve
159159
pa = PowerAnalysis(n_treated=100, n_control=100, n_pre=4, n_post=4, sigma=1.0)
160-
fig = plot_power_curve(pa, effect_range=(0, 1), n_points=50)
161-
fig.savefig('power_curve.png')
160+
ax = plot_power_curve(pa, effect_range=(0, 1), n_points=50)
161+
ax.figure.savefig('power_curve.png')
162162
163163
See Also
164164
--------

docs/api/visualization.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ Example
4747
time='period', first_treat='first_treat')
4848
4949
# Plot effects by treatment cohort
50-
fig = plot_group_effects(results)
50+
ax = plot_group_effects(results)
5151
5252
plot_sensitivity
5353
----------------
@@ -69,7 +69,7 @@ Example
6969
M_grid=[0, 0.5, 1.0, 1.5, 2.0]
7070
)
7171
72-
fig = plot_sensitivity(sensitivity)
72+
ax = plot_sensitivity(sensitivity)
7373
7474
plot_honest_event_study
7575
-----------------------

docs/methodology/REGISTRY.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1500,7 +1500,7 @@ For global method, LOOCV works as follows:
15001500
*Assumption checks / warnings:*
15011501
- Requires variation in treatment timing (staggered adoption)
15021502
- Warns if only one treatment cohort (decomposition not meaningful)
1503-
- Assumes no never-treated: uses not-yet-treated as controls
1503+
- Uses never-treated units as controls when present; falls back to timing-only comparisons otherwise
15041504

15051505
*Estimator equation (as implemented):*
15061506

docs/troubleshooting.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -252,15 +252,15 @@ Visualization Issues
252252
import matplotlib.pyplot as plt
253253
254254
# Option 1: Use plt.show()
255-
fig = plot_event_study(results)
255+
ax = plot_event_study(results)
256256
plt.show()
257257
258258
# Option 2: Use inline magic (Jupyter)
259259
%matplotlib inline
260260
261261
# Option 3: Return and display figure
262-
fig = plot_event_study(results)
263-
fig # Display in Jupyter
262+
ax = plot_event_study(results)
263+
ax # Display in Jupyter
264264
265265
Performance Issues
266266
------------------

tests/test_doc_snippets.py

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
44
Extracts ``.. code-block:: python`` snippets from RST files and executes them
55
in isolated namespaces with synthetic data and mock dataset loaders. Fails on
6-
all exceptions except NameError (context-dependent snippets).
6+
all exceptions except NameError (context-dependent snippets) and
7+
ImportError for known third-party packages (comparison-page snippets).
78
"""
89

910
import re
@@ -100,6 +101,11 @@ def _extract_snippets(rst_path: Path) -> List[Tuple[int, str]]:
100101
r"wide_to_long\(", # references undefined wide_data variable
101102
]
102103

104+
# Third-party packages imported by comparison-page snippets that may not
105+
# be installed in the test environment. Only these are exempt from
106+
# ImportError failures — diff_diff and stdlib imports must succeed.
107+
_THIRD_PARTY_MODULES = {"pyfixest", "linearmodels", "differences"}
108+
103109

104110
def _should_skip(code: str) -> Optional[str]:
105111
"""Return a reason string if the snippet should be skipped, else None."""
@@ -354,11 +360,18 @@ def test_doc_snippet(test_id: str, code: str, skip_reason: Optional[str]):
354360
# context block (e.g. ``results`` from an earlier fit). This is
355361
# expected for isolated execution — not an API mismatch.
356362
pass
357-
except ImportError:
358-
# ImportError covers both ModuleNotFoundError (comparison pages
359-
# importing pyfixest, linearmodels, etc.) and optional-dependency
360-
# guards (e.g. matplotlib required for plotting functions).
361-
pass
363+
except ImportError as exc:
364+
# Only suppress ImportError for known third-party packages that
365+
# comparison-page snippets import. In-package (diff_diff.*) and
366+
# stdlib import failures should still fail the test.
367+
mod_name = getattr(exc, "name", "") or ""
368+
top_level = mod_name.split(".")[0]
369+
if top_level not in _THIRD_PARTY_MODULES:
370+
pytest.fail(
371+
f"Snippet {test_id} raised ImportError for "
372+
f"'{mod_name}': {exc}\n\n"
373+
f"Code:\n{textwrap.indent(code, ' ')}"
374+
)
362375
except Exception as exc:
363376
pytest.fail(
364377
f"Snippet {test_id} raised {type(exc).__name__}: {exc}\n\n"

0 commit comments

Comments
 (0)