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 |
|
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 |
|
|
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 |
|
|
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