diff --git a/IPython/core/inputsplitter.py b/IPython/core/inputsplitter.py index bae1c74..b08b15a 100644 --- a/IPython/core/inputsplitter.py +++ b/IPython/core/inputsplitter.py @@ -480,7 +480,7 @@ class IPythonInputSplitter(InputSplitter): # List with lines of raw input accumulated so far. _buffer_raw = None - def __init__(self, physical_line_transforms=None, + def __init__(self, line_input_checker=False, physical_line_transforms=None, logical_line_transforms=None, python_line_transforms=None): super(IPythonInputSplitter, self).__init__() self._buffer_raw = [] @@ -498,7 +498,7 @@ class IPythonInputSplitter(InputSplitter): if logical_line_transforms is not None: self.logical_line_transforms = logical_line_transforms else: - self.logical_line_transforms = [cellmagic(), + self.logical_line_transforms = [cellmagic(end_on_blank_line=line_input_checker), help_end(), escaped_commands(), assign_from_magic(), diff --git a/IPython/core/inputtransformer.py b/IPython/core/inputtransformer.py index 4606623..1b288a7 100644 --- a/IPython/core/inputtransformer.py +++ b/IPython/core/inputtransformer.py @@ -60,8 +60,8 @@ class InputTransformer(object): will allow instantiation with the decorated object. """ @functools.wraps(func) - def transformer_factory(): - return cls(func) + def transformer_factory(**kwargs): + return cls(func, **kwargs) return transformer_factory @@ -84,9 +84,9 @@ class StatelessInputTransformer(InputTransformer): class CoroutineInputTransformer(InputTransformer): """Wrapper for an input transformer implemented as a coroutine.""" - def __init__(self, coro): + def __init__(self, coro, **kwargs): # Prime it - self.coro = coro() + self.coro = coro(**kwargs) next(self.coro) def __repr__(self): @@ -316,7 +316,7 @@ def help_end(line): @CoroutineInputTransformer.wrap -def cellmagic(): +def cellmagic(end_on_blank_line=False): """Captures & transforms cell magics. After a cell magic is started, this stores up any lines it gets until it is @@ -337,7 +337,8 @@ def cellmagic(): first = line body = [] line = (yield None) - while (line is not None) and (line.strip() != ''): + while (line is not None) and \ + ((line.strip() != '') or not end_on_blank_line): body.append(line) line = (yield None) diff --git a/IPython/core/tests/test_inputsplitter.py b/IPython/core/tests/test_inputsplitter.py index bdaab48..8a38816 100644 --- a/IPython/core/tests/test_inputsplitter.py +++ b/IPython/core/tests/test_inputsplitter.py @@ -538,39 +538,35 @@ class CellMagicsCommon(object): out = sp.source_reset() ref = u"get_ipython().run_cell_magic({u}'cellm', {u}'line', {u}'body')\n" nt.assert_equal(out, py3compat.u_format(ref)) + + def test_cellmagic_help(self): + self.sp.push('%%cellm?') + nt.assert_false(self.sp.push_accepts_more()) def tearDown(self): self.sp.reset() class CellModeCellMagics(CellMagicsCommon, unittest.TestCase): - sp = isp.IPythonInputSplitter(input_mode='cell') + sp = isp.IPythonInputSplitter(line_input_checker=False) def test_incremental(self): sp = self.sp - src = '%%cellm line2\n' - sp.push(src) + sp.push('%%cellm firstline\n') nt.assert_true(sp.push_accepts_more()) #1 - src += '\n' - sp.push(src) - # Note: if we ever change the logic to allow full blank lines (see - # _handle_cell_magic), then the following test should change to true - nt.assert_false(sp.push_accepts_more()) #2 - # By now, even with full blanks allowed, a second blank should signal - # the end. For now this test is only a redundancy safety, but don't - # delete it in case we change our mind and the previous one goes to - # true. - src += '\n' - sp.push(src) - nt.assert_false(sp.push_accepts_more()) #3 - + sp.push('line2\n') + nt.assert_true(sp.push_accepts_more()) #2 + sp.push('\n') + # This should accept a blank line and carry on until the cell is reset + nt.assert_true(sp.push_accepts_more()) #3 class LineModeCellMagics(CellMagicsCommon, unittest.TestCase): - sp = isp.IPythonInputSplitter(input_mode='line') + sp = isp.IPythonInputSplitter(line_input_checker=True) def test_incremental(self): sp = self.sp sp.push('%%cellm line2\n') nt.assert_true(sp.push_accepts_more()) #1 sp.push('\n') + # In this case, a blank line should end the cell magic nt.assert_false(sp.push_accepts_more()) #2 diff --git a/IPython/core/tests/test_inputtransformer.py b/IPython/core/tests/test_inputtransformer.py index 2fddf97..d12a176 100644 --- a/IPython/core/tests/test_inputtransformer.py +++ b/IPython/core/tests/test_inputtransformer.py @@ -19,9 +19,9 @@ def transform_and_reset(transformer): return transform # Transformer tests -def transform_checker(tests, transformer): +def transform_checker(tests, transformer, **kwargs): """Utility to loop over test inputs""" - transformer = transformer() + transformer = transformer(**kwargs) try: for inp, tr in tests: if inp is None: @@ -223,7 +223,7 @@ syntax_ml = \ ], [(u'%%bar 123', None), (u'hello', None), - (u'', u_fmt("get_ipython().run_cell_magic({u}'bar', {u}'123', {u}'hello')")), + (None , u_fmt("get_ipython().run_cell_magic({u}'bar', {u}'123', {u}'hello')")), ], ], @@ -348,6 +348,12 @@ def test_escaped_paren(): def test_cellmagic(): for example in syntax_ml['cellmagic']: transform_checker(example, ipt.cellmagic) + + line_example = [(u'%%bar 123', None), + (u'hello', None), + (u'' , u_fmt("get_ipython().run_cell_magic({u}'bar', {u}'123', {u}'hello')")), + ] + transform_checker(line_example, ipt.cellmagic, end_on_blank_line=True) def test_has_comment(): tests = [('text', False),