##// 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 2060 warn('Unknown failure executing file: <%s>' % fname)
2061 2061
2062 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 2066 The cell is split into separate blocks which can be executed
2066 2067 individually. Then, based on how many blocks there are, they are
2067 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 2073 If there's more than one block, it depends:
2072 2074
@@ -2085,6 +2087,15 b' class InteractiveShell(Configurable, Magic):'
2085 2087 cell : str
2086 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 2100 # We need to break up the input into executable blocks that can be run
2090 2101 # in 'single' mode, to provide comfortable user behavior.
@@ -2096,12 +2107,12 b' class InteractiveShell(Configurable, Magic):'
2096 2107 # Store the 'ipython' version of the cell as well, since that's what
2097 2108 # needs to go into the translated history and get executed (the
2098 2109 # original cell may contain non-python syntax).
2099 ipy_cell = ''.join(blocks)
2110 cell = ''.join(blocks)
2100 2111
2101 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 2117 # All user code execution must happen with our context managers active
2107 2118 with nested(self.builtin_trap, self.display_trap):
@@ -2109,19 +2120,17 b' class InteractiveShell(Configurable, Magic):'
2109 2120 # Single-block input should behave like an interactive prompt
2110 2121 if len(blocks) == 1:
2111 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 2124 self.execution_count += 1
2114 2125 return out
2115 2126
2116 2127 # In multi-block input, if the last block is a simple (one-two
2117 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
2119 # a reasonable usability design.
2129 # Otherwise just run it all in 'exec' mode. This seems like a
2130 # reasonable usability design.
2120 2131 last = blocks[-1]
2121 2132 last_nlines = len(last.splitlines())
2122
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.
2133
2125 2134 if last_nlines < 2:
2126 2135 # Here we consider the cell split between 'body' and 'last',
2127 2136 # store all history and execute 'body', and if successful, then
@@ -2132,8 +2141,8 b' class InteractiveShell(Configurable, Magic):'
2132 2141 retcode = self.run_source(ipy_body, symbol='exec',
2133 2142 post_execute=False)
2134 2143 if retcode==0:
2135 # And the last expression via runlines so it produces output
2136 self.run_one_block(last)
2144 # Last expression compiled as 'single' so it produces output
2145 self.run_source(last)
2137 2146 else:
2138 2147 # Run the whole cell as one entity, storing both raw and
2139 2148 # processed input in history
@@ -2142,46 +2151,6 b' class InteractiveShell(Configurable, Magic):'
2142 2151 # Each cell is a *single* input, regardless of how many lines it has
2143 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 2154 # PENDING REMOVAL: this method is slated for deletion, once our new
2186 2155 # input logic has been 100% moved to frontends and is stable.
2187 2156 def runlines(self, lines, clean=False):
@@ -163,6 +163,28 b' def test_macro():'
163 163 # List macros.
164 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 189 # XXX failing for now, until we get clearcmd out of quarantine. But we should
168 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