##// END OF EJS Templates
Clean up and document better starting process of global IPython in tests.
Fernando Perez -
Show More
@@ -1,160 +1,168 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 # Module imports
12 # Module imports
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 # From the standard library
15 # From the standard library
16 import __builtin__
16 import __builtin__
17 import commands
17 import commands
18 import new
18 import new
19 import os
19 import os
20 import sys
20 import sys
21
21
22 from . import tools
22 from . import tools
23 from IPython.utils.genutils import Term
23 from IPython.utils.genutils import Term
24
24
25 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
26 # Functions
26 # Functions
27 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
28
28
29 # Hack to modify the %run command so we can sync the user's namespace with the
29 # Hack to modify the %run command so we can sync the user's namespace with the
30 # test globals. Once we move over to a clean magic system, this will be done
30 # test globals. Once we move over to a clean magic system, this will be done
31 # with much less ugliness.
31 # with much less ugliness.
32
32
33 class py_file_finder(object):
33 class py_file_finder(object):
34 def __init__(self,test_filename):
34 def __init__(self,test_filename):
35 self.test_filename = test_filename
35 self.test_filename = test_filename
36
36
37 def __call__(self,name):
37 def __call__(self,name):
38 from IPython.utils.genutils import get_py_filename
38 from IPython.utils.genutils import get_py_filename
39 try:
39 try:
40 return get_py_filename(name)
40 return get_py_filename(name)
41 except IOError:
41 except IOError:
42 test_dir = os.path.dirname(self.test_filename)
42 test_dir = os.path.dirname(self.test_filename)
43 new_path = os.path.join(test_dir,name)
43 new_path = os.path.join(test_dir,name)
44 return get_py_filename(new_path)
44 return get_py_filename(new_path)
45
45
46
46
47 def _run_ns_sync(self,arg_s,runner=None):
47 def _run_ns_sync(self,arg_s,runner=None):
48 """Modified version of %run that syncs testing namespaces.
48 """Modified version of %run that syncs testing namespaces.
49
49
50 This is strictly needed for running doctests that call %run.
50 This is strictly needed for running doctests that call %run.
51 """
51 """
52 #print >> sys.stderr, 'in run_ns_sync', arg_s # dbg
52 #print >> sys.stderr, 'in run_ns_sync', arg_s # dbg
53
53
54 _ip = get_ipython()
54 _ip = get_ipython()
55 finder = py_file_finder(arg_s)
55 finder = py_file_finder(arg_s)
56 out = _ip.magic_run_ori(arg_s,runner,finder)
56 out = _ip.magic_run_ori(arg_s,runner,finder)
57 return out
57 return out
58
58
59
59
60 class ipnsdict(dict):
60 class ipnsdict(dict):
61 """A special subclass of dict for use as an IPython namespace in doctests.
61 """A special subclass of dict for use as an IPython namespace in doctests.
62
62
63 This subclass adds a simple checkpointing capability so that when testing
63 This subclass adds a simple checkpointing capability so that when testing
64 machinery clears it (we use it as the test execution context), it doesn't
64 machinery clears it (we use it as the test execution context), it doesn't
65 get completely destroyed.
65 get completely destroyed.
66 """
66 """
67
67
68 def __init__(self,*a):
68 def __init__(self,*a):
69 dict.__init__(self,*a)
69 dict.__init__(self,*a)
70 self._savedict = {}
70 self._savedict = {}
71
71
72 def clear(self):
72 def clear(self):
73 dict.clear(self)
73 dict.clear(self)
74 self.update(self._savedict)
74 self.update(self._savedict)
75
75
76 def _checkpoint(self):
76 def _checkpoint(self):
77 self._savedict.clear()
77 self._savedict.clear()
78 self._savedict.update(self)
78 self._savedict.update(self)
79
79
80 def update(self,other):
80 def update(self,other):
81 self._checkpoint()
81 self._checkpoint()
82 dict.update(self,other)
82 dict.update(self,other)
83
83
84 # If '_' is in the namespace, python won't set it when executing code,
84 # If '_' is in the namespace, python won't set it when executing code,
85 # and we have examples that test it. So we ensure that the namespace
85 # and we have examples that test it. So we ensure that the namespace
86 # is always 'clean' of it before it's used for test code execution.
86 # is always 'clean' of it before it's used for test code execution.
87 self.pop('_',None)
87 self.pop('_',None)
88
88
89 # The builtins namespace must *always* be the real __builtin__ module,
89 # The builtins namespace must *always* be the real __builtin__ module,
90 # else weird stuff happens. The main ipython code does have provisions
90 # else weird stuff happens. The main ipython code does have provisions
91 # to ensure this after %run, but since in this class we do some
91 # to ensure this after %run, but since in this class we do some
92 # aggressive low-level cleaning of the execution namespace, we need to
92 # aggressive low-level cleaning of the execution namespace, we need to
93 # correct for that ourselves, to ensure consitency with the 'real'
93 # correct for that ourselves, to ensure consitency with the 'real'
94 # ipython.
94 # ipython.
95 self['__builtins__'] = __builtin__
95 self['__builtins__'] = __builtin__
96
96
97
97
98 def get_ipython():
98 def get_ipython():
99 # This will get replaced by the real thing once we start IPython below
99 # This will get replaced by the real thing once we start IPython below
100 return start_ipython()
100 return start_ipython()
101
101
102 def start_ipython():
102 def start_ipython():
103 """Start a global IPython shell, which we need for IPython-specific syntax.
103 """Start a global IPython shell, which we need for IPython-specific syntax.
104 """
104 """
105 global get_ipython
105 global get_ipython
106
106
107 # This function should only ever run once!
107 # This function should only ever run once!
108 if hasattr(start_ipython,'already_called'):
108 if hasattr(start_ipython,'already_called'):
109 return
109 return
110 start_ipython.already_called = True
110 start_ipython.already_called = True
111
111
112 # Ok, first time we're called, go ahead
112 # Ok, first time we're called, go ahead
113 from IPython.core import ipapp, iplib
113 from IPython.core import ipapp, iplib
114
114
115 def xsys(cmd):
115 def xsys(cmd):
116 """Execute a command and print its output.
116 """Execute a command and print its output.
117
117
118 This is just a convenience function to replace the IPython system call
118 This is just a convenience function to replace the IPython system call
119 with one that is more doctest-friendly.
119 with one that is more doctest-friendly.
120 """
120 """
121 cmd = _ip.var_expand(cmd,depth=1)
121 cmd = _ip.var_expand(cmd,depth=1)
122 sys.stdout.write(commands.getoutput(cmd))
122 sys.stdout.write(commands.getoutput(cmd))
123 sys.stdout.flush()
123 sys.stdout.flush()
124
124
125 # Store certain global objects that IPython modifies
125 # Store certain global objects that IPython modifies
126 _displayhook = sys.displayhook
126 _displayhook = sys.displayhook
127 _excepthook = sys.excepthook
127 _excepthook = sys.excepthook
128 _main = sys.modules.get('__main__')
128 _main = sys.modules.get('__main__')
129
129
130 # Create custom argv and namespaces for our IPython to be test-friendly
130 argv = tools.default_argv()
131 argv = tools.default_argv()
132 user_ns, global_ns = iplib.make_user_namespaces(ipnsdict(), {})
131
133
132 # Start IPython instance. We customize it to start with minimal frills.
134 # Create and initialize our test-friendly IPython instance.
133 user_ns,global_ns = iplib.make_user_namespaces(ipnsdict(),{})
134 ip = ipapp.IPythonApp(argv, user_ns=user_ns, user_global_ns=global_ns)
135 ip = ipapp.IPythonApp(argv, user_ns=user_ns, user_global_ns=global_ns)
135 ip.initialize()
136 ip.initialize()
137
138 # A few more tweaks needed for playing nicely with doctests...
139
140 # These traps are normally only active for interactive use, set them
141 # permanently since we'll be mocking interactive sessions.
136 ip.shell.builtin_trap.set()
142 ip.shell.builtin_trap.set()
137
143
138 # Set error printing to stdout so nose can doctest exceptions
144 # Set error printing to stdout so nose can doctest exceptions
139 ip.shell.InteractiveTB.out_stream = 'stdout'
145 ip.shell.InteractiveTB.out_stream = 'stdout'
140
146
147 # Modify the IPython system call with one that uses getoutput, so that we
148 # can capture subcommands and print them to Python's stdout, otherwise the
149 # doctest machinery would miss them.
150 ip.shell.system = xsys
151
152 # IPython is ready, now clean up some global state...
153
141 # Deactivate the various python system hooks added by ipython for
154 # Deactivate the various python system hooks added by ipython for
142 # interactive convenience so we don't confuse the doctest system
155 # interactive convenience so we don't confuse the doctest system
143 sys.modules['__main__'] = _main
156 sys.modules['__main__'] = _main
144 sys.displayhook = _displayhook
157 sys.displayhook = _displayhook
145 sys.excepthook = _excepthook
158 sys.excepthook = _excepthook
146
159
147 # So that ipython magics and aliases can be doctested (they work by making
160 # So that ipython magics and aliases can be doctested (they work by making
148 # a call into a global _ip object). Also make the top-level get_ipython
161 # a call into a global _ip object). Also make the top-level get_ipython
149 # now return this without calling here again
162 # now return this without recursively calling here again.
150 _ip = ip.shell
163 _ip = ip.shell
151 get_ipython = _ip.get_ipython
164 get_ipython = _ip.get_ipython
152 __builtin__._ip = _ip
165 __builtin__._ip = _ip
153 __builtin__.get_ipython = get_ipython
166 __builtin__.get_ipython = get_ipython
154
167
155 # Modify the IPython system call with one that uses getoutput, so that we
156 # can capture subcommands and print them to Python's stdout, otherwise the
157 # doctest machinery would miss them.
158 ip.shell.system = xsys
159
160 return _ip
168 return _ip
General Comments 0
You need to be logged in to leave comments. Login now