##// END OF EJS Templates
Fix small bug in test suite for calling ipython's getoutput.x
Fernando Perez -
Show More
@@ -1,182 +1,182 b''
1 1 """Global IPython app to support test running.
2 2
3 3 We must start our own ipython object and heavily muck with it so that all the
4 4 modifications IPython makes to system behavior don't send the doctest machinery
5 5 into a fit. This code should be considered a gross hack, but it gets the job
6 6 done.
7 7 """
8 8 from __future__ import absolute_import
9 9 from __future__ import print_function
10 10
11 11 #-----------------------------------------------------------------------------
12 12 # Copyright (C) 2009-2010 The IPython Development Team
13 13 #
14 14 # Distributed under the terms of the BSD License. The full license is in
15 15 # the file COPYING, distributed as part of this software.
16 16 #-----------------------------------------------------------------------------
17 17
18 18 #-----------------------------------------------------------------------------
19 19 # Imports
20 20 #-----------------------------------------------------------------------------
21 21
22 22 # stdlib
23 23 import __builtin__
24 24 import os
25 25 import sys
26 26 from types import MethodType
27 27
28 28 # our own
29 29 from . import tools
30 30
31 31 from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell
32 32
33 33 #-----------------------------------------------------------------------------
34 34 # Functions
35 35 #-----------------------------------------------------------------------------
36 36
37 37 # Hack to modify the %run command so we can sync the user's namespace with the
38 38 # test globals. Once we move over to a clean magic system, this will be done
39 39 # with much less ugliness.
40 40
41 41 class py_file_finder(object):
42 42 def __init__(self,test_filename):
43 43 self.test_filename = test_filename
44 44
45 45 def __call__(self,name):
46 46 from IPython.utils.path import get_py_filename
47 47 try:
48 48 return get_py_filename(name)
49 49 except IOError:
50 50 test_dir = os.path.dirname(self.test_filename)
51 51 new_path = os.path.join(test_dir,name)
52 52 return get_py_filename(new_path)
53 53
54 54
55 55 def _run_ns_sync(self,arg_s,runner=None):
56 56 """Modified version of %run that syncs testing namespaces.
57 57
58 58 This is strictly needed for running doctests that call %run.
59 59 """
60 60 #print('in run_ns_sync', arg_s, file=sys.stderr) # dbg
61 61 finder = py_file_finder(arg_s)
62 62 return get_ipython().magic_run_ori(arg_s, runner, finder)
63 63
64 64
65 65 class ipnsdict(dict):
66 66 """A special subclass of dict for use as an IPython namespace in doctests.
67 67
68 68 This subclass adds a simple checkpointing capability so that when testing
69 69 machinery clears it (we use it as the test execution context), it doesn't
70 70 get completely destroyed.
71 71 """
72 72
73 73 def __init__(self,*a):
74 74 dict.__init__(self,*a)
75 75 self._savedict = {}
76 76
77 77 def clear(self):
78 78 dict.clear(self)
79 79 self.update(self._savedict)
80 80
81 81 def _checkpoint(self):
82 82 self._savedict.clear()
83 83 self._savedict.update(self)
84 84
85 85 def update(self,other):
86 86 self._checkpoint()
87 87 dict.update(self,other)
88 88
89 89 # If '_' is in the namespace, python won't set it when executing code,
90 90 # and we have examples that test it. So we ensure that the namespace
91 91 # is always 'clean' of it before it's used for test code execution.
92 92 self.pop('_',None)
93 93
94 94 # The builtins namespace must *always* be the real __builtin__ module,
95 95 # else weird stuff happens. The main ipython code does have provisions
96 96 # to ensure this after %run, but since in this class we do some
97 97 # aggressive low-level cleaning of the execution namespace, we need to
98 98 # correct for that ourselves, to ensure consitency with the 'real'
99 99 # ipython.
100 100 self['__builtins__'] = __builtin__
101 101
102 102
103 103 def get_ipython():
104 104 # This will get replaced by the real thing once we start IPython below
105 105 return start_ipython()
106 106
107 107
108 108 # A couple of methods to override those in the running IPython to interact
109 109 # better with doctest (doctest captures on raw stdout, so we need to direct
110 110 # various types of output there otherwise it will miss them).
111 111
112 112 def xsys(self, cmd):
113 113 """Replace the default system call with a capturing one for doctest.
114 114 """
115 115 # We use getoutput, but we need to strip it because pexpect captures
116 116 # the trailing newline differently from commands.getoutput
117 print(self.getoutput(cmd).rstrip(), end='', file=sys.stdout)
117 print(self.getoutput(cmd, split=False).rstrip(), end='', file=sys.stdout)
118 118 sys.stdout.flush()
119 119
120 120
121 121 def _showtraceback(self, etype, evalue, stb):
122 122 """Print the traceback purely on stdout for doctest to capture it.
123 123 """
124 124 print(self.InteractiveTB.stb2text(stb), file=sys.stdout)
125 125
126 126
127 127 def start_ipython():
128 128 """Start a global IPython shell, which we need for IPython-specific syntax.
129 129 """
130 130 global get_ipython
131 131
132 132 # This function should only ever run once!
133 133 if hasattr(start_ipython, 'already_called'):
134 134 return
135 135 start_ipython.already_called = True
136 136
137 137 # Store certain global objects that IPython modifies
138 138 _displayhook = sys.displayhook
139 139 _excepthook = sys.excepthook
140 140 _main = sys.modules.get('__main__')
141 141
142 142 # Create custom argv and namespaces for our IPython to be test-friendly
143 143 config = tools.default_config()
144 144
145 145 # Create and initialize our test-friendly IPython instance.
146 146 shell = TerminalInteractiveShell.instance(config=config,
147 147 user_ns=ipnsdict(),
148 148 user_global_ns={}
149 149 )
150 150
151 151 # A few more tweaks needed for playing nicely with doctests...
152 152
153 153 # These traps are normally only active for interactive use, set them
154 154 # permanently since we'll be mocking interactive sessions.
155 155 shell.builtin_trap.activate()
156 156
157 157 # Modify the IPython system call with one that uses getoutput, so that we
158 158 # can capture subcommands and print them to Python's stdout, otherwise the
159 159 # doctest machinery would miss them.
160 160 shell.system = MethodType(xsys, shell, TerminalInteractiveShell)
161 161
162 162
163 163 shell._showtraceback = MethodType(_showtraceback, shell,
164 164 TerminalInteractiveShell)
165 165
166 166 # IPython is ready, now clean up some global state...
167 167
168 168 # Deactivate the various python system hooks added by ipython for
169 169 # interactive convenience so we don't confuse the doctest system
170 170 sys.modules['__main__'] = _main
171 171 sys.displayhook = _displayhook
172 172 sys.excepthook = _excepthook
173 173
174 174 # So that ipython magics and aliases can be doctested (they work by making
175 175 # a call into a global _ip object). Also make the top-level get_ipython
176 176 # now return this without recursively calling here again.
177 177 _ip = shell
178 178 get_ipython = _ip.get_ipython
179 179 __builtin__._ip = _ip
180 180 __builtin__.get_ipython = get_ipython
181 181
182 182 return _ip
General Comments 0
You need to be logged in to leave comments. Login now