|
| 1 | +# AGENTS |
| 2 | + |
| 3 | +## Testing preferences |
| 4 | + |
| 5 | +- Write all Python tests as `pytest` style functions, not unittest classes |
| 6 | +- Use descriptive function names starting with `test_` |
| 7 | +- Prefer fixtures over setup/teardown methods |
| 8 | +- Use assert statements directly, not self.assertEqual |
| 9 | + |
| 10 | +## Testing approach |
| 11 | + |
| 12 | +- Never create throwaway test scripts or ad hoc verification files |
| 13 | +- If you need to test functionality, write a proper test in the test suite |
| 14 | +- All tests go in the `causalpy/tests/` directory following the project structure |
| 15 | +- Tests should be runnable with the rest of the suite (`python -m pytest`) |
| 16 | +- Even for quick verification, write it as a real test that provides ongoing value |
| 17 | +- Preference should be given to integration tests, but unit tests are acceptable for core functionality to maintain high code coverage. |
| 18 | +- Tests should remain quick to run. Tests involving MCMC sampling with PyMC should use custom `sample_kwargs` to minimize the computational load. |
| 19 | + |
| 20 | +## Documentation |
| 21 | + |
| 22 | +- **Structure**: Notebooks (how-to examples) go in `docs/source/notebooks/`, knowledgebase (educational content) goes in `docs/source/knowledgebase/` |
| 23 | +- **Notebook naming**: Use pattern `{method}_{model}.ipynb` (e.g., `did_pymc.ipynb`, `rd_skl.ipynb`), organized by causal method |
| 24 | +- **MyST directives**: Use `:::{note}` and other MyST features for callouts and formatting |
| 25 | +- **Glossary linking**: Use Sphinx `:term:` directives to link to glossary terms (defined in `glossary.rst`), typically on first mention in a file |
| 26 | +- **Citations**: Use `references.bib` for citations, cite sources in example notebooks where possible. Include reference section at bottom of notebooks using `:::{bibliography}` directive with `:filter: docname in docnames` |
| 27 | +- **API documentation**: Auto-generated from docstrings via Sphinx autodoc, no manual API docs needed |
| 28 | +- **Build**: Use `make html` to build documentation |
| 29 | +- **Doctest**: Use `make doctest` to test that Python examples in doctests work |
| 30 | + |
| 31 | +## Code structure and style |
| 32 | + |
| 33 | +- **Experiment classes**: All experiment classes inherit from `BaseExperiment` in `causalpy/experiments/`. Must declare `supports_ols` and `supports_bayes` class attributes. Only implement abstract methods for supported model types (e.g., if only Bayesian is supported, implement `_bayesian_plot()` and `get_plot_data_bayesian()`; if only OLS is supported, implement `_ols_plot()` and `get_plot_data_ols()`) |
| 34 | +- **Model-agnostic design**: Experiment classes should work with both PyMC and scikit-learn models. Use `isinstance(self.model, PyMCModel)` vs `isinstance(self.model, RegressorMixin)` to dispatch to appropriate implementations |
| 35 | +- **Model classes**: PyMC models inherit from `PyMCModel` (extends `pm.Model`). Scikit-learn models use `RegressorMixin` and are made compatible via `create_causalpy_compatible_class()`. Common interface: `fit()`, `predict()`, `score()`, `calculate_impact()`, `print_coefficients()` |
| 36 | +- **Data handling**: PyMC models use `xarray.DataArray` with coords (keys like "coeffs", "obs_ind", "treated_units"). Scikit-learn models use numpy arrays. Data index should be named "obs_ind" |
| 37 | +- **Formulas**: Use patsy for formula parsing (via `dmatrices()`) |
| 38 | +- **Custom exceptions**: Use project-specific exceptions from `causalpy.custom_exceptions`: `FormulaException`, `DataException`, `BadIndexException` |
| 39 | +- **File organization**: Experiments in `causalpy/experiments/`, PyMC models in `causalpy/pymc_models.py`, scikit-learn models in `causalpy/skl_models.py` |
0 commit comments