##// END OF EJS Templates
Add experimental support for cell-based execution....
Fernando Perez -
Show More
@@ -159,6 +159,88 b' def get_input_encoding():'
159 # Classes and functions for normal Python syntax handling
159 # Classes and functions for normal Python syntax handling
160 #-----------------------------------------------------------------------------
160 #-----------------------------------------------------------------------------
161
161
162 # HACK! This implementation, written by Robert K a while ago using the
163 # compiler module, is more robust than the other one below, but it expects its
164 # input to be pure python (no ipython syntax). For now we're using it as a
165 # second-pass splitter after the first pass transforms the input to pure
166 # python.
167
168 def split_blocks(python):
169 """ Split multiple lines of code into discrete commands that can be
170 executed singly.
171
172 Parameters
173 ----------
174 python : str
175 Pure, exec'able Python code.
176
177 Returns
178 -------
179 commands : list of str
180 Separate commands that can be exec'ed independently.
181 """
182
183 import compiler
184
185 # compiler.parse treats trailing spaces after a newline as a
186 # SyntaxError. This is different than codeop.CommandCompiler, which
187 # will compile the trailng spaces just fine. We simply strip any
188 # trailing whitespace off. Passing a string with trailing whitespace
189 # to exec will fail however. There seems to be some inconsistency in
190 # how trailing whitespace is handled, but this seems to work.
191 python_ori = python # save original in case we bail on error
192 python = python.strip()
193
194 # The compiler module does not like unicode. We need to convert
195 # it encode it:
196 if isinstance(python, unicode):
197 # Use the utf-8-sig BOM so the compiler detects this a UTF-8
198 # encode string.
199 python = '\xef\xbb\xbf' + python.encode('utf-8')
200
201 # The compiler module will parse the code into an abstract syntax tree.
202 # This has a bug with str("a\nb"), but not str("""a\nb""")!!!
203 try:
204 ast = compiler.parse(python)
205 except:
206 return [python_ori]
207
208 # Uncomment to help debug the ast tree
209 # for n in ast.node:
210 # print n.lineno,'->',n
211
212 # Each separate command is available by iterating over ast.node. The
213 # lineno attribute is the line number (1-indexed) beginning the commands
214 # suite.
215 # lines ending with ";" yield a Discard Node that doesn't have a lineno
216 # attribute. These nodes can and should be discarded. But there are
217 # other situations that cause Discard nodes that shouldn't be discarded.
218 # We might eventually discover other cases where lineno is None and have
219 # to put in a more sophisticated test.
220 linenos = [x.lineno-1 for x in ast.node if x.lineno is not None]
221
222 # When we finally get the slices, we will need to slice all the way to
223 # the end even though we don't have a line number for it. Fortunately,
224 # None does the job nicely.
225 linenos.append(None)
226
227 # Same problem at the other end: sometimes the ast tree has its
228 # first complete statement not starting on line 0. In this case
229 # we might miss part of it. This fixes ticket 266993. Thanks Gael!
230 linenos[0] = 0
231
232 lines = python.splitlines()
233
234 # Create a list of atomic commands.
235 cmds = []
236 for i, j in zip(linenos[:-1], linenos[1:]):
237 cmd = lines[i:j]
238 if cmd:
239 cmds.append('\n'.join(cmd)+'\n')
240
241 return cmds
242
243
162 class InputSplitter(object):
244 class InputSplitter(object):
163 """An object that can split Python source input in executable blocks.
245 """An object that can split Python source input in executable blocks.
164
246
@@ -431,7 +513,11 b' class InputSplitter(object):'
431 # Form the new block with the current source input
513 # Form the new block with the current source input
432 blocks.append(self.source_reset())
514 blocks.append(self.source_reset())
433
515
434 return blocks
516 #return blocks
517 # HACK!!! Now that our input is in blocks but guaranteed to be pure
518 # python syntax, feed it back a second time through the AST-based
519 # splitter, which is more accurate than ours.
520 return split_blocks(''.join(blocks))
435
521
436 #------------------------------------------------------------------------
522 #------------------------------------------------------------------------
437 # Private interface
523 # Private interface
@@ -46,6 +46,7 b' from IPython.core.error import TryNext, UsageError'
46 from IPython.core.extensions import ExtensionManager
46 from IPython.core.extensions import ExtensionManager
47 from IPython.core.fakemodule import FakeModule, init_fakemod_dict
47 from IPython.core.fakemodule import FakeModule, init_fakemod_dict
48 from IPython.core.inputlist import InputList
48 from IPython.core.inputlist import InputList
49 from IPython.core.inputsplitter import IPythonInputSplitter
49 from IPython.core.logger import Logger
50 from IPython.core.logger import Logger
50 from IPython.core.magic import Magic
51 from IPython.core.magic import Magic
51 from IPython.core.payload import PayloadManager
52 from IPython.core.payload import PayloadManager
@@ -154,6 +155,7 b' class InteractiveShell(Configurable, Magic):'
154 exit_now = CBool(False)
155 exit_now = CBool(False)
155 filename = Str("<ipython console>")
156 filename = Str("<ipython console>")
156 ipython_dir= Unicode('', config=True) # Set to get_ipython_dir() in __init__
157 ipython_dir= Unicode('', config=True) # Set to get_ipython_dir() in __init__
158 input_splitter = Instance('IPython.core.inputsplitter.IPythonInputSplitter')
157 logstart = CBool(False, config=True)
159 logstart = CBool(False, config=True)
158 logfile = Str('', config=True)
160 logfile = Str('', config=True)
159 logappend = Str('', config=True)
161 logappend = Str('', config=True)
@@ -212,7 +214,7 b' class InteractiveShell(Configurable, Magic):'
212
214
213 def __init__(self, config=None, ipython_dir=None,
215 def __init__(self, config=None, ipython_dir=None,
214 user_ns=None, user_global_ns=None,
216 user_ns=None, user_global_ns=None,
215 custom_exceptions=((),None)):
217 custom_exceptions=((), None)):
216
218
217 # This is where traits with a config_key argument are updated
219 # This is where traits with a config_key argument are updated
218 # from the values on config.
220 # from the values on config.
@@ -252,7 +254,7 b' class InteractiveShell(Configurable, Magic):'
252 # pre_config_initialization
254 # pre_config_initialization
253 self.init_shadow_hist()
255 self.init_shadow_hist()
254
256
255 # The next section should contain averything that was in ipmaker.
257 # The next section should contain everything that was in ipmaker.
256 self.init_logstart()
258 self.init_logstart()
257
259
258 # The following was in post_config_initialization
260 # The following was in post_config_initialization
@@ -386,6 +388,10 b' class InteractiveShell(Configurable, Magic):'
386 # Indentation management
388 # Indentation management
387 self.indent_current_nsp = 0
389 self.indent_current_nsp = 0
388
390
391 # Input splitter, to split entire cells of input into either individual
392 # interactive statements or whole blocks.
393 self.input_splitter = IPythonInputSplitter()
394
389 def init_encoding(self):
395 def init_encoding(self):
390 # Get system encoding at startup time. Certain terminals (like Emacs
396 # Get system encoding at startup time. Certain terminals (like Emacs
391 # under Win32 have it set to None, and we need to have a known valid
397 # under Win32 have it set to None, and we need to have a known valid
@@ -2061,6 +2067,46 b' class InteractiveShell(Configurable, Magic):'
2061 self.showtraceback()
2067 self.showtraceback()
2062 warn('Unknown failure executing file: <%s>' % fname)
2068 warn('Unknown failure executing file: <%s>' % fname)
2063
2069
2070 def run_cell(self, cell):
2071 """Run the contents of an entire multiline 'cell' of code.
2072
2073 The cell is split into separate blocks which can be executed
2074 individually. Then, based on how many blocks there are, they are
2075 executed as follows:
2076
2077 - A single block: 'single' mode.
2078
2079 If there's more than one block, it depends:
2080
2081 - if the last one is a single line long, run all but the last in
2082 'exec' mode and the very last one in 'single' mode. This makes it
2083 easy to type simple expressions at the end to see computed values.
2084 - otherwise (last one is also multiline), run all in 'exec' mode
2085
2086 When code is executed in 'single' mode, :func:`sys.displayhook` fires,
2087 results are displayed and output prompts are computed. In 'exec' mode,
2088 no results are displayed unless :func:`print` is called explicitly;
2089 this mode is more akin to running a script.
2090
2091 Parameters
2092 ----------
2093 cell : str
2094 A single or multiline string.
2095 """
2096 blocks = self.input_splitter.split_blocks(cell)
2097 if not blocks:
2098 return
2099
2100 if len(blocks) == 1:
2101 self.runlines(blocks[0])
2102
2103 last = blocks[-1]
2104 if len(last.splitlines()) < 2:
2105 map(self.runcode, blocks[:-1])
2106 self.runlines(last)
2107 else:
2108 map(self.runcode, blocks)
2109
2064 def runlines(self, lines, clean=False):
2110 def runlines(self, lines, clean=False):
2065 """Run a string of one or more lines of source.
2111 """Run a string of one or more lines of source.
2066
2112
@@ -2166,7 +2212,7 b' class InteractiveShell(Configurable, Magic):'
2166 else:
2212 else:
2167 return None
2213 return None
2168
2214
2169 def runcode(self,code_obj):
2215 def runcode(self, code_obj):
2170 """Execute a code object.
2216 """Execute a code object.
2171
2217
2172 When an exception occurs, self.showtraceback() is called to display a
2218 When an exception occurs, self.showtraceback() is called to display a
@@ -278,8 +278,8 b' class InputSplitterTestCase(unittest.TestCase):'
278 [['x=1'],
278 [['x=1'],
279 ['y=2']],
279 ['y=2']],
280
280
281 [['x=1'],
281 [['x=1',
282 ['# a comment'],
282 '# a comment'],
283 ['y=11']],
283 ['y=11']],
284
284
285 [['if 1:',
285 [['if 1:',
@@ -322,11 +322,11 b' class InputSplitterTestCase(unittest.TestCase):'
322 # Block splitting with invalid syntax
322 # Block splitting with invalid syntax
323 all_blocks = [ [['a syntax error']],
323 all_blocks = [ [['a syntax error']],
324
324
325 [['x=1'],
325 [['x=1',
326 ['a syntax error']],
326 'another syntax error']],
327
327
328 [['for i in range(10):'
328 [['for i in range(10):'
329 ' an error']],
329 ' yet another error']],
330
330
331 ]
331 ]
332 for block_lines in all_blocks:
332 for block_lines in all_blocks:
@@ -179,7 +179,12 b' class Kernel(Configurable):'
179 else:
179 else:
180 # FIXME: runlines calls the exception handler itself.
180 # FIXME: runlines calls the exception handler itself.
181 shell._reply_content = None
181 shell._reply_content = None
182 shell.runlines(code)
182
183 # Experimental: cell mode! Test more before turning into
184 # default and removing the hacks around runlines.
185 shell.run_cell(code)
186 # For now leave this here until we're sure we can stop using it
187 #shell.runlines(code)
183 except:
188 except:
184 status = u'error'
189 status = u'error'
185 # FIXME: this code right now isn't being used yet by default,
190 # FIXME: this code right now isn't being used yet by default,
@@ -26,12 +26,12 b' from IPython.core.interactiveshell import ('
26 )
26 )
27 from IPython.core.displayhook import DisplayHook
27 from IPython.core.displayhook import DisplayHook
28 from IPython.core.macro import Macro
28 from IPython.core.macro import Macro
29 from IPython.core.payloadpage import install_payload_page
29 from IPython.utils.path import get_py_filename
30 from IPython.utils.path import get_py_filename
30 from IPython.utils.text import StringTypes
31 from IPython.utils.text import StringTypes
31 from IPython.utils.traitlets import Instance, Type, Dict
32 from IPython.utils.traitlets import Instance, Type, Dict
32 from IPython.utils.warn import warn
33 from IPython.utils.warn import warn
33 from IPython.zmq.session import extract_header
34 from IPython.zmq.session import extract_header
34 from IPython.core.payloadpage import install_payload_page
35 from session import Session
35 from session import Session
36
36
37 #-----------------------------------------------------------------------------
37 #-----------------------------------------------------------------------------
General Comments 0
You need to be logged in to leave comments. Login now