##// END OF EJS Templates
Refactor execution logic, so that a multi-line macro is correctly expanded and executed (+ unit test). History is now stored after all input transformations have been done.
Thomas Kluyver -
Show More
@@ -2060,13 +2060,15 b' class InteractiveShell(Configurable, Magic):'
2060 warn('Unknown failure executing file: <%s>' % fname)
2060 warn('Unknown failure executing file: <%s>' % fname)
2061
2061
2062 def run_cell(self, cell):
2062 def run_cell(self, cell):
2063 """Run the contents of an entire multiline 'cell' of code.
2063 """Run the contents of an entire multiline 'cell' of code, and store it
2064 in the history.
2064
2065
2065 The cell is split into separate blocks which can be executed
2066 The cell is split into separate blocks which can be executed
2066 individually. Then, based on how many blocks there are, they are
2067 individually. Then, based on how many blocks there are, they are
2067 executed as follows:
2068 executed as follows:
2068
2069
2069 - A single block: 'single' mode.
2070 - A single block: 'single' mode. If it is also a single line, dynamic
2071 transformations, including automagic and macros, will be applied.
2070
2072
2071 If there's more than one block, it depends:
2073 If there's more than one block, it depends:
2072
2074
@@ -2085,6 +2087,15 b' class InteractiveShell(Configurable, Magic):'
2085 cell : str
2087 cell : str
2086 A single or multiline string.
2088 A single or multiline string.
2087 """
2089 """
2090 # Store the untransformed code
2091 raw_cell = cell
2092
2093 # We only do dynamic transforms on a single line. We need to do this
2094 # first, because a macro can be expanded to several lines, which then
2095 # need to be split into blocks again.
2096 if len(cell.splitlines()) <= 1:
2097 temp = self.input_splitter.split_blocks(cell)
2098 cell = self.prefilter_manager.prefilter_line(temp[0])
2088
2099
2089 # We need to break up the input into executable blocks that can be run
2100 # We need to break up the input into executable blocks that can be run
2090 # in 'single' mode, to provide comfortable user behavior.
2101 # in 'single' mode, to provide comfortable user behavior.
@@ -2096,12 +2107,12 b' class InteractiveShell(Configurable, Magic):'
2096 # Store the 'ipython' version of the cell as well, since that's what
2107 # Store the 'ipython' version of the cell as well, since that's what
2097 # needs to go into the translated history and get executed (the
2108 # needs to go into the translated history and get executed (the
2098 # original cell may contain non-python syntax).
2109 # original cell may contain non-python syntax).
2099 ipy_cell = ''.join(blocks)
2110 cell = ''.join(blocks)
2100
2111
2101 # Store raw and processed history
2112 # Store raw and processed history
2102 self.history_manager.store_inputs(self.execution_count, ipy_cell, cell)
2113 self.history_manager.store_inputs(self.execution_count, cell, raw_cell)
2103
2114
2104 self.logger.log(ipy_cell, cell)
2115 self.logger.log(cell, raw_cell)
2105
2116
2106 # All user code execution must happen with our context managers active
2117 # All user code execution must happen with our context managers active
2107 with nested(self.builtin_trap, self.display_trap):
2118 with nested(self.builtin_trap, self.display_trap):
@@ -2109,19 +2120,17 b' class InteractiveShell(Configurable, Magic):'
2109 # Single-block input should behave like an interactive prompt
2120 # Single-block input should behave like an interactive prompt
2110 if len(blocks) == 1:
2121 if len(blocks) == 1:
2111 # since we return here, we need to update the execution count
2122 # since we return here, we need to update the execution count
2112 out = self.run_one_block(blocks[0])
2123 out = self.run_source(blocks[0])
2113 self.execution_count += 1
2124 self.execution_count += 1
2114 return out
2125 return out
2115
2126
2116 # In multi-block input, if the last block is a simple (one-two
2127 # In multi-block input, if the last block is a simple (one-two
2117 # lines) expression, run it in single mode so it produces output.
2128 # lines) expression, run it in single mode so it produces output.
2118 # Otherwise just feed the whole thing to run_code. This seems like
2129 # Otherwise just run it all in 'exec' mode. This seems like a
2119 # a reasonable usability design.
2130 # reasonable usability design.
2120 last = blocks[-1]
2131 last = blocks[-1]
2121 last_nlines = len(last.splitlines())
2132 last_nlines = len(last.splitlines())
2122
2133
2123 # Note: below, whenever we call run_code, we must sync history
2124 # ourselves, because run_code is NOT meant to manage history at all.
2125 if last_nlines < 2:
2134 if last_nlines < 2:
2126 # Here we consider the cell split between 'body' and 'last',
2135 # Here we consider the cell split between 'body' and 'last',
2127 # store all history and execute 'body', and if successful, then
2136 # store all history and execute 'body', and if successful, then
@@ -2132,8 +2141,8 b' class InteractiveShell(Configurable, Magic):'
2132 retcode = self.run_source(ipy_body, symbol='exec',
2141 retcode = self.run_source(ipy_body, symbol='exec',
2133 post_execute=False)
2142 post_execute=False)
2134 if retcode==0:
2143 if retcode==0:
2135 # And the last expression via runlines so it produces output
2144 # Last expression compiled as 'single' so it produces output
2136 self.run_one_block(last)
2145 self.run_source(last)
2137 else:
2146 else:
2138 # Run the whole cell as one entity, storing both raw and
2147 # Run the whole cell as one entity, storing both raw and
2139 # processed input in history
2148 # processed input in history
@@ -2142,46 +2151,6 b' class InteractiveShell(Configurable, Magic):'
2142 # Each cell is a *single* input, regardless of how many lines it has
2151 # Each cell is a *single* input, regardless of how many lines it has
2143 self.execution_count += 1
2152 self.execution_count += 1
2144
2153
2145 def run_one_block(self, block):
2146 """Run a single interactive block of source code.
2147
2148 If the block is single-line, dynamic transformations are applied to it
2149 (like automagics, autocall and alias recognition).
2150
2151 If the block is multi-line, it must consist of valid Python code only.
2152
2153 Parameters
2154 ----------
2155 block : string
2156 A (possibly multiline) string of code to be executed.
2157
2158 Returns
2159 -------
2160 The output of the underlying execution method used, be it
2161 :meth:`run_source` or :meth:`run_single_line`.
2162 """
2163 if len(block.splitlines()) <= 1:
2164 out = self.run_single_line(block)
2165 else:
2166 # Call run_source, which correctly compiles the input cell.
2167 # run_code must only be called when we know we have a code object,
2168 # as it does a naked exec and the compilation mode may not be what
2169 # we wanted.
2170 out = self.run_source(block)
2171 return out
2172
2173 def run_single_line(self, line):
2174 """Run a single-line interactive statement.
2175
2176 This assumes the input has been transformed to IPython syntax by
2177 applying all static transformations (those with an explicit prefix like
2178 % or !), but it will further try to apply the dynamic ones.
2179
2180 It does not update history.
2181 """
2182 tline = self.prefilter_manager.prefilter_line(line)
2183 return self.run_source(tline)
2184
2185 # PENDING REMOVAL: this method is slated for deletion, once our new
2154 # PENDING REMOVAL: this method is slated for deletion, once our new
2186 # input logic has been 100% moved to frontends and is stable.
2155 # input logic has been 100% moved to frontends and is stable.
2187 def runlines(self, lines, clean=False):
2156 def runlines(self, lines, clean=False):
@@ -163,6 +163,28 b' def test_macro():'
163 # List macros.
163 # List macros.
164 assert "test" in ip.magic("macro")
164 assert "test" in ip.magic("macro")
165
165
166 # XXX This should be a doctest, but the doctest logic doesn't seem to do
167 # dynamic transformations (like expanding macros). Doing it like this for now.
168 def test_macro_run():
169 """Test that we can run a multi-line macro successfully."""
170 ip = get_ipython()
171 ip.history_manager.reset()
172 cmds = ["a=10", "a+=1", "print a", "%macro test 2-3"]
173 for cmd in cmds:
174 ip.run_cell(cmd)
175 nt.assert_equal(ip.user_ns["test"].value, "a+=1\nprint a\n")
176 original_stdout = sys.stdout
177 new_stdout = StringIO()
178 sys.stdout = new_stdout
179 try:
180 ip.run_cell("test")
181 nt.assert_true("12" in new_stdout.getvalue())
182 ip.run_cell("test")
183 nt.assert_true("13" in new_stdout.getvalue())
184 finally:
185 sys.stdout = original_stdout
186 new_stdout.close()
187
166
188
167 # XXX failing for now, until we get clearcmd out of quarantine. But we should
189 # XXX failing for now, until we get clearcmd out of quarantine. But we should
168 # fix this and revert the skip to happen only if numpy is not around.
190 # fix this and revert the skip to happen only if numpy is not around.
General Comments 0
You need to be logged in to leave comments. Login now