##// 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 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 244 class InputSplitter(object):
163 245 """An object that can split Python source input in executable blocks.
164 246
@@ -431,7 +513,11 b' class InputSplitter(object):'
431 513 # Form the new block with the current source input
432 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 523 # Private interface
@@ -46,6 +46,7 b' from IPython.core.error import TryNext, UsageError'
46 46 from IPython.core.extensions import ExtensionManager
47 47 from IPython.core.fakemodule import FakeModule, init_fakemod_dict
48 48 from IPython.core.inputlist import InputList
49 from IPython.core.inputsplitter import IPythonInputSplitter
49 50 from IPython.core.logger import Logger
50 51 from IPython.core.magic import Magic
51 52 from IPython.core.payload import PayloadManager
@@ -154,6 +155,7 b' class InteractiveShell(Configurable, Magic):'
154 155 exit_now = CBool(False)
155 156 filename = Str("<ipython console>")
156 157 ipython_dir= Unicode('', config=True) # Set to get_ipython_dir() in __init__
158 input_splitter = Instance('IPython.core.inputsplitter.IPythonInputSplitter')
157 159 logstart = CBool(False, config=True)
158 160 logfile = Str('', config=True)
159 161 logappend = Str('', config=True)
@@ -212,7 +214,7 b' class InteractiveShell(Configurable, Magic):'
212 214
213 215 def __init__(self, config=None, ipython_dir=None,
214 216 user_ns=None, user_global_ns=None,
215 custom_exceptions=((),None)):
217 custom_exceptions=((), None)):
216 218
217 219 # This is where traits with a config_key argument are updated
218 220 # from the values on config.
@@ -252,7 +254,7 b' class InteractiveShell(Configurable, Magic):'
252 254 # pre_config_initialization
253 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 258 self.init_logstart()
257 259
258 260 # The following was in post_config_initialization
@@ -386,6 +388,10 b' class InteractiveShell(Configurable, Magic):'
386 388 # Indentation management
387 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 395 def init_encoding(self):
390 396 # Get system encoding at startup time. Certain terminals (like Emacs
391 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 2067 self.showtraceback()
2062 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 2110 def runlines(self, lines, clean=False):
2065 2111 """Run a string of one or more lines of source.
2066 2112
@@ -2166,7 +2212,7 b' class InteractiveShell(Configurable, Magic):'
2166 2212 else:
2167 2213 return None
2168 2214
2169 def runcode(self,code_obj):
2215 def runcode(self, code_obj):
2170 2216 """Execute a code object.
2171 2217
2172 2218 When an exception occurs, self.showtraceback() is called to display a
@@ -278,8 +278,8 b' class InputSplitterTestCase(unittest.TestCase):'
278 278 [['x=1'],
279 279 ['y=2']],
280 280
281 [['x=1'],
282 ['# a comment'],
281 [['x=1',
282 '# a comment'],
283 283 ['y=11']],
284 284
285 285 [['if 1:',
@@ -322,11 +322,11 b' class InputSplitterTestCase(unittest.TestCase):'
322 322 # Block splitting with invalid syntax
323 323 all_blocks = [ [['a syntax error']],
324 324
325 [['x=1'],
326 ['a syntax error']],
325 [['x=1',
326 'another syntax error']],
327 327
328 328 [['for i in range(10):'
329 ' an error']],
329 ' yet another error']],
330 330
331 331 ]
332 332 for block_lines in all_blocks:
@@ -179,7 +179,12 b' class Kernel(Configurable):'
179 179 else:
180 180 # FIXME: runlines calls the exception handler itself.
181 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 188 except:
184 189 status = u'error'
185 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 27 from IPython.core.displayhook import DisplayHook
28 28 from IPython.core.macro import Macro
29 from IPython.core.payloadpage import install_payload_page
29 30 from IPython.utils.path import get_py_filename
30 31 from IPython.utils.text import StringTypes
31 32 from IPython.utils.traitlets import Instance, Type, Dict
32 33 from IPython.utils.warn import warn
33 34 from IPython.zmq.session import extract_header
34 from IPython.core.payloadpage import install_payload_page
35 35 from session import Session
36 36
37 37 #-----------------------------------------------------------------------------
General Comments 0
You need to be logged in to leave comments. Login now