##// END OF EJS Templates
Speedup builtin_trap enter/exit by reducing object creation....
Fernando Perez -
Show More
@@ -1,115 +1,120 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 """
1 """
4 A context manager for managing things injected into :mod:`__builtin__`.
2 A context manager for managing things injected into :mod:`__builtin__`.
5
3
6 Authors:
4 Authors:
7
5
8 * Brian Granger
6 * Brian Granger
7 * Fernando Perez
9 """
8 """
10
11 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
12 # Copyright (C) 2008-2009 The IPython Development Team
10 # Copyright (C) 2010 The IPython Development Team.
11 #
12 # Distributed under the terms of the BSD License.
13 #
13 #
14 # Distributed under the terms of the BSD License. The full license is in
14 # Complete license in the file COPYING.txt, distributed with this software.
15 # the file COPYING, distributed as part of this software.
16 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
17
16
18 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
19 # Imports
18 # Imports
20 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
21
20
22 import __builtin__
21 import __builtin__
23
22
24 from IPython.config.configurable import Configurable
23 from IPython.config.configurable import Configurable
25 from IPython.core.quitter import Quitter
24 from IPython.core.quitter import Quitter
26
25
27 from IPython.utils.traitlets import Instance
26 from IPython.utils.traitlets import Instance
28
27
29 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
30 # Classes and functions
29 # Classes and functions
31 #-----------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
32
31
33
34 class __BuiltinUndefined(object): pass
32 class __BuiltinUndefined(object): pass
35 BuiltinUndefined = __BuiltinUndefined()
33 BuiltinUndefined = __BuiltinUndefined()
36
34
37
35
38 class BuiltinTrap(Configurable):
36 class BuiltinTrap(Configurable):
39
37
40 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
38 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
41
39
42 def __init__(self, shell=None):
40 def __init__(self, shell=None):
43 super(BuiltinTrap, self).__init__(shell=shell, config=None)
41 super(BuiltinTrap, self).__init__(shell=shell, config=None)
44 self._orig_builtins = {}
42 self._orig_builtins = {}
45 # We define this to track if a single BuiltinTrap is nested.
43 # We define this to track if a single BuiltinTrap is nested.
46 # Only turn off the trap when the outermost call to __exit__ is made.
44 # Only turn off the trap when the outermost call to __exit__ is made.
47 self._nested_level = 0
45 self._nested_level = 0
48 self.shell = shell
46 self.shell = shell
47 # builtins we always add
48 self.auto_builtins = {'exit': Quitter(self.shell, 'exit'),
49 'quit': Quitter(self.shell, 'quit'),
50 'get_ipython': self.shell.get_ipython,
51 }
52 # Recursive reload function
53 try:
54 from IPython.lib import deepreload
55 if self.shell.deep_reload:
56 self.auto_builtins['reload'] = deepreload.reload
57 else:
58 self.auto_builtins['dreload']= deepreload.reload
59 except ImportError:
60 pass
49
61
50 def __enter__(self):
62 def __enter__(self):
51 if self._nested_level == 0:
63 if self._nested_level == 0:
52 self.set()
64 self.activate()
53 self._nested_level += 1
65 self._nested_level += 1
54 # I return self, so callers can use add_builtin in a with clause.
66 # I return self, so callers can use add_builtin in a with clause.
55 return self
67 return self
56
68
57 def __exit__(self, type, value, traceback):
69 def __exit__(self, type, value, traceback):
58 if self._nested_level == 1:
70 if self._nested_level == 1:
59 self.unset()
71 self.deactivate()
60 self._nested_level -= 1
72 self._nested_level -= 1
61 # Returning False will cause exceptions to propagate
73 # Returning False will cause exceptions to propagate
62 return False
74 return False
63
75
64 def add_builtin(self, key, value):
76 def add_builtin(self, key, value):
65 """Add a builtin and save the original."""
77 """Add a builtin and save the original."""
66 orig = __builtin__.__dict__.get(key, BuiltinUndefined)
78 bdict = __builtin__.__dict__
79 orig = bdict.get(key, BuiltinUndefined)
67 self._orig_builtins[key] = orig
80 self._orig_builtins[key] = orig
68 __builtin__.__dict__[key] = value
81 bdict[key] = value
69
82
70 def remove_builtin(self, key):
83 def remove_builtin(self, key):
71 """Remove an added builtin and re-set the original."""
84 """Remove an added builtin and re-set the original."""
72 try:
85 try:
73 orig = self._orig_builtins.pop(key)
86 orig = self._orig_builtins.pop(key)
74 except KeyError:
87 except KeyError:
75 pass
88 pass
76 else:
89 else:
77 if orig is BuiltinUndefined:
90 if orig is BuiltinUndefined:
78 del __builtin__.__dict__[key]
91 del __builtin__.__dict__[key]
79 else:
92 else:
80 __builtin__.__dict__[key] = orig
93 __builtin__.__dict__[key] = orig
81
94
82 def set(self):
95 def activate(self):
83 """Store ipython references in the __builtin__ namespace."""
96 """Store ipython references in the __builtin__ namespace."""
84 self.add_builtin('exit', Quitter(self.shell, 'exit'))
85 self.add_builtin('quit', Quitter(self.shell, 'quit'))
86 self.add_builtin('get_ipython', self.shell.get_ipython)
87
97
88 # Recursive reload function
98 add_builtin = self.add_builtin
89 try:
99 for name, func in self.auto_builtins.iteritems():
90 from IPython.lib import deepreload
100 add_builtin(name, func)
91 if self.shell.deep_reload:
92 self.add_builtin('reload', deepreload.reload)
93 else:
94 self.add_builtin('dreload', deepreload.reload)
95 del deepreload
96 except ImportError:
97 pass
98
101
99 # Keep in the builtins a flag for when IPython is active. We set it
102 # Keep in the builtins a flag for when IPython is active. We set it
100 # with setdefault so that multiple nested IPythons don't clobber one
103 # with setdefault so that multiple nested IPythons don't clobber one
101 # another. Each will increase its value by one upon being activated,
104 # another.
102 # which also gives us a way to determine the nesting level.
105 __builtin__.__dict__.setdefault('__IPYTHON__active', 0)
103 __builtin__.__dict__.setdefault('__IPYTHON__active',0)
104
106
105 def unset(self):
107 def deactivate(self):
106 """Remove any builtins which might have been added by add_builtins, or
108 """Remove any builtins which might have been added by add_builtins, or
107 restore overwritten ones to their previous values."""
109 restore overwritten ones to their previous values."""
110 # Note: must iterate over a static keys() list because we'll be
111 # mutating the dict itself
112 remove_builtin = self.remove_builtin
108 for key in self._orig_builtins.keys():
113 for key in self._orig_builtins.keys():
109 self.remove_builtin(key)
114 remove_builtin(key)
110 self._orig_builtins.clear()
115 self._orig_builtins.clear()
111 self._builtins_added = False
116 self._builtins_added = False
112 try:
117 try:
113 del __builtin__.__dict__['__IPYTHON__active']
118 del __builtin__.__dict__['__IPYTHON__active']
114 except KeyError:
119 except KeyError:
115 pass
120 pass
@@ -1,170 +1,170 b''
1 """Global IPython app to support test running.
1 """Global IPython app to support test running.
2
2
3 We must start our own ipython object and heavily muck with it so that all the
3 We must start our own ipython object and heavily muck with it so that all the
4 modifications IPython makes to system behavior don't send the doctest machinery
4 modifications IPython makes to system behavior don't send the doctest machinery
5 into a fit. This code should be considered a gross hack, but it gets the job
5 into a fit. This code should be considered a gross hack, but it gets the job
6 done.
6 done.
7 """
7 """
8
8
9 from __future__ import absolute_import
9 from __future__ import absolute_import
10
10
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 # Copyright (C) 2009 The IPython Development Team
12 # Copyright (C) 2009 The IPython Development Team
13 #
13 #
14 # Distributed under the terms of the BSD License. The full license is in
14 # Distributed under the terms of the BSD License. The full license is in
15 # the file COPYING, distributed as part of this software.
15 # the file COPYING, distributed as part of this software.
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 # Imports
19 # Imports
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
22 import __builtin__
22 import __builtin__
23 import commands
23 import commands
24 import os
24 import os
25 import sys
25 import sys
26
26
27 from . import tools
27 from . import tools
28
28
29 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
30 # Functions
30 # Functions
31 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
32
32
33 # Hack to modify the %run command so we can sync the user's namespace with the
33 # Hack to modify the %run command so we can sync the user's namespace with the
34 # test globals. Once we move over to a clean magic system, this will be done
34 # test globals. Once we move over to a clean magic system, this will be done
35 # with much less ugliness.
35 # with much less ugliness.
36
36
37 class py_file_finder(object):
37 class py_file_finder(object):
38 def __init__(self,test_filename):
38 def __init__(self,test_filename):
39 self.test_filename = test_filename
39 self.test_filename = test_filename
40
40
41 def __call__(self,name):
41 def __call__(self,name):
42 from IPython.utils.path import get_py_filename
42 from IPython.utils.path import get_py_filename
43 try:
43 try:
44 return get_py_filename(name)
44 return get_py_filename(name)
45 except IOError:
45 except IOError:
46 test_dir = os.path.dirname(self.test_filename)
46 test_dir = os.path.dirname(self.test_filename)
47 new_path = os.path.join(test_dir,name)
47 new_path = os.path.join(test_dir,name)
48 return get_py_filename(new_path)
48 return get_py_filename(new_path)
49
49
50
50
51 def _run_ns_sync(self,arg_s,runner=None):
51 def _run_ns_sync(self,arg_s,runner=None):
52 """Modified version of %run that syncs testing namespaces.
52 """Modified version of %run that syncs testing namespaces.
53
53
54 This is strictly needed for running doctests that call %run.
54 This is strictly needed for running doctests that call %run.
55 """
55 """
56 #print >> sys.stderr, 'in run_ns_sync', arg_s # dbg
56 #print >> sys.stderr, 'in run_ns_sync', arg_s # dbg
57
57
58 _ip = get_ipython()
58 _ip = get_ipython()
59 finder = py_file_finder(arg_s)
59 finder = py_file_finder(arg_s)
60 out = _ip.magic_run_ori(arg_s,runner,finder)
60 out = _ip.magic_run_ori(arg_s,runner,finder)
61 return out
61 return out
62
62
63
63
64 class ipnsdict(dict):
64 class ipnsdict(dict):
65 """A special subclass of dict for use as an IPython namespace in doctests.
65 """A special subclass of dict for use as an IPython namespace in doctests.
66
66
67 This subclass adds a simple checkpointing capability so that when testing
67 This subclass adds a simple checkpointing capability so that when testing
68 machinery clears it (we use it as the test execution context), it doesn't
68 machinery clears it (we use it as the test execution context), it doesn't
69 get completely destroyed.
69 get completely destroyed.
70 """
70 """
71
71
72 def __init__(self,*a):
72 def __init__(self,*a):
73 dict.__init__(self,*a)
73 dict.__init__(self,*a)
74 self._savedict = {}
74 self._savedict = {}
75
75
76 def clear(self):
76 def clear(self):
77 dict.clear(self)
77 dict.clear(self)
78 self.update(self._savedict)
78 self.update(self._savedict)
79
79
80 def _checkpoint(self):
80 def _checkpoint(self):
81 self._savedict.clear()
81 self._savedict.clear()
82 self._savedict.update(self)
82 self._savedict.update(self)
83
83
84 def update(self,other):
84 def update(self,other):
85 self._checkpoint()
85 self._checkpoint()
86 dict.update(self,other)
86 dict.update(self,other)
87
87
88 # If '_' is in the namespace, python won't set it when executing code,
88 # If '_' is in the namespace, python won't set it when executing code,
89 # and we have examples that test it. So we ensure that the namespace
89 # and we have examples that test it. So we ensure that the namespace
90 # is always 'clean' of it before it's used for test code execution.
90 # is always 'clean' of it before it's used for test code execution.
91 self.pop('_',None)
91 self.pop('_',None)
92
92
93 # The builtins namespace must *always* be the real __builtin__ module,
93 # The builtins namespace must *always* be the real __builtin__ module,
94 # else weird stuff happens. The main ipython code does have provisions
94 # else weird stuff happens. The main ipython code does have provisions
95 # to ensure this after %run, but since in this class we do some
95 # to ensure this after %run, but since in this class we do some
96 # aggressive low-level cleaning of the execution namespace, we need to
96 # aggressive low-level cleaning of the execution namespace, we need to
97 # correct for that ourselves, to ensure consitency with the 'real'
97 # correct for that ourselves, to ensure consitency with the 'real'
98 # ipython.
98 # ipython.
99 self['__builtins__'] = __builtin__
99 self['__builtins__'] = __builtin__
100
100
101
101
102 def get_ipython():
102 def get_ipython():
103 # This will get replaced by the real thing once we start IPython below
103 # This will get replaced by the real thing once we start IPython below
104 return start_ipython()
104 return start_ipython()
105
105
106
106
107 def start_ipython():
107 def start_ipython():
108 """Start a global IPython shell, which we need for IPython-specific syntax.
108 """Start a global IPython shell, which we need for IPython-specific syntax.
109 """
109 """
110 global get_ipython
110 global get_ipython
111
111
112 # This function should only ever run once!
112 # This function should only ever run once!
113 if hasattr(start_ipython, 'already_called'):
113 if hasattr(start_ipython, 'already_called'):
114 return
114 return
115 start_ipython.already_called = True
115 start_ipython.already_called = True
116
116
117 from IPython.frontend.terminal import interactiveshell
117 from IPython.frontend.terminal import interactiveshell
118
118
119 def xsys(cmd):
119 def xsys(cmd):
120 """Execute a command and print its output.
120 """Execute a command and print its output.
121
121
122 This is just a convenience function to replace the IPython system call
122 This is just a convenience function to replace the IPython system call
123 with one that is more doctest-friendly.
123 with one that is more doctest-friendly.
124 """
124 """
125 cmd = _ip.var_expand(cmd,depth=1)
125 cmd = _ip.var_expand(cmd,depth=1)
126 sys.stdout.write(commands.getoutput(cmd))
126 sys.stdout.write(commands.getoutput(cmd))
127 sys.stdout.flush()
127 sys.stdout.flush()
128
128
129 # Store certain global objects that IPython modifies
129 # Store certain global objects that IPython modifies
130 _displayhook = sys.displayhook
130 _displayhook = sys.displayhook
131 _excepthook = sys.excepthook
131 _excepthook = sys.excepthook
132 _main = sys.modules.get('__main__')
132 _main = sys.modules.get('__main__')
133
133
134 # Create custom argv and namespaces for our IPython to be test-friendly
134 # Create custom argv and namespaces for our IPython to be test-friendly
135 config = tools.default_config()
135 config = tools.default_config()
136
136
137 # Create and initialize our test-friendly IPython instance.
137 # Create and initialize our test-friendly IPython instance.
138 shell = interactiveshell.TerminalInteractiveShell.instance(
138 shell = interactiveshell.TerminalInteractiveShell.instance(
139 config=config,
139 config=config,
140 user_ns=ipnsdict(), user_global_ns={}
140 user_ns=ipnsdict(), user_global_ns={}
141 )
141 )
142
142
143 # A few more tweaks needed for playing nicely with doctests...
143 # A few more tweaks needed for playing nicely with doctests...
144
144
145 # These traps are normally only active for interactive use, set them
145 # These traps are normally only active for interactive use, set them
146 # permanently since we'll be mocking interactive sessions.
146 # permanently since we'll be mocking interactive sessions.
147 shell.builtin_trap.set()
147 shell.builtin_trap.activate()
148
148
149 # Modify the IPython system call with one that uses getoutput, so that we
149 # Modify the IPython system call with one that uses getoutput, so that we
150 # can capture subcommands and print them to Python's stdout, otherwise the
150 # can capture subcommands and print them to Python's stdout, otherwise the
151 # doctest machinery would miss them.
151 # doctest machinery would miss them.
152 shell.system = xsys
152 shell.system = xsys
153
153
154 # IPython is ready, now clean up some global state...
154 # IPython is ready, now clean up some global state...
155
155
156 # Deactivate the various python system hooks added by ipython for
156 # Deactivate the various python system hooks added by ipython for
157 # interactive convenience so we don't confuse the doctest system
157 # interactive convenience so we don't confuse the doctest system
158 sys.modules['__main__'] = _main
158 sys.modules['__main__'] = _main
159 sys.displayhook = _displayhook
159 sys.displayhook = _displayhook
160 sys.excepthook = _excepthook
160 sys.excepthook = _excepthook
161
161
162 # So that ipython magics and aliases can be doctested (they work by making
162 # So that ipython magics and aliases can be doctested (they work by making
163 # a call into a global _ip object). Also make the top-level get_ipython
163 # a call into a global _ip object). Also make the top-level get_ipython
164 # now return this without recursively calling here again.
164 # now return this without recursively calling here again.
165 _ip = shell
165 _ip = shell
166 get_ipython = _ip.get_ipython
166 get_ipython = _ip.get_ipython
167 __builtin__._ip = _ip
167 __builtin__._ip = _ip
168 __builtin__.get_ipython = get_ipython
168 __builtin__.get_ipython = get_ipython
169
169
170 return _ip
170 return _ip
General Comments 0
You need to be logged in to leave comments. Login now