Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/IPython/core/inputsplitter.py: 58%

317 statements  

« prev     ^ index     » next       coverage.py v7.3.1, created at 2023-09-25 06:05 +0000

1"""DEPRECATED: Input handling and transformation machinery. 

2 

3This module was deprecated in IPython 7.0, in favour of inputtransformer2. 

4 

5The first class in this module, :class:`InputSplitter`, is designed to tell when 

6input from a line-oriented frontend is complete and should be executed, and when 

7the user should be prompted for another line of code instead. The name 'input 

8splitter' is largely for historical reasons. 

9 

10A companion, :class:`IPythonInputSplitter`, provides the same functionality but 

11with full support for the extended IPython syntax (magics, system calls, etc). 

12The code to actually do these transformations is in :mod:`IPython.core.inputtransformer`. 

13:class:`IPythonInputSplitter` feeds the raw code to the transformers in order 

14and stores the results. 

15 

16For more details, see the class docstrings below. 

17""" 

18 

19from warnings import warn 

20 

21warn('IPython.core.inputsplitter is deprecated since IPython 7 in favor of `IPython.core.inputtransformer2`', 

22 DeprecationWarning) 

23 

24# Copyright (c) IPython Development Team. 

25# Distributed under the terms of the Modified BSD License. 

26import ast 

27import codeop 

28import io 

29import re 

30import sys 

31import tokenize 

32import warnings 

33 

34from typing import List 

35 

36from IPython.core.inputtransformer import (leading_indent, 

37 classic_prompt, 

38 ipy_prompt, 

39 cellmagic, 

40 assemble_logical_lines, 

41 help_end, 

42 escaped_commands, 

43 assign_from_magic, 

44 assign_from_system, 

45 assemble_python_lines, 

46 ) 

47from IPython.utils import tokenutil 

48 

49# These are available in this module for backwards compatibility. 

50from IPython.core.inputtransformer import (ESC_SHELL, ESC_SH_CAP, ESC_HELP, 

51 ESC_HELP2, ESC_MAGIC, ESC_MAGIC2, 

52 ESC_QUOTE, ESC_QUOTE2, ESC_PAREN, ESC_SEQUENCES) 

53 

54#----------------------------------------------------------------------------- 

55# Utilities 

56#----------------------------------------------------------------------------- 

57 

58# FIXME: These are general-purpose utilities that later can be moved to the 

59# general ward. Kept here for now because we're being very strict about test 

60# coverage with this code, and this lets us ensure that we keep 100% coverage 

61# while developing. 

62 

63# compiled regexps for autoindent management 

64dedent_re = re.compile('|'.join([ 

65 r'^\s+raise(\s.*)?$', # raise statement (+ space + other stuff, maybe) 

66 r'^\s+raise\([^\)]*\).*$', # wacky raise with immediate open paren 

67 r'^\s+return(\s.*)?$', # normal return (+ space + other stuff, maybe) 

68 r'^\s+return\([^\)]*\).*$', # wacky return with immediate open paren 

69 r'^\s+pass\s*$', # pass (optionally followed by trailing spaces) 

70 r'^\s+break\s*$', # break (optionally followed by trailing spaces) 

71 r'^\s+continue\s*$', # continue (optionally followed by trailing spaces) 

72])) 

73ini_spaces_re = re.compile(r'^([ \t\r\f\v]+)') 

74 

75# regexp to match pure comment lines so we don't accidentally insert 'if 1:' 

76# before pure comments 

77comment_line_re = re.compile(r'^\s*\#') 

78 

79 

80def num_ini_spaces(s): 

81 """Return the number of initial spaces in a string. 

82 

83 Note that tabs are counted as a single space. For now, we do *not* support 

84 mixing of tabs and spaces in the user's input. 

85 

86 Parameters 

87 ---------- 

88 s : string 

89 

90 Returns 

91 ------- 

92 n : int 

93 """ 

94 

95 ini_spaces = ini_spaces_re.match(s) 

96 if ini_spaces: 

