Skip to content

Commit 82bf833

Browse files
i-aki-ykrassowski
andauthored
Fix false “undefined name” message caused by cell magic (#1007)
* Fix cell magic is run in a different scope * Apply lint * Update CHANGELOG * Fix regex * Resolve traitlets type warnings, lint, remove `six` --------- Co-authored-by: krassowski <[email protected]>
1 parent ebcb064 commit 82bf833

File tree

3 files changed

+43
-4
lines changed

3 files changed

+43
-4
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1101,8 +1101,14 @@ Requires JupyterLab `>=3.6.0,<4.0.0a0` and Python 3.8 or newer.
11011101
### `jupyter-lsp 0.6.0b0`
11021102

11031103
- features
1104+
11041105
- starts language servers on demand
11051106
- accepts configuration via Jupyter config system (traitlets) and python
11061107
`entry_point`s
11071108
- autodetects language servers for bash, CSS, LESS, SASS, Dockerfile, YAML, JS,
11081109
TypeScript, JSX, TSX, JSON, YAML
1110+
1111+
- bugfixes
1112+
- fix issue that variables declared in cell magics(%%time, %%capture) are masked(
1113+
[#635](https://github.com/jupyter-lsp/jupyterlab-lsp/issues/635)
1114+
)

packages/jupyterlab-lsp/src/transclusions/ipython/overrides.spec.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,17 @@ describe('Default IPython overrides', () => {
6969
expect(reverse).toBe(cellMagicWithArgs);
7070
});
7171

72+
it('some cell magic commands are unwrapped', () => {
73+
const cellMagicToUnwrap = '%%capture --no-display\ntext';
74+
let override = cellMagicsMap.overrideFor(cellMagicToUnwrap)!;
75+
expect(override).toBe(
76+
'# START_CELL_MAGIC("capture", " --no-display")\ntext\n# END_CELL_MAGIC'
77+
);
78+
79+
let reverse = cellMagicsMap.reverse.overrideFor(override);
80+
expect(reverse).toBe(cellMagicToUnwrap);
81+
});
82+
7283
it('escapes docstrings properly', () => {
7384
let override = cellMagicsMap.overrideFor(CELL_MAGIC_WITH_DOCSTRINGS)!;
7485
expect(override).toBe(

packages/jupyterlab-lsp/src/transclusions/ipython/overrides.ts

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@ function emptyOrEscaped(x: string) {
1616
}
1717
}
1818

19+
const MAGICS_TO_UNWRAP = ['time', 'capture'];
20+
21+
function unwrapCellMagic(name: string, firstLine: string, content: string) {
22+
return `# START_CELL_MAGIC("${name}", "${firstLine}")
23+
${content}
24+
# END_CELL_MAGIC`;
25+
}
26+
1927
/**
2028
* Line magics do not have to start with the new line, for example:
2129
* x = !ls
@@ -131,13 +139,27 @@ export let overrides: IScopedCodeOverride[] = [
131139
firstLine = firstLine.slice(0, -1);
132140
}
133141
content = content.replace(/"""/g, '\\"\\"\\"');
134-
return `get_ipython().run_cell_magic("${name}", "${firstLine}", """${content}""")`;
142+
143+
let replaced: string;
144+
if (MAGICS_TO_UNWRAP.includes(name)) {
145+
replaced = unwrapCellMagic(name, firstLine, content);
146+
} else {
147+
replaced = `get_ipython().run_cell_magic("${name}", "${firstLine}", """${content}""")`;
148+
}
149+
return replaced;
135150
},
136151
scope: 'cell',
137152
reverse: {
138-
pattern:
139-
'^get_ipython\\(\\).run_cell_magic\\("(.*?)", "(.*?)", """([\\s\\S]*)"""\\)',
140-
replacement: (match, name, line, content) => {
153+
pattern: '^get_ipython[\\s\\S]*|^# START_CELL_MAGIC[\\s\\S]*',
154+
replacement: code => {
155+
const regCellMagic = RegExp(
156+
'^get_ipython\\(\\).run_cell_magic\\("(.*?)", "(.*?)", """([\\s\\S]*)"""\\)'
157+
);
158+
const regUnwrapped = RegExp(
159+
'^# START_CELL_MAGIC\\("(.*?)", "(.*?)"\\)\\n([\\s\\S]*)\\n# END_CELL_MAGIC$'
160+
);
161+
let m = code.match(regCellMagic) || code.match(regUnwrapped);
162+
let [name, line, content] = m?.slice(1, 4) || ['', '', ''];
141163
content = content.replace(/\\"\\"\\"/g, '"""');
142164
line = unescape(line);
143165
return `%%${name}${line}\n${content}`;

0 commit comments

Comments
 (0)