diff --git a/IPython/core/display_trap.py b/IPython/core/display_trap.py old mode 100644 new mode 100755 diff --git a/IPython/core/displayhook.py b/IPython/core/displayhook.py index 55accc5..7868b46 100644 --- a/IPython/core/displayhook.py +++ b/IPython/core/displayhook.py @@ -54,8 +54,9 @@ class DisplayHook(Configurable): """ shell = Instance('IPython.core.interactiveshell.InteractiveShellABC') + # Each call to the In[] prompt raises it by 1, even the first. - prompt_count = Int(0) + #prompt_count = Int(0) def __init__(self, shell=None, cache_size=1000, colors='NoColor', input_sep='\n', @@ -114,6 +115,14 @@ class DisplayHook(Configurable): to_user_ns = {'_':self._,'__':self.__,'___':self.___} self.shell.user_ns.update(to_user_ns) + @property + def prompt_count(self): + return self.shell.execution_count + + @prompt_count.setter + def _set_prompt_count(self, val): + raise ValueError('prompt count is read only') + def _set_prompt_str(self,p_str,cache_def,no_cache_def): if p_str is None: if self.do_full_cache: diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index 7f1d9e7..1ecb6d4 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -397,6 +397,9 @@ class InteractiveShell(Configurable, Magic): # Indentation management self.indent_current_nsp = 0 + # Increasing execution counter + self.execution_count = 0 + def init_environment(self): """Any changes we need to make to the user's environment.""" pass @@ -978,6 +981,9 @@ class InteractiveShell(Configurable, Magic): self.input_hist_raw[:] = [] self.output_hist.clear() + # Reset counter used to index all histories + self.execution_count = 0 + # Restore the user namespaces to minimal usability self.init_user_ns() @@ -2186,42 +2192,68 @@ class InteractiveShell(Configurable, Magic): # needs to go into the translated history and get executed (the # original cell may contain non-python syntax). ipy_cell = ''.join(blocks) - - # Single-block input should behave like an interactive prompt - if len(blocks) == 1: - self.runlines(blocks[0]) - return - # In multi-block input, if the last block is a simple (one-two lines) - # expression, run it in single mode so it produces output. Otherwise - # just feed the whole thing to runcode. - # This seems like a reasonable usability design. - last = blocks[-1] - last_nlines = len(last.splitlines()) - - # Note: below, whenever we call runcode, we must sync history - # ourselves, because runcode is NOT meant to manage history at all. - if last_nlines < 2: - # Here we consider the cell split between 'body' and 'last', store - # all history and execute 'body', and if successful, then proceed - # to execute 'last'. - - # Raw history must contain the unmodified cell - raw_body = '\n'.join(cell.splitlines()[:-last_nlines])+'\n' - self.input_hist_raw.append(raw_body) - # Get the main body to run as a cell - ipy_body = ''.join(blocks[:-1]) - self.input_hist.append(ipy_body) - retcode = self.runcode(ipy_body, post_execute=False) - if retcode==0: - # And the last expression via runlines so it produces output - self.runlines(last) + # Each cell is a *single* input, regardless of how many lines it has + self.execution_count += 1 + + # Store raw and processed history + self.input_hist_raw.append(cell) + self.input_hist.append(ipy_cell) + + # All user code execution must happen with our context managers active + with nested(self.builtin_trap, self.display_trap): + # Single-block input should behave like an interactive prompt + if len(blocks) == 1: + return self.run_one_block(blocks[0]) + + # In multi-block input, if the last block is a simple (one-two + # lines) expression, run it in single mode so it produces output. + # Otherwise just feed the whole thing to runcode. This seems like + # a reasonable usability design. + last = blocks[-1] + last_nlines = len(last.splitlines()) + + # Note: below, whenever we call runcode, we must sync history + # ourselves, because runcode is NOT meant to manage history at all. + if last_nlines < 2: + # Here we consider the cell split between 'body' and 'last', + # store all history and execute 'body', and if successful, then + # proceed to execute 'last'. + + # Get the main body to run as a cell + ipy_body = ''.join(blocks[:-1]) + retcode = self.runcode(ipy_body, post_execute=False) + if retcode==0: + # And the last expression via runlines so it produces output + self.run_one_block(last) + else: + # Run the whole cell as one entity, storing both raw and + # processed input in history + self.runcode(ipy_cell) + + def run_one_block(self, block): + """Run a single interactive block. + + If the block is single-line, dynamic transformations are applied to it + (like automagics, autocall and alias recognition). + """ + if len(block.splitlines()) <= 1: + out = self.run_single_line(block) else: - # Run the whole cell as one entity, storing both raw and processed - # input in history - self.input_hist_raw.append(cell) - self.input_hist.append(ipy_cell) - self.runcode(ipy_cell) + out = self.runcode(block) + return out + + def run_single_line(self, line): + """Run a single-line interactive statement. + + This assumes the input has been transformed to IPython syntax by + applying all static transformations (those with an explicit prefix like + % or !), but it will further try to apply the dynamic ones. + + It does not update history. + """ + tline = self.prefilter_manager.prefilter_line(line) + return self.runsource(tline) def runlines(self, lines, clean=False): """Run a string of one or more lines of source. @@ -2421,6 +2453,7 @@ class InteractiveShell(Configurable, Magic): more = self.runsource('\n'.join(self.buffer), self.filename) if not more: self.resetbuffer() + self.execution_count += 1 return more def resetbuffer(self): diff --git a/IPython/core/logger.py b/IPython/core/logger.py index c177877..1df2165 100644 --- a/IPython/core/logger.py +++ b/IPython/core/logger.py @@ -214,9 +214,11 @@ which already exists. But you must first start the logging process with # but if the opposite is true (a macro can produce multiple inputs # with no output display called), then bring the output counter in # sync: - last_num = len(input_hist)-1 - if in_num != last_num: - in_num = self.shell.displayhook.prompt_count = last_num + ## last_num = len(input_hist)-1 + ## if in_num != last_num: + ## pass # dbg + ## #in_num = self.shell.execution_count = last_num + new_i = '_i%s' % in_num if continuation: self._i00 = '%s%s\n' % (self.shell.user_ns[new_i],line_mod) diff --git a/IPython/core/prompts.py b/IPython/core/prompts.py index 10761cc..2964320 100644 --- a/IPython/core/prompts.py +++ b/IPython/core/prompts.py @@ -372,15 +372,7 @@ class Prompt1(BasePrompt): self.col_p_ni = self.col_p.replace('\001','').replace('\002','') self.col_norm_ni = Colors.normal - def peek_next_prompt(self): - """Get the next prompt, but don't increment the counter.""" - self.cache.prompt_count += 1 - next_prompt = str_safe(self.p_str) - self.cache.prompt_count -= 1 - return next_prompt - def __str__(self): - self.cache.prompt_count += 1 self.cache.last_prompt = str_safe(self.p_str_nocolor).split('\n')[-1] return str_safe(self.p_str) diff --git a/IPython/frontend/terminal/interactiveshell.py b/IPython/frontend/terminal/interactiveshell.py index b0f8398..80f5a92 100644 --- a/IPython/frontend/terminal/interactiveshell.py +++ b/IPython/frontend/terminal/interactiveshell.py @@ -227,6 +227,11 @@ class TerminalInteractiveShell(InteractiveShell): self.readline_startup_hook(self.pre_readline) # exit_now is set by a call to %Exit or %Quit, through the # ask_exit callback. + + # Before showing any prompts, if the counter is at zero, we execute an + # empty line to ensure the user only sees prompts starting at one. + if self.execution_count == 0: + self.push_line('\n') while not self.exit_now: self.hooks.pre_prompt_hook() diff --git a/IPython/zmq/ipkernel.py b/IPython/zmq/ipkernel.py index 0966e7d..2c56dd9 100755 --- a/IPython/zmq/ipkernel.py +++ b/IPython/zmq/ipkernel.py @@ -238,8 +238,9 @@ class Kernel(Configurable): status = u'ok' reply_content[u'status'] = status - # Compute the execution counter so clients can display prompts - reply_content['execution_count'] = shell.displayhook.prompt_count + + # Return the execution counter so clients can display prompts + reply_content['execution_count'] = shell.execution_count # FIXME - fish exception info out of shell, possibly left there by # runlines. We'll need to clean up this logic later.