97 return ini_spaces.end() 

98 else: 

99 return 0 

100 

101# Fake token types for partial_tokenize: 

102INCOMPLETE_STRING = tokenize.N_TOKENS 

103IN_MULTILINE_STATEMENT = tokenize.N_TOKENS + 1 

104 

105# The 2 classes below have the same API as TokenInfo, but don't try to look up 

106# a token type name that they won't find. 

107class IncompleteString: 

108 type = exact_type = INCOMPLETE_STRING 

109 def __init__(self, s, start, end, line): 

110 self.s = s 

111 self.start = start 

112 self.end = end 

113 self.line = line 

114 

115class InMultilineStatement: 

116 type = exact_type = IN_MULTILINE_STATEMENT 

117 def __init__(self, pos, line): 

118 self.s = '' 

119 self.start = self.end = pos 

120 self.line = line 

121 

122def partial_tokens(s): 

123 """Iterate over tokens from a possibly-incomplete string of code. 

124 

125 This adds two special token types: INCOMPLETE_STRING and 

126 IN_MULTILINE_STATEMENT. These can only occur as the last token yielded, and 

127 represent the two main ways for code to be incomplete. 

128 """ 

129 readline = io.StringIO(s).readline 

130 token = tokenize.TokenInfo(tokenize.NEWLINE, '', (1, 0), (1, 0), '') 

131 try: 

132 for token in tokenutil.generate_tokens_catch_errors(readline): 

133 yield token 

134 except tokenize.TokenError as e: 

135 # catch EOF error 

136 lines = s.splitlines(keepends=True) 

137 end = len(lines), len(lines[-1]) 

138 if 'multi-line string' in e.args[0]: 

139 l, c = start = token.end 

140 s = lines[l-1][c:] + ''.join(lines[l:]) 

141 yield IncompleteString(s, start, end, lines[-1]) 

142 elif 'multi-line statement' in e.args[0]: 

143 yield InMultilineStatement(end, lines[-1]) 

144 else: 

145 raise 

146 

147def find_next_indent(code): 

148 """Find the number of spaces for the next line of indentation""" 

149 tokens = list(partial_tokens(code)) 

150 if tokens[-1].type == tokenize.ENDMARKER: 

151 tokens.pop() 

152 if not tokens: 

153 return 0 

154 

155 while tokens[-1].type in { 

156 tokenize.DEDENT, 

157 tokenize.NEWLINE, 

158 tokenize.COMMENT, 

159 tokenize.ERRORTOKEN, 

160 }: 

161 tokens.pop() 

162 

163 # Starting in Python 3.12, the tokenize module adds implicit newlines at the end 

164 # of input. We need to remove those if we're in a multiline statement 

165 if tokens[-1].type == IN_MULTILINE_STATEMENT: 

166 while tokens[-2].type in {tokenize.NL}: 

167 tokens.pop(-2) 

168 

169 

170 if tokens[-1].type == INCOMPLETE_STRING: 

171 # Inside a multiline string 

172 return 0 

173 

174 # Find the indents used before 

175 prev_indents = [0] 

176 def _add_indent(n): 

177 if n != prev_indents[-1]: 

178 prev_indents.append(n) 

179 

180 tokiter = iter(tokens) 

181 for tok in tokiter: 

182 if tok.type in {tokenize.INDENT, tokenize.DEDENT}: 

183 _add_indent(tok.end[1]) 

184 elif (tok.type == tokenize.NL): 

185 try: 

186 _add_indent(next(tokiter).start[1]) 

187 except StopIteration: 

188 break 

189 

190 last_indent = prev_indents.pop() 

191 

192 # If we've just opened a multiline statement (e.g. 'a = ['), indent more 

193 if tokens[-1].type == IN_MULTILINE_STATEMENT: 

194 if tokens[-2].exact_type in {tokenize.LPAR, tokenize.LSQB, tokenize.LBRACE}: 

195 return last_indent + 4 

196 return last_indent 

197 

