pytest-notebook-policy 0.8.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pytest_notebook_policy-0.8.0/LICENSE +21 -0
- pytest_notebook_policy-0.8.0/PKG-INFO +279 -0
- pytest_notebook_policy-0.8.0/README.md +251 -0
- pytest_notebook_policy-0.8.0/pyproject.toml +76 -0
- pytest_notebook_policy-0.8.0/src/pytest_notebook_policy/__init__.py +5 -0
- pytest_notebook_policy-0.8.0/src/pytest_notebook_policy/plugin.py +1100 -0
- pytest_notebook_policy-0.8.0/src/pytest_notebook_policy/quality.py +903 -0
- pytest_notebook_policy-0.8.0/src/pytest_notebook_policy/report_templates.py +121 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Michael Booth
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pytest-notebook-policy
|
|
3
|
+
Version: 0.8.0
|
|
4
|
+
Summary: Pytest plugin for notebook policy and quality checks
|
|
5
|
+
Keywords: pytest,marimo,jupyter,notebooks,lint,quality,policy
|
|
6
|
+
Author: Michael Booth
|
|
7
|
+
Author-email: Michael Booth <michael@databooth.com.au>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Framework :: Pytest
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
19
|
+
Requires-Dist: jinja2
|
|
20
|
+
Requires-Dist: pytest>=8.0
|
|
21
|
+
Requires-Dist: jupytext>=1.16 ; extra == 'sync'
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Project-URL: Homepage, https://github.com/DataBooth/pytest-notebook-policy
|
|
24
|
+
Project-URL: Repository, https://github.com/DataBooth/pytest-notebook-policy
|
|
25
|
+
Project-URL: Issues, https://github.com/DataBooth/pytest-notebook-policy/issues
|
|
26
|
+
Provides-Extra: sync
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# pytest-notebook-policy
|
|
30
|
+
[](https://github.com/DataBooth/pytest-notebook-policy/actions/workflows/ci.yml)
|
|
31
|
+
`pytest-notebook-policy` is a `pytest` plugin that enforces notebook policy and quality rules.
|
|
32
|
+
|
|
33
|
+
It focuses on notebook-specific checks for marimo and Jupyter workflows, not generic Python linting.
|
|
34
|
+
|
|
35
|
+
## Terminology and discoverability
|
|
36
|
+
If you are looking for notebook **best practices**, **assurance**, **validation**, or **testing** tooling, this project is intended to cover those needs through enforceable policy checks and automated quality gates.
|
|
37
|
+
|
|
38
|
+
## What this package is
|
|
39
|
+
`pytest-notebook-policy` is a lightweight semantic checker for notebook workflows.
|
|
40
|
+
|
|
41
|
+
It focuses on enforcing notebook patterns that are easy to miss in review, such as:
|
|
42
|
+
- `on_change` callback usage where reactivity is clearer
|
|
43
|
+
- cross-cell mutation of shared objects
|
|
44
|
+
- non-idempotent cell behaviour
|
|
45
|
+
- mixed test/helper cells and fixture placement conventions
|
|
46
|
+
|
|
47
|
+
## Why this package exists
|
|
48
|
+
marimo already gives you:
|
|
49
|
+
- native notebook testing with `pytest`
|
|
50
|
+
- built-in notebook linting via `marimo check` ([announcement](https://marimo.io/blog/marimo-check))
|
|
51
|
+
|
|
52
|
+
`pytest-notebook-policy` is designed to complement those tools with opinionated, team-level checks tailored to a stricter “production notebook” style.
|
|
53
|
+
|
|
54
|
+
In practice:
|
|
55
|
+
- use **Ruff** for general Python quality/security
|
|
56
|
+
- use **marimo check** for core notebook validity and formatting rules
|
|
57
|
+
- use **pytest-notebook-policy** for extra policy checks around reactive design and notebook maintainability
|
|
58
|
+
|
|
59
|
+
## Machine-assisted coding guardrails
|
|
60
|
+
`pytest-notebook-policy` is especially useful as an automated quality gate when notebooks are generated or edited by coding agents (for example Claude, Warp, Codex, or similar tools).
|
|
61
|
+
|
|
62
|
+
Adding it to pre-commit and CI helps catch marimo-specific issues immediately, so agents can self-correct before code reaches review.
|
|
63
|
+
|
|
64
|
+
Example pre-commit hook:
|
|
65
|
+
|
|
66
|
+
```yaml
|
|
67
|
+
repos:
|
|
68
|
+
- repo: local
|
|
69
|
+
hooks:
|
|
70
|
+
- id: pytest-notebook-quality
|
|
71
|
+
name: pytest-notebook-quality
|
|
72
|
+
entry: uv run pytest-notebook-quality experiments notebooks
|
|
73
|
+
language: system
|
|
74
|
+
pass_filenames: false
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
This keeps the feedback loop short:
|
|
78
|
+
- agent proposes notebook edits
|
|
79
|
+
- pre-commit/CI runs Ruff + `pytest-notebook-policy` checks
|
|
80
|
+
- agent fixes violations and retries
|
|
81
|
+
|
|
82
|
+
## Current rules
|
|
83
|
+
- `M001`: prefer reactive dependencies over `on_change` handlers.
|
|
84
|
+
- `M002`: keep test cells focused; avoid mixing tests with helper/setup code in the same cell.
|
|
85
|
+
- `M003`: avoid mutable module-level state in notebook files.
|
|
86
|
+
- `M004`: prefer fixtures in `conftest.py` or helper modules rather than notebook modules.
|
|
87
|
+
- `M005`: avoid cross-cell mutation of shared objects (including notebook inputs and module-level mutable state).
|
|
88
|
+
- `M006`: avoid non-idempotent calls in cells (for example `random.*`, `np.random.*`, `time.time`, `uuid.uuid4`).
|
|
89
|
+
- `J001`: avoid notebook magics and shell escapes in policy-checked notebooks.
|
|
90
|
+
- `J002`: avoid non-idempotent calls in Jupyter notebook code cells.
|
|
91
|
+
- `J010`: (opt-in) check that paired `.ipynb` and `.py` files stay in sync.
|
|
92
|
+
- `J011`: require a top-of-notebook parameter/configuration cell in the first few code cells.
|
|
93
|
+
- `J012`: keep notebooks and cells small enough to stay reviewable and maintainable.
|
|
94
|
+
- `J013`: avoid excessive inline function/class definitions in notebooks; extract reusable logic into modules.
|
|
95
|
+
Detailed rationale and remediation guidance: [`docs/RULES.md`](docs/RULES.md).
|
|
96
|
+
|
|
97
|
+
## Usage
|
|
98
|
+
Install in a project:
|
|
99
|
+
|
|
100
|
+
```shell
|
|
101
|
+
uv add --dev pytest-notebook-policy
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Install pre-commit hooks:
|
|
105
|
+
|
|
106
|
+
```shell
|
|
107
|
+
uv run --with pre-commit pre-commit install
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Run hooks across all files:
|
|
111
|
+
|
|
112
|
+
```shell
|
|
113
|
+
uv run --with pre-commit pre-commit run --all-files
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
CI runs on push/PR using `.github/workflows/ci.yml` and executes Ruff plus the test suite.
|
|
117
|
+
|
|
118
|
+
Run checks explicitly:
|
|
119
|
+
|
|
120
|
+
```shell
|
|
121
|
+
uv run pytest --notebook-check
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Enable by default in `pyproject.toml`:
|
|
125
|
+
|
|
126
|
+
```toml
|
|
127
|
+
[tool.pytest.ini_options]
|
|
128
|
+
notebook_check = true
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Filter rules:
|
|
132
|
+
|
|
133
|
+
```shell
|
|
134
|
+
uv run pytest --notebook-check --notebook-check-select M001 --notebook-check-ignore M004
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Choose Jupyter rule source:
|
|
138
|
+
|
|
139
|
+
```shell
|
|
140
|
+
uv run pytest --notebook-check --notebook-check-jupyter-source paired-py
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Set default Jupyter rule source in `pyproject.toml`:
|
|
144
|
+
|
|
145
|
+
```toml
|
|
146
|
+
[tool.pytest.ini_options]
|
|
147
|
+
notebook_check = true
|
|
148
|
+
notebook_check_jupyter_source = "paired-py"
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
`paired-py` prefers the paired `.py` notebook (when available and readable) for J-rules and falls back to `.ipynb`.
|
|
152
|
+
|
|
153
|
+
Tune Jupyter size/complexity thresholds:
|
|
154
|
+
|
|
155
|
+
```shell
|
|
156
|
+
uv run pytest --notebook-check \
|
|
157
|
+
--notebook-check-jupyter-max-code-cells 30 \
|
|
158
|
+
--notebook-check-jupyter-max-cell-lines 120 \
|
|
159
|
+
--notebook-check-jupyter-max-inline-definitions 5
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Set defaults in `pyproject.toml`:
|
|
163
|
+
|
|
164
|
+
```toml
|
|
165
|
+
[tool.pytest.ini_options]
|
|
166
|
+
notebook_check_jupyter_max_code_cells = "30"
|
|
167
|
+
notebook_check_jupyter_max_cell_lines = "120"
|
|
168
|
+
notebook_check_jupyter_max_inline_definitions = "5"
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Run combined Ruff + notebook policy checks:
|
|
172
|
+
|
|
173
|
+
```shell
|
|
174
|
+
uv run pytest-notebook-quality path/to/notebooks
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Skip Ruff and run only notebook policy checks:
|
|
178
|
+
|
|
179
|
+
```shell
|
|
180
|
+
uv run pytest-notebook-quality --skip-ruff path/to/notebooks
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Customise deterministic rule toggles and thresholds on the quality command:
|
|
184
|
+
|
|
185
|
+
```shell
|
|
186
|
+
uv run pytest-notebook-quality --skip-ruff \
|
|
187
|
+
--notebook-check-select M \
|
|
188
|
+
--notebook-check-select J \
|
|
189
|
+
--notebook-check-ignore J010 \
|
|
190
|
+
--notebook-check-jupyter-source paired-py \
|
|
191
|
+
--notebook-check-jupyter-max-code-cells 30 \
|
|
192
|
+
--notebook-check-jupyter-max-cell-lines 120 \
|
|
193
|
+
--notebook-check-jupyter-max-inline-definitions 5 \
|
|
194
|
+
path/to/notebooks
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Write an NBOM-style JSON manifest (notebook surface + dependency correlation):
|
|
198
|
+
|
|
199
|
+
```shell
|
|
200
|
+
uv run pytest-notebook-quality --skip-ruff \
|
|
201
|
+
--report-nbom-json reports/notebook-policy-nbom.json \
|
|
202
|
+
--report-dependency-enrichment \
|
|
203
|
+
path/to/notebooks
|
|
204
|
+
```
|
|
205
|
+
Include optional vulnerability IDs (queried from OSV) in dependency enrichment output:
|
|
206
|
+
|
|
207
|
+
```shell
|
|
208
|
+
uv run pytest-notebook-quality --skip-ruff \
|
|
209
|
+
--report-nbom-json reports/notebook-policy-nbom.json \
|
|
210
|
+
--report-dependency-vulns \
|
|
211
|
+
path/to/notebooks
|
|
212
|
+
```
|
|
213
|
+
`--report-dependency-vulns` implicitly enables dependency enrichment.
|
|
214
|
+
|
|
215
|
+
Generate a markdown report with findings-first layout, touchpoint summary, and appendices:
|
|
216
|
+
|
|
217
|
+
```shell
|
|
218
|
+
uv run pytest-notebook-quality --skip-ruff --report-md reports/notebook-policy-report.md path/to/notebooks
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Enable optional dependency enrichment in the report:
|
|
222
|
+
|
|
223
|
+
```shell
|
|
224
|
+
uv run pytest-notebook-quality --skip-ruff \
|
|
225
|
+
--report-md reports/notebook-policy-report.md \
|
|
226
|
+
--report-dependency-enrichment \
|
|
227
|
+
path/to/notebooks
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Project-specific quality defaults can be set in `pyproject.toml`:
|
|
231
|
+
|
|
232
|
+
```toml
|
|
233
|
+
[tool.pytest_notebook_policy.quality]
|
|
234
|
+
select = ["M", "J"]
|
|
235
|
+
ignore = ["J010"]
|
|
236
|
+
jupyter_source = "paired-py"
|
|
237
|
+
jupyter_max_code_cells = 30
|
|
238
|
+
jupyter_max_cell_lines = 120
|
|
239
|
+
jupyter_max_inline_definitions = 5
|
|
240
|
+
report_md = "reports/notebook-policy-report.md"
|
|
241
|
+
report_dependency_enrichment = true
|
|
242
|
+
report_dependency_vulns = false
|
|
243
|
+
report_nbom_json = "reports/notebook-policy-nbom.json"
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Enable optional sync tooling:
|
|
247
|
+
|
|
248
|
+
```shell
|
|
249
|
+
uv add --dev 'pytest-notebook-policy[sync]'
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## Versioning and release workflow
|
|
253
|
+
- Versioning follows Semantic Versioning (`MAJOR.MINOR.PATCH`).
|
|
254
|
+
- Release history lives in `RELEASE_NOTES.md`.
|
|
255
|
+
|
|
256
|
+
Typical release flow:
|
|
257
|
+
|
|
258
|
+
```shell
|
|
259
|
+
uv version --bump patch
|
|
260
|
+
uv build
|
|
261
|
+
uv run --with twine twine check dist/*
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
When ready to release (not run yet here), upload with Twine:
|
|
265
|
+
|
|
266
|
+
```shell
|
|
267
|
+
uv run --with twine twine upload dist/*
|
|
268
|
+
```
|
|
269
|
+
## Notebook fixtures for testing
|
|
270
|
+
The repository includes notebook fixtures in `tests/fixtures`:
|
|
271
|
+
|
|
272
|
+
- `tests/fixtures/synthetic`: synthetic notebooks for targeted pass/fail checks.
|
|
273
|
+
- `tests/fixtures/real`: real-world notebooks sourced from public repositories (with provenance in `tests/fixtures/real/SOURCES.txt`).
|
|
274
|
+
|
|
275
|
+
Refresh pinned real fixtures and print their observed rule-code sets:
|
|
276
|
+
|
|
277
|
+
```shell
|
|
278
|
+
uv run python scripts/refresh_real_fixtures.py
|
|
279
|
+
```
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
# pytest-notebook-policy
|
|
2
|
+
[](https://github.com/DataBooth/pytest-notebook-policy/actions/workflows/ci.yml)
|
|
3
|
+
`pytest-notebook-policy` is a `pytest` plugin that enforces notebook policy and quality rules.
|
|
4
|
+
|
|
5
|
+
It focuses on notebook-specific checks for marimo and Jupyter workflows, not generic Python linting.
|
|
6
|
+
|
|
7
|
+
## Terminology and discoverability
|
|
8
|
+
If you are looking for notebook **best practices**, **assurance**, **validation**, or **testing** tooling, this project is intended to cover those needs through enforceable policy checks and automated quality gates.
|
|
9
|
+
|
|
10
|
+
## What this package is
|
|
11
|
+
`pytest-notebook-policy` is a lightweight semantic checker for notebook workflows.
|
|
12
|
+
|
|
13
|
+
It focuses on enforcing notebook patterns that are easy to miss in review, such as:
|
|
14
|
+
- `on_change` callback usage where reactivity is clearer
|
|
15
|
+
- cross-cell mutation of shared objects
|
|
16
|
+
- non-idempotent cell behaviour
|
|
17
|
+
- mixed test/helper cells and fixture placement conventions
|
|
18
|
+
|
|
19
|
+
## Why this package exists
|
|
20
|
+
marimo already gives you:
|
|
21
|
+
- native notebook testing with `pytest`
|
|
22
|
+
- built-in notebook linting via `marimo check` ([announcement](https://marimo.io/blog/marimo-check))
|
|
23
|
+
|
|
24
|
+
`pytest-notebook-policy` is designed to complement those tools with opinionated, team-level checks tailored to a stricter “production notebook” style.
|
|
25
|
+
|
|
26
|
+
In practice:
|
|
27
|
+
- use **Ruff** for general Python quality/security
|
|
28
|
+
- use **marimo check** for core notebook validity and formatting rules
|
|
29
|
+
- use **pytest-notebook-policy** for extra policy checks around reactive design and notebook maintainability
|
|
30
|
+
|
|
31
|
+
## Machine-assisted coding guardrails
|
|
32
|
+
`pytest-notebook-policy` is especially useful as an automated quality gate when notebooks are generated or edited by coding agents (for example Claude, Warp, Codex, or similar tools).
|
|
33
|
+
|
|
34
|
+
Adding it to pre-commit and CI helps catch marimo-specific issues immediately, so agents can self-correct before code reaches review.
|
|
35
|
+
|
|
36
|
+
Example pre-commit hook:
|
|
37
|
+
|
|
38
|
+
```yaml
|
|
39
|
+
repos:
|
|
40
|
+
- repo: local
|
|
41
|
+
hooks:
|
|
42
|
+
- id: pytest-notebook-quality
|
|
43
|
+
name: pytest-notebook-quality
|
|
44
|
+
entry: uv run pytest-notebook-quality experiments notebooks
|
|
45
|
+
language: system
|
|
46
|
+
pass_filenames: false
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
This keeps the feedback loop short:
|
|
50
|
+
- agent proposes notebook edits
|
|
51
|
+
- pre-commit/CI runs Ruff + `pytest-notebook-policy` checks
|
|
52
|
+
- agent fixes violations and retries
|
|
53
|
+
|
|
54
|
+
## Current rules
|
|
55
|
+
- `M001`: prefer reactive dependencies over `on_change` handlers.
|
|
56
|
+
- `M002`: keep test cells focused; avoid mixing tests with helper/setup code in the same cell.
|
|
57
|
+
- `M003`: avoid mutable module-level state in notebook files.
|
|
58
|
+
- `M004`: prefer fixtures in `conftest.py` or helper modules rather than notebook modules.
|
|
59
|
+
- `M005`: avoid cross-cell mutation of shared objects (including notebook inputs and module-level mutable state).
|
|
60
|
+
- `M006`: avoid non-idempotent calls in cells (for example `random.*`, `np.random.*`, `time.time`, `uuid.uuid4`).
|
|
61
|
+
- `J001`: avoid notebook magics and shell escapes in policy-checked notebooks.
|
|
62
|
+
- `J002`: avoid non-idempotent calls in Jupyter notebook code cells.
|
|
63
|
+
- `J010`: (opt-in) check that paired `.ipynb` and `.py` files stay in sync.
|
|
64
|
+
- `J011`: require a top-of-notebook parameter/configuration cell in the first few code cells.
|
|
65
|
+
- `J012`: keep notebooks and cells small enough to stay reviewable and maintainable.
|
|
66
|
+
- `J013`: avoid excessive inline function/class definitions in notebooks; extract reusable logic into modules.
|
|
67
|
+
Detailed rationale and remediation guidance: [`docs/RULES.md`](docs/RULES.md).
|
|
68
|
+
|
|
69
|
+
## Usage
|
|
70
|
+
Install in a project:
|
|
71
|
+
|
|
72
|
+
```shell
|
|
73
|
+
uv add --dev pytest-notebook-policy
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Install pre-commit hooks:
|
|
77
|
+
|
|
78
|
+
```shell
|
|
79
|
+
uv run --with pre-commit pre-commit install
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Run hooks across all files:
|
|
83
|
+
|
|
84
|
+
```shell
|
|
85
|
+
uv run --with pre-commit pre-commit run --all-files
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
CI runs on push/PR using `.github/workflows/ci.yml` and executes Ruff plus the test suite.
|
|
89
|
+
|
|
90
|
+
Run checks explicitly:
|
|
91
|
+
|
|
92
|
+
```shell
|
|
93
|
+
uv run pytest --notebook-check
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Enable by default in `pyproject.toml`:
|
|
97
|
+
|
|
98
|
+
```toml
|
|
99
|
+
[tool.pytest.ini_options]
|
|
100
|
+
notebook_check = true
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Filter rules:
|
|
104
|
+
|
|
105
|
+
```shell
|
|
106
|
+
uv run pytest --notebook-check --notebook-check-select M001 --notebook-check-ignore M004
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Choose Jupyter rule source:
|
|
110
|
+
|
|
111
|
+
```shell
|
|
112
|
+
uv run pytest --notebook-check --notebook-check-jupyter-source paired-py
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Set default Jupyter rule source in `pyproject.toml`:
|
|
116
|
+
|
|
117
|
+
```toml
|
|
118
|
+
[tool.pytest.ini_options]
|
|
119
|
+
notebook_check = true
|
|
120
|
+
notebook_check_jupyter_source = "paired-py"
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
`paired-py` prefers the paired `.py` notebook (when available and readable) for J-rules and falls back to `.ipynb`.
|
|
124
|
+
|
|
125
|
+
Tune Jupyter size/complexity thresholds:
|
|
126
|
+
|
|
127
|
+
```shell
|
|
128
|
+
uv run pytest --notebook-check \
|
|
129
|
+
--notebook-check-jupyter-max-code-cells 30 \
|
|
130
|
+
--notebook-check-jupyter-max-cell-lines 120 \
|
|
131
|
+
--notebook-check-jupyter-max-inline-definitions 5
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Set defaults in `pyproject.toml`:
|
|
135
|
+
|
|
136
|
+
```toml
|
|
137
|
+
[tool.pytest.ini_options]
|
|
138
|
+
notebook_check_jupyter_max_code_cells = "30"
|
|
139
|
+
notebook_check_jupyter_max_cell_lines = "120"
|
|
140
|
+
notebook_check_jupyter_max_inline_definitions = "5"
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Run combined Ruff + notebook policy checks:
|
|
144
|
+
|
|
145
|
+
```shell
|
|
146
|
+
uv run pytest-notebook-quality path/to/notebooks
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Skip Ruff and run only notebook policy checks:
|
|
150
|
+
|
|
151
|
+
```shell
|
|
152
|
+
uv run pytest-notebook-quality --skip-ruff path/to/notebooks
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Customise deterministic rule toggles and thresholds on the quality command:
|
|
156
|
+
|
|
157
|
+
```shell
|
|
158
|
+
uv run pytest-notebook-quality --skip-ruff \
|
|
159
|
+
--notebook-check-select M \
|
|
160
|
+
--notebook-check-select J \
|
|
161
|
+
--notebook-check-ignore J010 \
|
|
162
|
+
--notebook-check-jupyter-source paired-py \
|
|
163
|
+
--notebook-check-jupyter-max-code-cells 30 \
|
|
164
|
+
--notebook-check-jupyter-max-cell-lines 120 \
|
|
165
|
+
--notebook-check-jupyter-max-inline-definitions 5 \
|
|
166
|
+
path/to/notebooks
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Write an NBOM-style JSON manifest (notebook surface + dependency correlation):
|
|
170
|
+
|
|
171
|
+
```shell
|
|
172
|
+
uv run pytest-notebook-quality --skip-ruff \
|
|
173
|
+
--report-nbom-json reports/notebook-policy-nbom.json \
|
|
174
|
+
--report-dependency-enrichment \
|
|
175
|
+
path/to/notebooks
|
|
176
|
+
```
|
|
177
|
+
Include optional vulnerability IDs (queried from OSV) in dependency enrichment output:
|
|
178
|
+
|
|
179
|
+
```shell
|
|
180
|
+
uv run pytest-notebook-quality --skip-ruff \
|
|
181
|
+
--report-nbom-json reports/notebook-policy-nbom.json \
|
|
182
|
+
--report-dependency-vulns \
|
|
183
|
+
path/to/notebooks
|
|
184
|
+
```
|
|
185
|
+
`--report-dependency-vulns` implicitly enables dependency enrichment.
|
|
186
|
+
|
|
187
|
+
Generate a markdown report with findings-first layout, touchpoint summary, and appendices:
|
|
188
|
+
|
|
189
|
+
```shell
|
|
190
|
+
uv run pytest-notebook-quality --skip-ruff --report-md reports/notebook-policy-report.md path/to/notebooks
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Enable optional dependency enrichment in the report:
|
|
194
|
+
|
|
195
|
+
```shell
|
|
196
|
+
uv run pytest-notebook-quality --skip-ruff \
|
|
197
|
+
--report-md reports/notebook-policy-report.md \
|
|
198
|
+
--report-dependency-enrichment \
|
|
199
|
+
path/to/notebooks
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Project-specific quality defaults can be set in `pyproject.toml`:
|
|
203
|
+
|
|
204
|
+
```toml
|
|
205
|
+
[tool.pytest_notebook_policy.quality]
|
|
206
|
+
select = ["M", "J"]
|
|
207
|
+
ignore = ["J010"]
|
|
208
|
+
jupyter_source = "paired-py"
|
|
209
|
+
jupyter_max_code_cells = 30
|
|
210
|
+
jupyter_max_cell_lines = 120
|
|
211
|
+
jupyter_max_inline_definitions = 5
|
|
212
|
+
report_md = "reports/notebook-policy-report.md"
|
|
213
|
+
report_dependency_enrichment = true
|
|
214
|
+
report_dependency_vulns = false
|
|
215
|
+
report_nbom_json = "reports/notebook-policy-nbom.json"
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Enable optional sync tooling:
|
|
219
|
+
|
|
220
|
+
```shell
|
|
221
|
+
uv add --dev 'pytest-notebook-policy[sync]'
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Versioning and release workflow
|
|
225
|
+
- Versioning follows Semantic Versioning (`MAJOR.MINOR.PATCH`).
|
|
226
|
+
- Release history lives in `RELEASE_NOTES.md`.
|
|
227
|
+
|
|
228
|
+
Typical release flow:
|
|
229
|
+
|
|
230
|
+
```shell
|
|
231
|
+
uv version --bump patch
|
|
232
|
+
uv build
|
|
233
|
+
uv run --with twine twine check dist/*
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
When ready to release (not run yet here), upload with Twine:
|
|
237
|
+
|
|
238
|
+
```shell
|
|
239
|
+
uv run --with twine twine upload dist/*
|
|
240
|
+
```
|
|
241
|
+
## Notebook fixtures for testing
|
|
242
|
+
The repository includes notebook fixtures in `tests/fixtures`:
|
|
243
|
+
|
|
244
|
+
- `tests/fixtures/synthetic`: synthetic notebooks for targeted pass/fail checks.
|
|
245
|
+
- `tests/fixtures/real`: real-world notebooks sourced from public repositories (with provenance in `tests/fixtures/real/SOURCES.txt`).
|
|
246
|
+
|
|
247
|
+
Refresh pinned real fixtures and print their observed rule-code sets:
|
|
248
|
+
|
|
249
|
+
```shell
|
|
250
|
+
uv run python scripts/refresh_real_fixtures.py
|
|
251
|
+
```
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "pytest-notebook-policy"
|
|
3
|
+
version = "0.8.0"
|
|
4
|
+
description = "Pytest plugin for notebook policy and quality checks"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
license-files = ["LICENSE"]
|
|
8
|
+
authors = [
|
|
9
|
+
{ name = "Michael Booth", email = "michael@databooth.com.au" }
|
|
10
|
+
]
|
|
11
|
+
keywords = ["pytest", "marimo", "jupyter", "notebooks", "lint", "quality", "policy"]
|
|
12
|
+
classifiers = [
|
|
13
|
+
"Development Status :: 3 - Alpha",
|
|
14
|
+
"Framework :: Pytest",
|
|
15
|
+
"Intended Audience :: Developers",
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"Programming Language :: Python :: 3.10",
|
|
18
|
+
"Programming Language :: Python :: 3.11",
|
|
19
|
+
"Programming Language :: Python :: 3.12",
|
|
20
|
+
"Programming Language :: Python :: 3.13",
|
|
21
|
+
"Topic :: Software Development :: Quality Assurance",
|
|
22
|
+
]
|
|
23
|
+
requires-python = ">=3.10"
|
|
24
|
+
dependencies = [
|
|
25
|
+
"jinja2",
|
|
26
|
+
"pytest>=8.0",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[project.urls]
|
|
30
|
+
Homepage = "https://github.com/DataBooth/pytest-notebook-policy"
|
|
31
|
+
Repository = "https://github.com/DataBooth/pytest-notebook-policy"
|
|
32
|
+
Issues = "https://github.com/DataBooth/pytest-notebook-policy/issues"
|
|
33
|
+
|
|
34
|
+
[project.scripts]
|
|
35
|
+
pytest-notebook-quality = "pytest_notebook_policy.quality:main"
|
|
36
|
+
|
|
37
|
+
[project.entry-points.pytest11]
|
|
38
|
+
notebook_policy = "pytest_notebook_policy.plugin"
|
|
39
|
+
|
|
40
|
+
[project.optional-dependencies]
|
|
41
|
+
sync = [
|
|
42
|
+
"jupytext>=1.16",
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
[build-system]
|
|
46
|
+
requires = ["uv_build>=0.11.7,<0.12.0"]
|
|
47
|
+
build-backend = "uv_build"
|
|
48
|
+
|
|
49
|
+
[dependency-groups]
|
|
50
|
+
dev = [
|
|
51
|
+
"jupytext>=1.16",
|
|
52
|
+
"pytest>=8.0",
|
|
53
|
+
"ruff>=0.14.0",
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
[tool.ruff]
|
|
57
|
+
target-version = "py310"
|
|
58
|
+
line-length = 100
|
|
59
|
+
extend-exclude = ["tests/fixtures/real/*.ipynb"]
|
|
60
|
+
|
|
61
|
+
[tool.ruff.lint]
|
|
62
|
+
extend-select = ["S"]
|
|
63
|
+
|
|
64
|
+
[tool.ruff.lint.per-file-ignores]
|
|
65
|
+
"experiments/**/*.ipynb" = ["S311"]
|
|
66
|
+
"experiments/**/*.py" = ["B018", "S311"]
|
|
67
|
+
"src/pytest_notebook_policy/quality.py" = ["S603"]
|
|
68
|
+
"tests/**/*.py" = ["S101"]
|
|
69
|
+
"tests/fixtures/**/*.ipynb" = ["S311"]
|
|
70
|
+
"tests/fixtures/**/*.py" = ["B018", "S311"]
|
|
71
|
+
|
|
72
|
+
[tool.uv.workspace]
|
|
73
|
+
members = [
|
|
74
|
+
"tmp/manual_checks/experiments",
|
|
75
|
+
".",
|
|
76
|
+
]
|