From 732f3d1a14e0c5c60018e70704fdfdc0d5cecff7 2009-08-27 22:40:52 From: Brian Granger Date: 2009-08-27 22:40:52 Subject: [PATCH] sys.displayhook is now managed dynamically by display_trap. --- diff --git a/IPython/core/builtin_trap.py b/IPython/core/builtin_trap.py index 227d659..28996e5 100644 --- a/IPython/core/builtin_trap.py +++ b/IPython/core/builtin_trap.py @@ -38,8 +38,8 @@ BuiltinUndefined = BuiltinUndefined() class BuiltinTrap(Component): shell = Instance('IPython.core.iplib.InteractiveShell') - def __init__(self, parent, name=None, config=None): - super(BuiltinTrap, self).__init__(parent, name, config) + def __init__(self, parent): + super(BuiltinTrap, self).__init__(parent, None, None) # Don't just grab parent!!! self.shell = Component.get_instances(root=self.root, klass='IPython.core.iplib.InteractiveShell')[0] diff --git a/IPython/core/display_trap.py b/IPython/core/display_trap.py new file mode 100644 index 0000000..3cdaa29 --- /dev/null +++ b/IPython/core/display_trap.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python +# encoding: utf-8 +""" +A context manager for handling sys.displayhook. + +Authors: + +* Robert Kern +* Brian Granger +""" + +#----------------------------------------------------------------------------- +# Copyright (C) 2008-2009 The IPython Development Team +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +import sys + +from IPython.core.component import Component + +#----------------------------------------------------------------------------- +# Classes and functions +#----------------------------------------------------------------------------- + + +class DisplayTrap(Component): + """Object to manage sys.displayhook. + + This came from IPython.core.kernel.display_hook, but is simplified + (no callbacks or formatters) until more of the core is refactored. + """ + + def __init__(self, parent, hook): + super(DisplayTrap, self).__init__(parent, None, None) + + self.hook = hook + self.old_hook = None + + def __enter__(self): + self.set() + return self + + def __exit__(self, type, value, traceback): + self.unset() + return True + + def set(self): + """Set the hook.""" + if sys.displayhook is not self.hook: + self.old_hook = sys.displayhook + sys.displayhook = self.hook + + def unset(self): + """Unset the hook.""" + sys.displayhook = self.old_hook + diff --git a/IPython/core/embed.py b/IPython/core/embed.py index 6ee1aa4..eeff274 100644 --- a/IPython/core/embed.py +++ b/IPython/core/embed.py @@ -26,6 +26,7 @@ Notes from __future__ import with_statement import sys +from contextlib import nested from IPython.core import ultratb from IPython.core.iplib import InteractiveShell @@ -67,18 +68,14 @@ class InteractiveShellEmbed(InteractiveShell): banner1=None, banner2=None, custom_exceptions=((),None), exit_msg=''): - # First we need to save the state of sys.displayhook and - # sys.ipcompleter so we can restore it when we are done. - self.save_sys_displayhook() self.save_sys_ipcompleter() super(InteractiveShellEmbed,self).__init__( parent=parent, config=config, ipythondir=ipythondir, usage=usage, user_ns=user_ns, user_global_ns=user_global_ns, - banner1=banner1, banner2=banner2, + banner1=banner1, banner2=banner2, custom_exceptions=custom_exceptions) - self.save_sys_displayhook_embed() self.exit_msg = exit_msg self.define_magic("kill_embedded", kill_embedded) @@ -88,17 +85,11 @@ class InteractiveShellEmbed(InteractiveShell): mode=self.xmode, call_pdb=self.pdb) - self.restore_sys_displayhook() self.restore_sys_ipcompleter() def init_sys_modules(self): pass - def save_sys_displayhook(self): - # sys.displayhook is a global, we need to save the user's original - # Don't rely on __displayhook__, as the user may have changed that. - self.sys_displayhook_orig = sys.displayhook - def save_sys_ipcompleter(self): """Save readline completer status.""" try: @@ -107,9 +98,6 @@ class InteractiveShellEmbed(InteractiveShell): except: pass # not nested with IPython - def restore_sys_displayhook(self): - sys.displayhook = self.sys_displayhook_orig - def restore_sys_ipcompleter(self): """Restores the readline completer which was in place. @@ -122,12 +110,6 @@ class InteractiveShellEmbed(InteractiveShell): except: pass - def save_sys_displayhook_embed(self): - self.sys_displayhook_embed = sys.displayhook - - def restore_sys_displayhook_embed(self): - sys.displayhook = self.sys_displayhook_embed - def __call__(self, header='', local_ns=None, global_ns=None, dummy=None, stack_depth=1): """Activate the interactive interpreter. @@ -161,8 +143,6 @@ class InteractiveShellEmbed(InteractiveShell): if dummy or (dummy != 0 and self.dummy_mode): return - self.restore_sys_displayhook_embed() - if self.has_readline: self.set_completer() @@ -174,14 +154,12 @@ class InteractiveShellEmbed(InteractiveShell): # Call the embedding code with a stack depth of 1 so it can skip over # our call and get the original caller's namespaces. - self.mainloop(banner, local_ns, global_ns, + self.mainloop(banner, local_ns, global_ns, stack_depth=stack_depth) if self.exit_msg is not None: print self.exit_msg - - # Restore global systems (display, completion) - self.restore_sys_displayhook() + self.restore_sys_ipcompleter() def mainloop(self,header='',local_ns=None,global_ns=None,stack_depth=0): @@ -240,7 +218,7 @@ class InteractiveShellEmbed(InteractiveShell): # actually completes using the frame's locals/globals self.set_completer_frame() - with self.builtin_trap: + with nested(self.builtin_trap, self.display_trap): self.interact(header) # now, purge out the user namespace from anything we might have added diff --git a/IPython/core/iplib.py b/IPython/core/iplib.py index 346da0c..4261755 100644 --- a/IPython/core/iplib.py +++ b/IPython/core/iplib.py @@ -33,6 +33,7 @@ import shutil import string import sys import tempfile +from contextlib import nested from IPython.core import ultratb from IPython.core import debugger, oinspect @@ -41,6 +42,7 @@ from IPython.core import history as ipcorehist from IPython.core import prefilter from IPython.core.autocall import IPyAutocall from IPython.core.builtin_trap import BuiltinTrap +from IPython.core.display_trap import DisplayTrap from IPython.core.fakemodule import FakeModule, init_fakemod_dict from IPython.core.logger import Logger from IPython.core.magic import Magic @@ -940,13 +942,7 @@ class InteractiveShell(Component, Magic): pass def init_displayhook(self): - # I don't like assigning globally to sys, because it means when - # embedding instances, each embedded instance overrides the previous - # choice. But sys.displayhook seems to be called internally by exec, - # so I don't see a way around it. We first save the original and then - # overwrite it. - self.sys_displayhook = sys.displayhook - sys.displayhook = self.outputcache + self.display_trap = DisplayTrap(self, self.outputcache) def init_reload_doctest(self): # Do a proper resetting of doctest, including the necessary displayhook @@ -1061,7 +1057,6 @@ class InteractiveShell(Component, Magic): self._orig_sys_module_state['stdin'] = sys.stdin self._orig_sys_module_state['stdout'] = sys.stdout self._orig_sys_module_state['stderr'] = sys.stderr - self._orig_sys_module_state['displayhook'] = sys.displayhook self._orig_sys_module_state['excepthook'] = sys.excepthook try: self._orig_sys_modules_main_name = self.user_ns['__name__'] @@ -1251,7 +1246,7 @@ class InteractiveShell(Component, Magic): error("Magic function `%s` not found." % magic_name) else: magic_args = self.var_expand(magic_args,1) - with self.builtin_trap: + with nested(self.builtin_trap, self.display_trap): return fn(magic_args) # return result @@ -1348,7 +1343,7 @@ class InteractiveShell(Component, Magic): def ex(self, cmd): """Execute a normal python statement in user namespace.""" - with self.builtin_trap: + with nested(self.builtin_trap, self.display_trap): exec cmd in self.user_global_ns, self.user_ns def ev(self, expr): @@ -1356,7 +1351,7 @@ class InteractiveShell(Component, Magic): Returns the result of evaluation """ - with self.builtin_trap: + with nested(self.builtin_trap, self.display_trap): return eval(expr, self.user_global_ns, self.user_ns) def getoutput(self, cmd): @@ -1688,6 +1683,8 @@ class InteractiveShell(Component, Magic): try: f = file(err.filename) try: + # This should be inside a display_trap block and I + # think it is. sys.displayhook(f.read()) finally: f.close() @@ -1805,7 +1802,7 @@ class InteractiveShell(Component, Magic): internally created default banner. """ - with self.builtin_trap: + with nested(self.builtin_trap, self.display_trap): if self.c: # Emulate Python's -c option self.exec_init_cmd() @@ -2237,7 +2234,7 @@ class InteractiveShell(Component, Magic): lines = lines.splitlines() more = 0 - with self.builtin_trap: + with nested(self.builtin_trap, self.display_trap): for line in lines: # skip blank lines so we don't mess up the prompt counter, but do # NOT skip even a blank line if we are in a code block (more is diff --git a/IPython/core/ultratb.py b/IPython/core/ultratb.py index e79cae9..ddaf0b2 100644 --- a/IPython/core/ultratb.py +++ b/IPython/core/ultratb.py @@ -69,7 +69,8 @@ possible inclusion in future releases. # the file COPYING, distributed as part of this software. #***************************************************************************** -# Required modules +from __future__ import with_statement + import inspect import keyword import linecache @@ -85,16 +86,17 @@ import types # For purposes of monkeypatching inspect to fix a bug in it. from inspect import getsourcefile, getfile, getmodule,\ - ismodule, isclass, ismethod, isfunction, istraceback, isframe, iscode + ismodule, isclass, ismethod, isfunction, istraceback, isframe, iscode # IPython's own modules # Modified pdb which doesn't damage IPython's readline handling from IPython.utils import PyColorize from IPython.core import debugger, ipapi +from IPython.core.display_trap import DisplayTrap from IPython.utils.ipstruct import Struct from IPython.core.excolors import exception_colors -from IPython.utils.genutils import Term,uniq_stable,error,info +from IPython.utils.genutils import Term, uniq_stable, error, info # Globals # amount of space to put line numbers before verbose tracebacks @@ -848,24 +850,21 @@ class VerboseTB(TBTools): self.color_scheme_table.active_scheme_name) # the system displayhook may have changed, restore the original # for pdb - dhook = sys.displayhook - sys.displayhook = sys.__displayhook__ - self.pdb.reset() - # Find the right frame so we don't pop up inside ipython itself - if hasattr(self,'tb'): - etb = self.tb - else: - etb = self.tb = sys.last_traceback - while self.tb.tb_next is not None: - self.tb = self.tb.tb_next - try: + display_trap = DisplayTrap(None, sys.__displayhook__) + with display_trap: + self.pdb.reset() + # Find the right frame so we don't pop up inside ipython itself + if hasattr(self,'tb'): + etb = self.tb + else: + etb = self.tb = sys.last_traceback + while self.tb.tb_next is not None: + self.tb = self.tb.tb_next if etb and etb.tb_next: etb = etb.tb_next self.pdb.botframe = etb.tb_frame self.pdb.interaction(self.tb.tb_frame, self.tb) - finally: - sys.displayhook = dhook - + if hasattr(self,'tb'): del self.tb