Skip to content

Commit bea8f2e

Browse files
Detect automagic-like assignments in notebooks (#9653)
## Summary Given a statement like `colors = 6`, we currently treat the cell as an automagic (since `colors` is an automagic) -- i.e., we assume it's equivalent to `%colors = 6`. This PR adds some additional detection whereby if the statement is an _assignment_, we avoid treating it as such. I audited the list of automagics, and I believe this is safe for all of them. Closes #8526. Closes #9648. ## Test Plan `cargo test`
1 parent c8074b0 commit bea8f2e

File tree

6 files changed

+113
-7
lines changed

6 files changed

+113
-7
lines changed

crates/ruff_linter/src/linter.rs

+17
Original file line numberDiff line numberDiff line change
@@ -795,6 +795,23 @@ mod tests {
795795
Ok(())
796796
}
797797

798+
#[test]
799+
fn test_undefined_name() -> Result<(), NotebookError> {
800+
let actual = notebook_path("undefined_name.ipynb");
801+
let expected = notebook_path("undefined_name.ipynb");
802+
let TestedNotebook {
803+
messages,
804+
source_notebook,
805+
..
806+
} = assert_notebook_path(
807+
&actual,
808+
expected,
809+
&settings::LinterSettings::for_rule(Rule::UndefinedName),
810+
)?;
811+
assert_messages!(messages, actual, source_notebook);
812+
Ok(())
813+
}
814+
798815
#[test]
799816
fn test_json_consistency() -> Result<()> {
800817
let actual_path = notebook_path("before_fix.ipynb");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
source: crates/ruff_linter/src/linter.rs
3+
---
4+
undefined_name.ipynb:cell 3:1:7: F821 Undefined name `undefined`
5+
|
6+
1 | print(undefined)
7+
| ^^^^^^^^^ F821
8+
|
9+
10+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"execution_count": null,
3+
"cell_type": "code",
4+
"id": "1",
5+
"metadata": {},
6+
"outputs": [],
7+
"source": ["colors = 6\n", "print(colors)"]
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": null,
6+
"id": "a0efffbc-85f1-4513-bf49-5387ec3a2a4e",
7+
"metadata": {},
8+
"outputs": [],
9+
"source": [
10+
"colors = 6"
11+
]
12+
},
13+
{
14+
"cell_type": "code",
15+
"execution_count": null,
16+
"id": "6e0b2b50-43f2-4f59-951d-9404dd560ae4",
17+
"metadata": {
18+
"is_executing": true
19+
},
20+
"outputs": [],
21+
"source": [
22+
"print(colors)"
23+
]
24+
},
25+
{
26+
"cell_type": "code",
27+
"execution_count": null,
28+
"outputs": [],
29+
"source": [
30+
"print(undefined)"
31+
],
32+
"metadata": {
33+
"collapsed": false
34+
},
35+
"id": "884e0e4e686fd56"
36+
}
37+
],
38+
"metadata": {
39+
"kernelspec": {
40+
"display_name": "Python (ruff)",
41+
"language": "python",
42+
"name": "ruff"
43+
},
44+
"language_info": {
45+
"codemirror_mode": {
46+
"name": "ipython",
47+
"version": 3
48+
},
49+
"file_extension": ".py",
50+
"mimetype": "text/x-python",
51+
"name": "python",
52+
"nbconvert_exporter": "python",
53+
"pygments_lexer": "ipython3",
54+
"version": "3.11.3"
55+
}
56+
},
57+
"nbformat": 4,
58+
"nbformat_minor": 5
59+
}

crates/ruff_notebook/src/cell.rs

+18-7
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,11 @@ impl Cell {
8080
// ```
8181
//
8282
// See: https://ipython.readthedocs.io/en/stable/interactive/magics.html
83-
if lines
84-
.peek()
85-
.and_then(|line| line.split_whitespace().next())
86-
.is_some_and(|token| {
83+
if let Some(line) = lines.peek() {
84+
let mut tokens = line.split_whitespace();
85+
86+
// The first token must be an automagic, like `load_exit`.
87+
if tokens.next().is_some_and(|token| {
8788
matches!(
8889
token,
8990
"alias"
@@ -164,9 +165,19 @@ impl Cell {
164165
| "xdel"
165166
| "xmode"
166167
)
167-
})
168-
{
169-
return true;
168+
}) {
169+
// The second token must _not_ be an operator, like `=` (to avoid false positives).
170+
// The assignment operators can never follow an automagic. Some binary operators
171+
// _can_, though (e.g., `cd -` is valid), so we omit them.
172+
if !tokens.next().is_some_and(|token| {
173+
matches!(
174+
token,
175+
"=" | "+=" | "-=" | "*=" | "/=" | "//=" | "%=" | "**=" | "&=" | "|=" | "^="
176+
)
177+
}) {
178+
return true;
179+
}
180+
}
170181
}
171182

172183
// Detect cell magics (which operate on multiple lines).

crates/ruff_notebook/src/notebook.rs

+1
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,7 @@ mod tests {
454454
#[test_case("cell_magic", false)]
455455
#[test_case("valid_cell_magic", true)]
456456
#[test_case("automagic", false)]
457+
#[test_case("automagic_assignment", true)]
457458
#[test_case("automagics", false)]
458459
#[test_case("automagic_before_code", false)]
459460
#[test_case("automagic_after_code", true)]

0 commit comments

Comments
 (0)