198 if tokens[-1].exact_type == tokenize.COLON: 

199 # Line ends with colon - indent 

200 return last_indent + 4 

201 

202 if last_indent: 

203 # Examine the last line for dedent cues - statements like return or 

204 # raise which normally end a block of code. 

205 last_line_starts = 0 

206 for i, tok in enumerate(tokens): 

207 if tok.type == tokenize.NEWLINE: 

208 last_line_starts = i + 1 

209 

210 last_line_tokens = tokens[last_line_starts:] 

211 names = [t.string for t in last_line_tokens if t.type == tokenize.NAME] 

212 if names and names[0] in {'raise', 'return', 'pass', 'break', 'continue'}: 

213 # Find the most recent indentation less than the current level 

214 for indent in reversed(prev_indents): 

215 if indent < last_indent: 

216 return indent 

217 

218 return last_indent 

219 

220 

221def last_blank(src): 

222 """Determine if the input source ends in a blank. 

223 

224 A blank is either a newline or a line consisting of whitespace. 

225 

226 Parameters 

227 ---------- 

228 src : string 

229 A single or multiline string. 

230 """ 

231 if not src: return False 

232 ll = src.splitlines()[-1] 

233 return (ll == '') or ll.isspace() 

234 

235 

236last_two_blanks_re = re.compile(r'\n\s*\n\s*$', re.MULTILINE) 

237last_two_blanks_re2 = re.compile(r'.+\n\s*\n\s+$', re.MULTILINE) 

238 

239def last_two_blanks(src): 

240 """Determine if the input source ends in two blanks. 

241 

242 A blank is either a newline or a line consisting of whitespace. 

243 

244 Parameters 

245 ---------- 

246 src : string 

247 A single or multiline string. 

248 """ 

249 if not src: return False 

250 # The logic here is tricky: I couldn't get a regexp to work and pass all 

251 # the tests, so I took a different approach: split the source by lines, 

252 # grab the last two and prepend '###\n' as a stand-in for whatever was in 

253 # the body before the last two lines. Then, with that structure, it's 

254 # possible to analyze with two regexps. Not the most elegant solution, but 

255 # it works. If anyone tries to change this logic, make sure to validate 

256 # the whole test suite first! 

257 new_src = '\n'.join(['###\n'] + src.splitlines()[-2:]) 

258 return (bool(last_two_blanks_re.match(new_src)) or 

259 bool(last_two_blanks_re2.match(new_src)) ) 

260 

261 

262def remove_comments(src): 

263 """Remove all comments from input source. 

264 

265 Note: comments are NOT recognized inside of strings! 

266 

267 Parameters 

268 ---------- 

269 src : string 

270 A single or multiline input string. 

271 

272 Returns 

273 ------- 

274 String with all Python comments removed. 

275 """ 

276 

277 return re.sub('#.*', '', src) 

278 

279 

280def get_input_encoding(): 

281 """Return the default standard input encoding. 

282 

283 If sys.stdin has no encoding, 'ascii' is returned.""" 

284 # There are strange environments for which sys.stdin.encoding is None. We 

285 # ensure that a valid encoding is returned. 

286 encoding = getattr(sys.stdin, 'encoding', None) 

287 if encoding is None: 

288 encoding = 'ascii' 

289 return encoding 

290 

291#----------------------------------------------------------------------------- 

292# Classes and functions for normal Python syntax handling 

293#----------------------------------------------------------------------------- 

294 

295class InputSplitter(object): 

296 r"""An object that can accumulate lines of Python source before execution. 

297 

298 This object is designed to be fed python source line-by-line, using 

299 :meth:`push`. It will return on each push whether the currently pushed 

300 code could be executed already. In addition, it provides a method called 

301 :meth:`push_accepts_more` that can be used to query whether more input 

302 can be pushed into a single interactive block. 

303 

304 This is a simple example of how an interactive terminal-based client can use 

305 this tool:: 

306 

307 isp = InputSplitter() 

308 while isp.push_accepts_more(): 

309 indent = ' '*isp.indent_spaces 

310 prompt = '>>> ' + indent 

311 line = indent + raw_input(prompt) 

312 isp.push(line) 

313 print 'Input source was:\n', isp.source_reset(), 

314 """ 

