Highlight shell escapes inside timing and profiling cell magics - #14
Open
ump45nose wants to merge 1 commit into
Open
Highlight shell escapes inside timing and profiling cell magics#14ump45nose wants to merge 1 commit into
ump45nose wants to merge 1 commit into
Conversation
`%%time`, `%%timeit`, `%%capture` and `%%prun` lexed their body with
`using(Python3Lexer)`, which is the bare Python lexer and knows nothing about
IPython syntax. A shell escape inside one of those bodies therefore came out as
`Token.Error` for the `!` instead of being highlighted as a command:
%%time
!cmd # '!' was Token.Error
The body of each of those four magics is IPython input, not plain Python, which
is why the `!` reaches the system shell at runtime. Verified against IPython
9.17.1 by executing each magic with a shell escape in the body: `%%time`,
`%%timeit`, `%%prun` and `%%capture` all run it, while `%%python` does not.
Delegate those four to `using(this)`, the current lexer's own root state, which
already carries the `!`-escape, line-magic and everything else in
`ipython_tokens`. Using `this` rather than a concrete class keeps the rule
correct for both the Python 2 and Python 3 IPython lexers, which share this
token table.
The other Python-running magics keep delegating to `Python3Lexer`, because
their bodies are not IPython input: `%%debug` runs the body under pdb,
`%%python`/`%%python2`/`%%python3`/`%%pypy` run it in a separate interpreter, and
`%%writefile`/`%%file` treat it as literal file content.
Closes ipython#11
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes the case in the issue: a shell escape inside a cell magic whose body is IPython input was highlighted as an error instead of as a command.
The bug
%%time,%%timeit,%%captureand%%prunlexed their body withusing(Python3Lexer)— the bare Python lexer, which knows nothing about IPython syntax. So the!of a shell escape came out asToken.Error:!!cmdToken.Operator%%time(before)Token.Error%%time(after)Token.OperatorWhich magics get the fix, and why
I checked this against IPython itself rather than guessing, by executing each magic with a shell escape in the body (IPython 9.17.1):
!echo …reaches the shell?%%time%%timeit%%capturecaptured.stdout)%%prun%%debug%%python,%%python2,%%python3,%%pypy%%writefile,%%fileThat is why the change is limited to four rules: the other Python-running magics genuinely do lex plain Python, and the existing test for
%%writefilestill pins that.The change
Those four now delegate to
using(this)— the current lexer's own root state, which already contains the!-escape rule, the line-magic rules and everything else inipython_tokens. Usingthisrather than naming a class keeps the rule correct for both the Python 2 and Python 3 IPython lexers, which share this token table.Testing
The CI matrix is Python 3.8–3.13; nothing added uses syntax newer than 3.8.
Eight test cases are added, in the style of the existing
test_shell_commands:test_shell_commands_inside_ipython_input_cell_magics(4 params) — the body tokenizes as the shell escape it is.test_no_shell_commands_where_the_body_is_not_ipython_input(3 params) —%%debug,%%pythonand%%writefilekeep plain-Python highlighting, so the fix cannot silently widen to magics whose bodies are not IPython input.test_python_still_lexes_inside_a_timing_magic— delegating to this lexer's root keeps ordinary Python highlighting intact.Red/green: with only the test changes applied, the four parametrizations of the first test fail and the other eleven pass — so the failure is specific to the reported behaviour, and the guards are genuinely pinning unchanged behaviour rather than passing by accident.
Final-interface output from the reporter's snippet, through the shipped
ipython3entry point withHtmlFormatter(nowrap=True):and unchanged for a magic where the body is not IPython input:
Notes
Open PR Keep exception message continuation lines as text #13 (also mine) edits
ipython_pygments_lexers.py, but around line 232 inIPythonPartialTracebackLexer; this change is confined to lines 53–150 (ipython_tokens), so the two do not overlap textually.Closes failure on shell commands after
%%time#11I did not change how
%%writefilelexes its body. It treats the body as literal text, so Python highlighting of it is arguably also imprecise, but that is a separate behaviour from the one reported here.This PR description was written with AI assistance.