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 https://github.com/astral-sh/ruff/issues/8526.

Closes https://github.com/astral-sh/ruff/issues/9648.

## Test Plan

`cargo test`
This commit is contained in:
Charlie Marsh 2024-01-29 04:55:44 -08:00 committed by GitHub
parent c8074b0e18
commit bea8f2ee3a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 113 additions and 7 deletions

View file

@ -80,10 +80,11 @@ impl Cell {
// ```
//
// See: https://ipython.readthedocs.io/en/stable/interactive/magics.html
if lines
.peek()
.and_then(|line| line.split_whitespace().next())
.is_some_and(|token| {
if let Some(line) = lines.peek() {
let mut tokens = line.split_whitespace();
// The first token must be an automagic, like `load_exit`.
if tokens.next().is_some_and(|token| {
matches!(
token,
"alias"
@ -164,9 +165,19 @@ impl Cell {
| "xdel"
| "xmode"
)
})
{
return true;
}) {
// The second token must _not_ be an operator, like `=` (to avoid false positives).
// The assignment operators can never follow an automagic. Some binary operators
// _can_, though (e.g., `cd -` is valid), so we omit them.
if !tokens.next().is_some_and(|token| {
matches!(
token,
"=" | "+=" | "-=" | "*=" | "/=" | "//=" | "%=" | "**=" | "&=" | "|=" | "^="
)
}) {
return true;
}
}
}
// Detect cell magics (which operate on multiple lines).

View file

@ -454,6 +454,7 @@ mod tests {
#[test_case("cell_magic", false)]
#[test_case("valid_cell_magic", true)]
#[test_case("automagic", false)]
#[test_case("automagic_assignment", true)]
#[test_case("automagics", false)]
#[test_case("automagic_before_code", false)]
#[test_case("automagic_after_code", true)]