315 # A cache for storing the current indentation 

316 # The first value stores the most recently processed source input 

317 # The second value is the number of spaces for the current indentation  

318 # If self.source matches the first value, the second value is a valid 

319 # current indentation. Otherwise, the cache is invalid and the indentation 

320 # must be recalculated. 

321 _indent_spaces_cache = None, None 

322 # String, indicating the default input encoding. It is computed by default 

323 # at initialization time via get_input_encoding(), but it can be reset by a 

324 # client with specific knowledge of the encoding. 

325 encoding = '' 

326 # String where the current full source input is stored, properly encoded. 

327 # Reading this attribute is the normal way of querying the currently pushed 

328 # source code, that has been properly encoded. 

329 source = '' 

330 # Code object corresponding to the current source. It is automatically 

331 # synced to the source, so it can be queried at any time to obtain the code 

332 # object; it will be None if the source doesn't compile to valid Python. 

333 code = None 

334 

335 # Private attributes 

336 

337 # List with lines of input accumulated so far 

338 _buffer: List[str] 

339 # Command compiler 

340 _compile: codeop.CommandCompiler 

341 # Boolean indicating whether the current block is complete 

342 _is_complete = None 

343 # Boolean indicating whether the current block has an unrecoverable syntax error 

344 _is_invalid = False 

345 

346 def __init__(self) -> None: 

347 """Create a new InputSplitter instance.""" 

348 self._buffer = [] 

349 self._compile = codeop.CommandCompiler() 

350 self.encoding = get_input_encoding() 

351 

352 def reset(self): 

353 """Reset the input buffer and associated state.""" 

354 self._buffer[:] = [] 

355 self.source = '' 

356 self.code = None 

357 self._is_complete = False 

358 self._is_invalid = False 

359 

360 def source_reset(self): 

361 """Return the input source and perform a full reset. 

362 """ 

363 out = self.source 

364 self.reset() 

365 return out 

366 

367 def check_complete(self, source): 

368 """Return whether a block of code is ready to execute, or should be continued 

369 

370 This is a non-stateful API, and will reset the state of this InputSplitter. 

371 

372 Parameters 

373 ---------- 

374 source : string 

375 Python input code, which can be multiline. 

376 

377 Returns 

378 ------- 

379 status : str 

380 One of 'complete', 'incomplete', or 'invalid' if source is not a 

381 prefix of valid code. 

382 indent_spaces : int or None 

383 The number of spaces by which to indent the next line of code. If 

384 status is not 'incomplete', this is None. 

385 """ 

386 self.reset() 

387 try: 

388 self.push(source) 

389 except SyntaxError: 

390 # Transformers in IPythonInputSplitter can raise SyntaxError, 

391 # which push() will not catch. 

392 return 'invalid', None 

393 else: 

394 if self._is_invalid: 

395 return 'invalid', None 

396 elif self.push_accepts_more(): 

397 return 'incomplete', self.get_indent_spaces() 

398 else: 

399 return 'complete', None 

400 finally: 

401 self.reset() 

402 

403 def push(self, lines:str) -> bool: 

404 """Push one or more lines of input. 

405 

406 This stores the given lines and returns a status code indicating 

407 whether the code forms a complete Python block or not. 

408 

409 Any exceptions generated in compilation are swallowed, but if an 

410 exception was produced, the method returns True. 

411 

412 Parameters 

413 ---------- 

414 lines : string 

415 One or more lines of Python input. 

416 

417 Returns 

418 ------- 

419 is_complete : boolean 

420 True if the current input source (the result of the current input 

421 plus prior inputs) forms a complete Python execution block. Note that 

422 this value is also stored as a private attribute (``_is_complete``), so it 

423 can be queried at any time. 

424 """ 

425 assert isinstance(lines, str) 

