Skip to content

Keep IPython magics in annotated assignments in notebooks - #5628

Merged
JelleZijlstra merged 1 commit into
psf:mainfrom
Anandb71:fix-annotated-magic-assignment
Oct 27, 2026
Merged

JelleZijlstra merged 1 commit into
psf:mainfrom
Anandb71:fix-annotated-magic-assignment

Conversation

@Anandb71

Copy link
Copy Markdown
Contributor

Description

IPython transforms an assignment magic that has a type annotation the same way as a plain one, so in a notebook cell

files: list[str] = !ls *.csv

runs as files: list[str] = get_ipython().getoutput('ls *.csv'). MagicFinder only visited ast.Assign nodes, so the magic in an annotated assignment was never masked, and Black wrote the transformed call back into the cell.

Reproduction on main (f74dfd7):

$ cat > repro.ipynb <<'NB'
{"cells":[{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["files: list[str] = !ls *.csv\n","print(files)"]}],"metadata":{"language_info":{"name":"python"}},"nbformat":4,"nbformat_minor":5}
NB
$ black repro.ipynb
reformatted repro.ipynb

The cell now reads files: list[str] = get_ipython().getoutput("ls *.csv"). The same happens for home: str = %env HOME. With this change the file is left unchanged.

The fix adds visit_AnnAssign to MagicFinder, reusing the visit_Assign logic (node.value is checked the same way).

Tests: new cases in test_magic (annotated ! and %env assignments) and in test_long_magic_assignment_is_not_wrapped_in_parentheses. They fail on main and pass here. tests/test_ipynb.py passes, and the full suite passes apart from the two *_diff_with_color tests, which fail the same way on main in my environment. pre-commit is clean.

Checklist - did you ...

  • Implement any code style changes under the --preview style, following the stability policy?
  • Add an entry in CHANGES.md if necessary?
  • Add / update tests if necessary?
  • Add new / update outdated documentation?

IPython transforms an assignment magic with a type annotation the same
way as a plain one, so

    files: list[str] = !ls *.csv

becomes `files: list[str] = get_ipython().getoutput('ls *.csv')`. The
MagicFinder only looked at `ast.Assign` nodes, so the magic in an
annotated assignment was never masked and Black wrote the transformed
`get_ipython()` call back into the cell. Look for magics in
`ast.AnnAssign` nodes too.
@github-actions

Copy link
Copy Markdown
Contributor

diff-shades results comparing this PR (e4edc88) to main (f74dfd7):

--preview style: no changes

--stable style: no changes


What is this? | Workflow run | diff-shades documentation

@JelleZijlstra
JelleZijlstra merged commit 9485ad4 into psf:main Oct 27, 2026
83 of 84 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants