From ec9984dc8f30f361eb45e815ed0a49ddd22ffd64 2010-09-06 03:52:18 From: Fernando Perez Date: 2010-09-06 03:52:18 Subject: [PATCH] Speedup builtin_trap enter/exit by reducing object creation. Most of the work that was being done on *every* enter/exit cycle could be done statically only once, at object creation time. This trap is used on every single code execution, so we need it to be fast. In the long run we probably want to rethink this system altogether, but for now at least we want it fast. --- diff --git a/IPython/core/builtin_trap.py b/IPython/core/builtin_trap.py index 029b2ff..38a8097 100755 --- a/IPython/core/builtin_trap.py +++ b/IPython/core/builtin_trap.py @@ -1,18 +1,17 @@ -#!/usr/bin/env python -# encoding: utf-8 """ A context manager for managing things injected into :mod:`__builtin__`. Authors: * Brian Granger +* Fernando Perez """ - #----------------------------------------------------------------------------- -# Copyright (C) 2008-2009 The IPython Development Team +# Copyright (C) 2010 The IPython Development Team. +# +# Distributed under the terms of the BSD License. # -# Distributed under the terms of the BSD License. The full license is in -# the file COPYING, distributed as part of this software. +# Complete license in the file COPYING.txt, distributed with this software. #----------------------------------------------------------------------------- #----------------------------------------------------------------------------- @@ -30,7 +29,6 @@ from IPython.utils.traitlets import Instance # Classes and functions #----------------------------------------------------------------------------- - class __BuiltinUndefined(object): pass BuiltinUndefined = __BuiltinUndefined() @@ -46,26 +44,41 @@ class BuiltinTrap(Configurable): # Only turn off the trap when the outermost call to __exit__ is made. self._nested_level = 0 self.shell = shell + # builtins we always add + self.auto_builtins = {'exit': Quitter(self.shell, 'exit'), + 'quit': Quitter(self.shell, 'quit'), + 'get_ipython': self.shell.get_ipython, + } + # Recursive reload function + try: + from IPython.lib import deepreload + if self.shell.deep_reload: + self.auto_builtins['reload'] = deepreload.reload + else: + self.auto_builtins['dreload']= deepreload.reload + except ImportError: + pass def __enter__(self): if self._nested_level == 0: - self.set() + self.activate() self._nested_level += 1 # I return self, so callers can use add_builtin in a with clause. return self def __exit__(self, type, value, traceback): if self._nested_level == 1: - self.unset() + self.deactivate() self._nested_level -= 1 # Returning False will cause exceptions to propagate return False def add_builtin(self, key, value): """Add a builtin and save the original.""" - orig = __builtin__.__dict__.get(key, BuiltinUndefined) + bdict = __builtin__.__dict__ + orig = bdict.get(key, BuiltinUndefined) self._orig_builtins[key] = orig - __builtin__.__dict__[key] = value + bdict[key] = value def remove_builtin(self, key): """Remove an added builtin and re-set the original.""" @@ -79,34 +92,26 @@ class BuiltinTrap(Configurable): else: __builtin__.__dict__[key] = orig - def set(self): + def activate(self): """Store ipython references in the __builtin__ namespace.""" - self.add_builtin('exit', Quitter(self.shell, 'exit')) - self.add_builtin('quit', Quitter(self.shell, 'quit')) - self.add_builtin('get_ipython', self.shell.get_ipython) - # Recursive reload function - try: - from IPython.lib import deepreload - if self.shell.deep_reload: - self.add_builtin('reload', deepreload.reload) - else: - self.add_builtin('dreload', deepreload.reload) - del deepreload - except ImportError: - pass + add_builtin = self.add_builtin + for name, func in self.auto_builtins.iteritems(): + add_builtin(name, func) # Keep in the builtins a flag for when IPython is active. We set it # with setdefault so that multiple nested IPythons don't clobber one - # another. Each will increase its value by one upon being activated, - # which also gives us a way to determine the nesting level. - __builtin__.__dict__.setdefault('__IPYTHON__active',0) + # another. + __builtin__.__dict__.setdefault('__IPYTHON__active', 0) - def unset(self): + def deactivate(self): """Remove any builtins which might have been added by add_builtins, or restore overwritten ones to their previous values.""" + # Note: must iterate over a static keys() list because we'll be + # mutating the dict itself + remove_builtin = self.remove_builtin for key in self._orig_builtins.keys(): - self.remove_builtin(key) + remove_builtin(key) self._orig_builtins.clear() self._builtins_added = False try: diff --git a/IPython/testing/globalipapp.py b/IPython/testing/globalipapp.py index cafd9ab..1439ed7 100644 --- a/IPython/testing/globalipapp.py +++ b/IPython/testing/globalipapp.py @@ -144,7 +144,7 @@ def start_ipython(): # These traps are normally only active for interactive use, set them # permanently since we'll be mocking interactive sessions. - shell.builtin_trap.set() + shell.builtin_trap.activate() # Modify the IPython system call with one that uses getoutput, so that we # can capture subcommands and print them to Python's stdout, otherwise the