426 self._store(lines) 

427 source = self.source 

428 

429 # Before calling _compile(), reset the code object to None so that if an 

430 # exception is raised in compilation, we don't mislead by having 

431 # inconsistent code/source attributes. 

432 self.code, self._is_complete = None, None 

433 self._is_invalid = False 

434 

435 # Honor termination lines properly 

436 if source.endswith('\\\n'): 

437 return False 

438 

439 try: 

440 with warnings.catch_warnings(): 

441 warnings.simplefilter('error', SyntaxWarning) 

442 self.code = self._compile(source, symbol="exec") 

443 # Invalid syntax can produce any of a number of different errors from 

444 # inside the compiler, so we have to catch them all. Syntax errors 

445 # immediately produce a 'ready' block, so the invalid Python can be 

446 # sent to the kernel for evaluation with possible ipython 

447 # special-syntax conversion. 

448 except (SyntaxError, OverflowError, ValueError, TypeError, 

449 MemoryError, SyntaxWarning): 

450 self._is_complete = True 

451 self._is_invalid = True 

452 else: 

453 # Compilation didn't produce any exceptions (though it may not have 

454 # given a complete code object) 

455 self._is_complete = self.code is not None 

456 

457 return self._is_complete 

458 

459 def push_accepts_more(self): 

460 """Return whether a block of interactive input can accept more input. 

461 

462 This method is meant to be used by line-oriented frontends, who need to 

463 guess whether a block is complete or not based solely on prior and 

464 current input lines. The InputSplitter considers it has a complete 

465 interactive block and will not accept more input when either: 

466 

467 * A SyntaxError is raised 

468 

469 * The code is complete and consists of a single line or a single 

470 non-compound statement 

471 

472 * The code is complete and has a blank line at the end 

473 

474 If the current input produces a syntax error, this method immediately 

475 returns False but does *not* raise the syntax error exception, as 

476 typically clients will want to send invalid syntax to an execution 

477 backend which might convert the invalid syntax into valid Python via 

478 one of the dynamic IPython mechanisms. 

479 """ 

480 

481 # With incomplete input, unconditionally accept more 

482 # A syntax error also sets _is_complete to True - see push() 

483 if not self._is_complete: 

484 #print("Not complete") # debug 

485 return True 

486 

487 # The user can make any (complete) input execute by leaving a blank line 

488 last_line = self.source.splitlines()[-1] 

489 if (not last_line) or last_line.isspace(): 

490 #print("Blank line") # debug 

491 return False 

492 

493 # If there's just a single line or AST node, and we're flush left, as is 

494 # the case after a simple statement such as 'a=1', we want to execute it 

495 # straight away. 

496 if self.get_indent_spaces() == 0: 

497 if len(self.source.splitlines()) <= 1: 

498 return False 

499 

500 try: 

501 code_ast = ast.parse("".join(self._buffer)) 

502 except Exception: 

503 #print("Can't parse AST") # debug 

504 return False 

505 else: 

506 if len(code_ast.body) == 1 and \ 

507 not hasattr(code_ast.body[0], 'body'): 

508 #print("Simple statement") # debug 

509 return False 

510 

511 # General fallback - accept more code 

512 return True 

513 

514 def get_indent_spaces(self): 

515 sourcefor, n = self._indent_spaces_cache 

516 if sourcefor == self.source: 

517 return n 

518 

519 # self.source always has a trailing newline 

520 n = find_next_indent(self.source[:-1]) 

521 self._indent_spaces_cache = (self.source, n) 

522 return n 

523 

524 # Backwards compatibility. I think all code that used .indent_spaces was 

525 # inside IPython, but we can leave this here until IPython 7 in case any 

526 # other modules are using it. -TK, November 2017 

527 indent_spaces = property(get_indent_spaces) 

528 

529 def _store(self, lines, buffer=None, store='source'): 

530 """Store one or more lines of input. 

531 

532 If input lines are not newline-terminated, a newline is automatically 

533 appended.""" 

534 

535 if buffer is None: 

536 buffer = self._buffer 

537 

538 if lines.endswith('\n'): 

539 buffer.append(lines) 

540 else: 

541 buffer.append(lines+'\n') 

542 setattr(self, store, self._set_source(buffer)) 

543 

544 def _set_source(self, buffer): 

545 return u''.join(buffer) 

546 

547 

548class IPythonInputSplitter(InputSplitter): 

549 """An input splitter that recognizes all of IPython's special syntax.""" 

550 

551 # String with raw, untransformed input. 

552 source_raw = '' 

553 

554 # Flag to track when a transformer has stored input that it hasn't given 

555 # back yet. 

556 transformer_accumulating = False 

557 

558 # Flag to track when assemble_python_lines has stored input that it hasn't 

559 # given back yet. 

560 within_python_line = False 

561 

562 # Private attributes 

563 

564 # List with lines of raw input accumulated so far. 

565 _buffer_raw = None 

566 

567 def __init__(self, line_input_checker=True, physical_line_transforms=None, 

568 logical_line_transforms=None, python_line_transforms=None): 

569 super(IPythonInputSplitter, self).__init__() 

570 self._buffer_raw = [] 

571 self._validate = True 

572 

573 if physical_line_transforms is not None: 

574 self.physical_line_transforms = physical_line_transforms 

575 else: 

576 self.physical_line_transforms = [ 

577 leading_indent(), 

578 classic_prompt(), 

579 ipy_prompt(), 

580 cellmagic(end_on_blank_line=line_input_checker), 

581 ] 

582 

583 self.assemble_logical_lines = assemble_logical_lines() 

584 if logical_line_transforms is not None: 

585 self.logical_line_transforms = logical_line_transforms 

586 else: 

587 self.logical_line_transforms = [ 

588 help_end(), 

589 escaped_commands(), 

590 assign_from_magic(), 

591 assign_from_system(), 

592 ] 

593 

594 self.assemble_python_lines = assemble_python_lines() 

595 if python_line_transforms is not None: 

596 self.python_line_transforms = python_line_transforms 

597 else: 

598 # We don't use any of these at present 

599 self.python_line_transforms = [] 

600 

601 @property 

602 def transforms(self): 

603 "Quick access to all transformers." 

604 return self.physical_line_transforms + \ 

605 [self.assemble_logical_lines] + self.logical_line_transforms + \ 

606 [self.assemble_python_lines] + self.python_line_transforms 

607 

608 @property 

609 def transforms_in_use(self): 

610 """Transformers, excluding logical line transformers if we're in a 

611 Python line.""" 

612 t = self.physical_line_transforms[:] 

613 if not self.within_python_line: 

614 t += [self.assemble_logical_lines] + self.logical_line_transforms 

615 return t + [self.assemble_python_lines] + self.python_line_transforms 

616 

617 def reset(self): 

618 """Reset the input buffer and associated state.""" 

619 super(IPythonInputSplitter, self).reset() 

620 self._buffer_raw[:] = [] 

621 self.source_raw = '' 

622 self.transformer_accumulating = False 

623 self.within_python_line = False 

624 

625 for t in self.transforms: 

626 try: 

627 t.reset() 

628 except SyntaxError: 

629 # Nothing that calls reset() expects to handle transformer 

630 # errors 

631 pass 

632 

633 def flush_transformers(self): 

634 def _flush(transform, outs): 

635 """yield transformed lines 

636 

637 always strings, never None 

638 

639 transform: the current transform 

640 outs: an iterable of previously transformed inputs. 

641 Each may be multiline, which will be passed 

642 one line at a time to transform. 

643 """ 

644 for out in outs: 

645 for line in out.splitlines(): 

646 # push one line at a time 

647 tmp = transform.push(line) 

648 if tmp is not None: 

649 yield tmp 

650 

651 # reset the transform 

652 tmp = transform.reset() 

653 if tmp is not None: 

654 yield tmp 

655 

656 out = [] 

