Show More
@@ -9,9 +9,8 b' This includes the machinery to recognise and transform ``%magic`` commands,' | |||
|
9 | 9 | |
|
10 | 10 | from codeop import compile_command |
|
11 | 11 | import re |
|
12 | import tokenize | |
|
12 | 13 | from typing import List, Tuple |
|
13 | from IPython.utils import tokenize2 | |
|
14 | from IPython.utils.tokenutil import generate_tokens | |
|
15 | 14 | |
|
16 | 15 | _indent_re = re.compile(r'^[ \t]+') |
|
17 | 16 | |
@@ -140,7 +139,7 b' class MagicAssign(TokenTransformBase):' | |||
|
140 | 139 | if (assign_ix is not None) \ |
|
141 | 140 | and (len(line) >= assign_ix + 2) \ |
|
142 | 141 | and (line[assign_ix+1].string == '%') \ |
|
143 |
and (line[assign_ix+2].type == tokenize |
|
|
142 | and (line[assign_ix+2].type == tokenize.NAME): | |
|
144 | 143 | return cls(line[assign_ix+1].start) |
|
145 | 144 | |
|
146 | 145 | def transform(self, lines: List[str]): |
@@ -172,10 +171,10 b' class SystemAssign(TokenTransformBase):' | |||
|
172 | 171 | assign_ix = _find_assign_op(line) |
|
173 | 172 | if (assign_ix is not None) \ |
|
174 | 173 | and (len(line) >= assign_ix + 2) \ |
|
175 |
and (line[assign_ix + 1].type == tokenize |
|
|
174 | and (line[assign_ix + 1].type == tokenize.ERRORTOKEN): | |
|
176 | 175 | ix = assign_ix + 1 |
|
177 | 176 | |
|
178 |
while ix < len(line) and line[ix].type == tokenize |
|
|
177 | while ix < len(line) and line[ix].type == tokenize.ERRORTOKEN: | |
|
179 | 178 | if line[ix].string == '!': |
|
180 | 179 | return cls(line[ix].start) |
|
181 | 180 | elif not line[ix].string.isspace(): |
@@ -289,7 +288,7 b' class EscapedCommand(TokenTransformBase):' | |||
|
289 | 288 | """ |
|
290 | 289 | for line in tokens_by_line: |
|
291 | 290 | ix = 0 |
|
292 |
while line[ix].type in {tokenize |
|
|
291 | while line[ix].type in {tokenize.INDENT, tokenize.DEDENT}: | |
|
293 | 292 | ix += 1 |
|
294 | 293 | if line[ix].string in ESCAPE_SINGLES: |
|
295 | 294 | return cls(line[ix].start) |
@@ -338,7 +337,7 b' class HelpEnd(TokenTransformBase):' | |||
|
338 | 337 | if len(line) > 2 and line[-2].string == '?': |
|
339 | 338 | # Find the first token that's not INDENT/DEDENT |
|
340 | 339 | ix = 0 |
|
341 |
while line[ix].type in {tokenize |
|
|
340 | while line[ix].type in {tokenize.INDENT, tokenize.DEDENT}: | |
|
342 | 341 | ix += 1 |
|
343 | 342 | return cls(line[ix].start, line[-2].start) |
|
344 | 343 | |
@@ -365,11 +364,31 b' class HelpEnd(TokenTransformBase):' | |||
|
365 | 364 | return lines_before + [new_line] + lines_after |
|
366 | 365 | |
|
367 | 366 | def make_tokens_by_line(lines): |
|
367 | """Tokenize a series of lines and group tokens by line. | |
|
368 | ||
|
369 | The tokens for a multiline Python string or expression are | |
|
370 | grouped as one line. | |
|
371 | """ | |
|
372 | # NL tokens are used inside multiline expressions, but also after blank | |
|
373 | # lines or comments. This is intentional - see https://bugs.python.org/issue17061 | |
|
374 | # We want to group the former case together but split the latter, so we | |
|
375 | # track parentheses level, similar to the internals of tokenize. | |
|
376 | NEWLINE, NL = tokenize.NEWLINE, tokenize.NL | |
|
368 | 377 | tokens_by_line = [[]] |
|
369 | for token in generate_tokens(iter(lines).__next__): | |
|
370 | tokens_by_line[-1].append(token) | |
|
371 | if token.type == tokenize2.NEWLINE: | |
|
372 |
tokens_by_line.append( |
|
|
378 | parenlev = 0 | |
|
379 | try: | |
|
380 | for token in tokenize.generate_tokens(iter(lines).__next__): | |
|
381 | tokens_by_line[-1].append(token) | |
|
382 | if (token.type == NEWLINE) \ | |
|
383 | or ((token.type == NL) and (parenlev <= 0)): | |
|
384 | tokens_by_line.append([]) | |
|
385 | elif token.string in {'(', '[', '{'}: | |
|
386 | parenlev += 1 | |
|
387 | elif token.string in {')', ']', '}'}: | |
|
388 | parenlev -= 1 | |
|
389 | except tokenize.TokenError: | |
|
390 | # Input ended in a multiline string or expression. That's OK for us. | |
|
391 | pass | |
|
373 | 392 | |
|
374 | 393 | return tokens_by_line |
|
375 | 394 | |
@@ -490,21 +509,21 b' class TransformerManager:' | |||
|
490 | 509 | return 'invalid', None |
|
491 | 510 | |
|
492 | 511 | tokens_by_line = make_tokens_by_line(lines) |
|
493 |
if tokens_by_line[-1][-1].type != tokenize |
|
|
512 | if tokens_by_line[-1][-1].type != tokenize.ENDMARKER: | |
|
494 | 513 | # We're in a multiline string or expression |
|
495 | 514 | return 'incomplete', find_last_indent(lines) |
|
496 | 515 | |
|
497 | 516 | # Find the last token on the previous line that's not NEWLINE or COMMENT |
|
498 | 517 | toks_last_line = tokens_by_line[-2] |
|
499 | 518 | ix = len(toks_last_line) - 1 |
|
500 |
while ix >= 0 and toks_last_line[ix].type in {tokenize |
|
|
501 |
tokenize |
|
|
519 | while ix >= 0 and toks_last_line[ix].type in {tokenize.NEWLINE, | |
|
520 | tokenize.COMMENT}: | |
|
502 | 521 | ix -= 1 |
|
503 | 522 | |
|
504 | 523 | if toks_last_line[ix].string == ':': |
|
505 | 524 | # The last line starts a block (e.g. 'if foo:') |
|
506 | 525 | ix = 0 |
|
507 |
while toks_last_line[ix].type in {tokenize |
|
|
526 | while toks_last_line[ix].type in {tokenize.INDENT, tokenize.DEDENT}: | |
|
508 | 527 | ix += 1 |
|
509 | 528 | indent = toks_last_line[ix].start[1] |
|
510 | 529 | return 'incomplete', indent + 4 |
General Comments 0
You need to be logged in to leave comments.
Login now