diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index ce69fa1..cd5e36e 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -2429,7 +2429,7 @@ class InteractiveShell(SingletonConfigurable, Magic): self.showtraceback() warn('Unknown failure executing module: <%s>' % mod_name) - def run_cell(self, raw_cell, store_history=False): + def run_cell(self, raw_cell, store_history=False, silent=False): """Run a complete IPython cell. Parameters @@ -2440,9 +2440,15 @@ class InteractiveShell(SingletonConfigurable, Magic): If True, the raw and translated cell will be stored in IPython's history. For user code calling back into IPython's machinery, this should be set to False. + silent : bool + If True, avoid side-effets, such as implicit displayhooks, history, + and logging. silent=True forces store_history=False. """ if (not raw_cell) or raw_cell.isspace(): return + + if silent: + store_history = False for line in raw_cell.splitlines(): self.input_splitter.push(line) @@ -2467,8 +2473,8 @@ class InteractiveShell(SingletonConfigurable, Magic): if store_history: self.history_manager.store_inputs(self.execution_count, cell, raw_cell) - - self.logger.log(cell, raw_cell) + if not silent: + self.logger.log(cell, raw_cell) if not prefilter_failed: # don't run if prefilter failed @@ -2488,12 +2494,16 @@ class InteractiveShell(SingletonConfigurable, Magic): if store_history: self.execution_count += 1 return None - + + interactivity = "none" if silent else "last_expr" self.run_ast_nodes(code_ast.body, cell_name, - interactivity="last_expr") - + interactivity=interactivity) + # Execute any registered post-execution functions. - for func, status in self._post_execute.iteritems(): + # unless we are silent + post_exec = [] if silent else self._post_execute.iteritems() + + for func, status in post_exec: if self.disable_failing_post_execute and not status: continue try: diff --git a/IPython/core/tests/test_interactiveshell.py b/IPython/core/tests/test_interactiveshell.py index aee855d..21008e7 100644 --- a/IPython/core/tests/test_interactiveshell.py +++ b/IPython/core/tests/test_interactiveshell.py @@ -254,6 +254,61 @@ class InteractiveShellTestCase(unittest.TestCase): self.assertEqual(ip.var_expand(u"{asdf}"), u"{asdf}") # ZeroDivisionError self.assertEqual(ip.var_expand(u"{1/0}"), u"{1/0}") + + def test_silent_nopostexec(self): + """run_cell(silent=True) doesn't invoke post-exec funcs""" + ip = get_ipython() + + d = dict(called=False) + def set_called(): + d['called'] = True + + ip.register_post_execute(set_called) + ip.run_cell("1", silent=True) + self.assertFalse(d['called']) + # double-check that non-silent exec did what we expected + # silent to avoid + ip.run_cell("1") + self.assertTrue(d['called']) + # remove post-exec + ip._post_execute.pop(set_called) + + def test_silent_noadvance(self): + """run_cell(silent=True) doesn't advance execution_count""" + ip = get_ipython() + + ec = ip.execution_count + # silent should force store_history=False + ip.run_cell("1", store_history=True, silent=True) + + self.assertEquals(ec, ip.execution_count) + # double-check that non-silent exec did what we expected + # silent to avoid + ip.run_cell("1", store_history=True) + self.assertEquals(ec+1, ip.execution_count) + + def test_silent_nodisplayhook(self): + """run_cell(silent=True) doesn't trigger displayhook""" + ip = get_ipython() + + d = dict(called=False) + + trap = ip.display_trap + save_hook = trap.hook + + def failing_hook(*args, **kwargs): + d['called'] = True + + try: + trap.hook = failing_hook + ip.run_cell("1", silent=True) + self.assertFalse(d['called']) + # double-check that non-silent exec did what we expected + # silent to avoid + ip.run_cell("1") + self.assertTrue(d['called']) + finally: + trap.hook = save_hook @skipif(sys.version_info[0] >= 3, "softspace removed in py3") def test_print_softspace(self):