657 for t in self.transforms_in_use: 

658 out = _flush(t, out) 

659 

660 out = list(out) 

661 if out: 

662 self._store('\n'.join(out)) 

663 

664 def raw_reset(self): 

665 """Return raw input only and perform a full reset. 

666 """ 

667 out = self.source_raw 

668 self.reset() 

669 return out 

670 

671 def source_reset(self): 

672 try: 

673 self.flush_transformers() 

674 return self.source 

675 finally: 

676 self.reset() 

677 

678 def push_accepts_more(self): 

679 if self.transformer_accumulating: 

680 return True 

681 else: 

682 return super(IPythonInputSplitter, self).push_accepts_more() 

683 

684 def transform_cell(self, cell): 

685 """Process and translate a cell of input. 

686 """ 

687 self.reset() 

688 try: 

689 self.push(cell) 

690 self.flush_transformers() 

691 return self.source 

692 finally: 

693 self.reset() 

694 

695 def push(self, lines:str) -> bool: 

696 """Push one or more lines of IPython input. 

697 

698 This stores the given lines and returns a status code indicating 

699 whether the code forms a complete Python block or not, after processing 

700 all input lines for special IPython syntax. 

701 

702 Any exceptions generated in compilation are swallowed, but if an 

703 exception was produced, the method returns True. 

704 

705 Parameters 

706 ---------- 

707 lines : string 

708 One or more lines of Python input. 

709 

710 Returns 

711 ------- 

712 is_complete : boolean 

713 True if the current input source (the result of the current input 

714 plus prior inputs) forms a complete Python execution block. Note that 

715 this value is also stored as a private attribute (_is_complete), so it 

716 can be queried at any time. 

717 """ 

718 assert isinstance(lines, str) 

719 # We must ensure all input is pure unicode 

720 # ''.splitlines() --> [], but we need to push the empty line to transformers 

721 lines_list = lines.splitlines() 

722 if not lines_list: 

723 lines_list = [''] 

724 

725 # Store raw source before applying any transformations to it. Note 

726 # that this must be done *after* the reset() call that would otherwise 

727 # flush the buffer. 

728 self._store(lines, self._buffer_raw, 'source_raw') 

729 

730 transformed_lines_list = [] 

731 for line in lines_list: 

732 transformed = self._transform_line(line) 

733 if transformed is not None: 

734 transformed_lines_list.append(transformed) 

735 

736 if transformed_lines_list: 

737 transformed_lines = '\n'.join(transformed_lines_list) 

738 return super(IPythonInputSplitter, self).push(transformed_lines) 

739 else: 

740 # Got nothing back from transformers - they must be waiting for 

741 # more input. 

742 return False 

743 

744 def _transform_line(self, line): 

745 """Push a line of input code through the various transformers. 

746 

747 Returns any output from the transformers, or None if a transformer 

748 is accumulating lines. 

749 

750 Sets self.transformer_accumulating as a side effect. 

751 """ 

752 def _accumulating(dbg): 

753 #print(dbg) 

754 self.transformer_accumulating = True 

755 return None 

756 

757 for transformer in self.physical_line_transforms: 

758 line = transformer.push(line) 

759 if line is None: 

760 return _accumulating(transformer) 

761 

762 if not self.within_python_line: 

763 line = self.assemble_logical_lines.push(line) 

764 if line is None: 

765 return _accumulating('acc logical line') 

766 

767 for transformer in self.logical_line_transforms: 

768 line = transformer.push(line) 

769 if line is None: 

770 return _accumulating(transformer) 

771 

772 line = self.assemble_python_lines.push(line) 

773 if line is None: 

774 self.within_python_line = True 

775 return _accumulating('acc python line') 

776 else: 

777 self.within_python_line = False 

778 

779 for transformer in self.python_line_transforms: 

780 line = transformer.push(line) 

781 if line is None: 

782 return _accumulating(transformer) 

783 

784 #print("transformers clear") #debug 

785 self.transformer_accumulating = False 

786 return line 

787