Skip to content

Don't rewrite IPython help syntax and prompts in notebooks - #5630

Open
Anandb71 wants to merge 1 commit into
psf:mainfrom
Anandb71:fix-ipython-help-syntax
Open

Anandb71 wants to merge 1 commit into
psf:mainfrom
Anandb71:fix-ipython-help-syntax

Conversation

@Anandb71

Copy link
Copy Markdown
Contributor

Description

To format a notebook cell with magics in it, Black runs IPython's TransformerManager on the cell, masks the get_ipython() calls it produces and turns them back into magics afterwards. IPython also rewrites some syntax that can't be turned back into what was written, and Black then put IPython's version into the cell:

  • help after an object, pd.read_csv? / df.head??, became ?pd.read_csv / ??df.head (and %pinfo obj became ?obj)
  • wildcard searches such as str.*find*? became %psearch str.*find*
  • a pasted >>> or In [1]: session lost its prompts
  • the autocall escapes /f a and ,f a b became f(a) and f("a", "b")

Reproduction on main (9485ad4), same with 26.10.1:

$ cat > repro.ipynb <<'NB'
{"cells":[
 {"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["pd.read_csv?"]},
 {"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["str.*find*?"]},
 {"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":[">>> import math\n",">>> math.sqrt(2)"]}
],"metadata":{"language_info":{"name":"python"}},"nbformat":4,"nbformat_minor":5}
NB
$ black --diff repro.ipynb
--- repro.ipynb	...:cell_0
+++ repro.ipynb	...:cell_0
@@ -1 +1 @@
-pd.read_csv?
+?pd.read_csv
--- repro.ipynb	...:cell_1
+++ repro.ipynb	...:cell_1
@@ -1 +1 @@
-str.*find*?
+%psearch str.*find*
--- repro.ipynb	...:cell_2
+++ repro.ipynb	...:cell_2
@@ -1,2 +1,3 @@
->>> import math
->>> math.sqrt(2)
+import math
+
+math.sqrt(2)
would reformat repro.ipynb

IPython runs both versions the same way, but Black shouldn't rewrite what the user typed, and len? or str.*find*? cells are common in teaching notebooks. With this change the file is left unchanged.

mask_cell now checks that putting the masked magics back gives the original cell, ignoring indentation and surrounding whitespace (IPython strips those anyway), and raises NothingChanged if it doesn't, like Black already does for multi-line magics. This replaces the old line count check, which only caught multi-line magics. Help before the object (?obj), other line and cell magics and indented cells are formatted as before.

test_set_input expected a = b?? to become ??b, which drops the a = . That expectation was set in #3008 when IPython 8.3 changed how it transforms the line; before that, the cell was left alone. It's left alone again now on every IPython version, so the version skip and the IPython import it needed are removed.

Tests: test_rewritten_ipython_syntax_noop covers help and source after the object, a wildcard search, both prompt styles and the two autocall escapes. It fails on main. tox -e py passes (639 passed, 1 skipped, plus 96 jupyter tests), and so do tox -e run_self and pre-commit run -a. The notebook tests also pass with IPython 8.2.0. Formatting the 2,176 code cells of 218 notebooks from other projects gives identical output on main and on this branch.

Checklist - did you ...

  • Implement any code style changes under the --preview style, following the stability policy? (No style change, Black now leaves these cells alone.)
  • Add an entry in CHANGES.md if necessary?
  • Add / update tests if necessary?
  • Add new / update outdated documentation? (Added this case to the lists of skipped cells in the Jupyter guide and the FAQ.)

Black masks the magics in a notebook cell by running IPython's
TransformerManager and turning the get_ipython() calls back into magics.
IPython also rewrites syntax that this can't put back as written:
`df.head?` and `?df.head` become the same call, `str.*find*?` becomes a
`psearch` call, `>>>` and `In [1]:` prompts are removed and the autocall
escapes are turned into calls. Black then wrote `?df.head`,
`%psearch str.*find*` or the code without its prompts into the cell.

Check that putting the masked magics back gives the original cell,
ignoring whitespace that IPython strips anyway, and leave the cell
unchanged otherwise. This also covers the multi-line magics that the old
line count check caught. `a = b??` is left alone again, as it was before
IPython 8.3, so its test no longer depends on the IPython version.
@github-actions

Copy link
Copy Markdown
Contributor

diff-shades results comparing this PR (2829fbb) to main (9485ad4):

--preview style: no changes

--stable style: no changes


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

This branch has not been deployed

No deployments
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.

1 participant