crates/ruff_linter/resources/mdtest/notebook/cell-boundaries.md
Ruff parses every notebook cell as its own module while retaining a single combined module for linting.
A decorator at the end of a cell would apply to a definition in the next cell if the sources were concatenated. Parsing the cells independently must instead report the syntax error in the decorator's cell.
syntax-error.ipynb:
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": ["# snapshot\n", "@deco"]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": ["def f(): pass"]
}
],
"metadata": {},
"nbformat": 4,
"nbformat_minor": 4
}
error[invalid-syntax]: Expected class, function definition or async function definition after decorator
--> src/syntax-error.ipynb:cell 1:2:6
|
2 | @deco
| ^
|
A notebook containing no Python code cells still parses successfully.
no-code-cells.ipynb:
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": ["# Nothing to check"]
}
],
"metadata": {},
"nbformat": 4,
"nbformat_minor": 4
}
Per-cell parsing inserts tokens at each boundary before merging the cells into one module. This
notebook exercises a trailing Dedent, a definition referenced across cells, and a range suppression
whose directive and import are in separate cells. None of the selected rules should report a
diagnostic.
[lint]
select = [
"E301",
"E302",
"E303",
"E305",
"E306",
"F401",
"F821",
"W291",
"W293",
"W391",
]
boundary-tokens.ipynb:
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": ["def compute():\n", " return 1"]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": ["# ruff: disable[F401]"]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": ["import json\n", "print(compute())"]
}
],
"metadata": {},
"nbformat": 4,
"nbformat_minor": 4
}