##// END OF EJS Templates
move IPython.inprocess to IPython.kernel.inprocess
MinRK -
Show More
@@ -1,85 +1,85 b''
1 1 # encoding: utf-8
2 2 """
3 3 IPython: tools for interactive and parallel computing in Python.
4 4
5 5 http://ipython.org
6 6 """
7 7 #-----------------------------------------------------------------------------
8 8 # Copyright (c) 2008-2011, IPython Development Team.
9 9 # Copyright (c) 2001-2007, Fernando Perez <fernando.perez@colorado.edu>
10 10 # Copyright (c) 2001, Janko Hauser <jhauser@zscout.de>
11 11 # Copyright (c) 2001, Nathaniel Gray <n8gray@caltech.edu>
12 12 #
13 13 # Distributed under the terms of the Modified BSD License.
14 14 #
15 15 # The full license is in the file COPYING.txt, distributed with this software.
16 16 #-----------------------------------------------------------------------------
17 17
18 18 #-----------------------------------------------------------------------------
19 19 # Imports
20 20 #-----------------------------------------------------------------------------
21 21 from __future__ import absolute_import
22 22
23 23 import os
24 24 import sys
25 25
26 26 #-----------------------------------------------------------------------------
27 27 # Setup everything
28 28 #-----------------------------------------------------------------------------
29 29
30 30 # Don't forget to also update setup.py when this changes!
31 31 if sys.version[0:3] < '2.6':
32 32 raise ImportError('Python Version 2.6 or above is required for IPython.')
33 33
34 34 # Make it easy to import extensions - they are always directly on pythonpath.
35 35 # Therefore, non-IPython modules can be added to extensions directory.
36 36 # This should probably be in ipapp.py.
37 37 sys.path.append(os.path.join(os.path.dirname(__file__), "extensions"))
38 38
39 39 #-----------------------------------------------------------------------------
40 40 # Setup the top level names
41 41 #-----------------------------------------------------------------------------
42 42
43 43 from .config.loader import Config
44 44 from .core import release
45 45 from .core.application import Application
46 46 from .frontend.terminal.embed import embed
47 47
48 48 from .core.error import TryNext
49 49 from .core.interactiveshell import InteractiveShell
50 50 from .testing import test
51 51 from .utils.sysinfo import sys_info
52 52 from .utils.frame import extract_module_locals
53 53
54 54 # Release data
55 55 __author__ = '%s <%s>' % (release.author, release.author_email)
56 56 __license__ = release.license
57 57 __version__ = release.version
58 58 version_info = release.version_info
59 59
60 60 def embed_kernel(module=None, local_ns=None, **kwargs):
61 61 """Embed and start an IPython kernel in a given scope.
62 62
63 63 Parameters
64 64 ----------
65 65 module : ModuleType, optional
66 66 The module to load into IPython globals (default: caller)
67 67 local_ns : dict, optional
68 68 The namespace to load into IPython user namespace (default: caller)
69 69
70 70 kwargs : various, optional
71 71 Further keyword args are relayed to the KernelApp constructor,
72 72 allowing configuration of the Kernel. Will only have an effect
73 73 on the first embed_kernel call for a given process.
74 74
75 75 """
76 76
77 77 (caller_module, caller_locals) = extract_module_locals(1)
78 78 if module is None:
79 79 module = caller_module
80 80 if local_ns is None:
81 81 local_ns = caller_locals
82 82
83 83 # Only import .zmq when we really need it
84 from .zmq.embed import embed_kernel as real_embed_kernel
84 from IPython.kernel.zmq.embed import embed_kernel as real_embed_kernel
85 85 real_embed_kernel(module=module, local_ns=local_ns, **kwargs)
@@ -1,33 +1,33 b''
1 1 """ Defines an in-process KernelManager with signals and slots.
2 2 """
3 3
4 4 # Local imports.
5 from IPython.inprocess.kernelmanager import \
5 from IPython.kernel.inprocess.kernelmanager import \
6 6 InProcessShellChannel, InProcessIOPubChannel, InProcessStdInChannel, \
7 7 InProcessHBChannel, InProcessKernelManager
8 8 from IPython.utils.traitlets import Type
9 9 from base_kernelmanager import QtShellChannelMixin, QtIOPubChannelMixin, \
10 10 QtStdInChannelMixin, QtHBChannelMixin, QtKernelManagerMixin
11 11
12 12
13 13 class QtInProcessShellChannel(QtShellChannelMixin, InProcessShellChannel):
14 14 pass
15 15
16 16 class QtInProcessIOPubChannel(QtIOPubChannelMixin, InProcessIOPubChannel):
17 17 pass
18 18
19 19 class QtInProcessStdInChannel(QtStdInChannelMixin, InProcessStdInChannel):
20 20 pass
21 21
22 22 class QtInProcessHBChannel(QtHBChannelMixin, InProcessHBChannel):
23 23 pass
24 24
25 25
26 26 class QtInProcessKernelManager(QtKernelManagerMixin, InProcessKernelManager):
27 27 """ An in-process KernelManager with signals and slots.
28 28 """
29 29
30 30 iopub_channel_class = Type(QtInProcessIOPubChannel)
31 31 shell_channel_class = Type(QtInProcessShellChannel)
32 32 stdin_channel_class = Type(QtInProcessStdInChannel)
33 33 hb_channel_class = Type(QtInProcessHBChannel)
1 NO CONTENT: file renamed from IPython/inprocess/__init__.py to IPython/kernel/inprocess/__init__.py
1 NO CONTENT: file renamed from IPython/inprocess/blockingkernelmanager.py to IPython/kernel/inprocess/blockingkernelmanager.py
@@ -1,176 +1,177 b''
1 """ An in-process kernel. """
1 """An in-process kernel"""
2 2
3 3 #-----------------------------------------------------------------------------
4 4 # Copyright (C) 2012 The IPython Development Team
5 5 #
6 6 # Distributed under the terms of the BSD License. The full license is in
7 7 # the file COPYING, distributed as part of this software.
8 8 #-----------------------------------------------------------------------------
9 9
10 10 #-----------------------------------------------------------------------------
11 11 # Imports
12 12 #-----------------------------------------------------------------------------
13 13
14 14 # Standard library imports
15 15 from contextlib import contextmanager
16 16 import logging
17 17 import sys
18 18
19 # Local imports.
19 # Local imports
20 20 from IPython.core.interactiveshell import InteractiveShellABC
21 from IPython.inprocess.socket import DummySocket
22 21 from IPython.utils.jsonutil import json_clean
23 22 from IPython.utils.traitlets import Any, Enum, Instance, List, Type
24 23 from IPython.kernel.zmq.ipkernel import Kernel
25 24 from IPython.kernel.zmq.zmqshell import ZMQInteractiveShell
26 25
26 from .socket import DummySocket
27
27 28 #-----------------------------------------------------------------------------
28 29 # Main kernel class
29 30 #-----------------------------------------------------------------------------
30 31
31 32 class InProcessKernel(Kernel):
32 33
33 34 #-------------------------------------------------------------------------
34 35 # InProcessKernel interface
35 36 #-------------------------------------------------------------------------
36 37
37 38 # The frontends connected to this kernel.
38 39 frontends = List(
39 Instance('IPython.inprocess.kernelmanager.InProcessKernelManager'))
40 Instance('IPython.kernel.inprocess.kernelmanager.InProcessKernelManager'))
40 41
41 42 # The GUI environment that the kernel is running under. This need not be
42 43 # specified for the normal operation for the kernel, but is required for
43 44 # IPython's GUI support (including pylab). The default is 'inline' because
44 45 # it is safe under all GUI toolkits.
45 46 gui = Enum(('tk', 'gtk', 'wx', 'qt', 'qt4', 'inline'),
46 47 default_value='inline')
47 48
48 49 raw_input_str = Any()
49 50 stdout = Any()
50 51 stderr = Any()
51 52
52 53 #-------------------------------------------------------------------------
53 54 # Kernel interface
54 55 #-------------------------------------------------------------------------
55 56
56 57 shell_class = Type()
57 58 shell_streams = List()
58 59 control_stream = Any()
59 60 iopub_socket = Instance(DummySocket, ())
60 61 stdin_socket = Instance(DummySocket, ())
61 62
62 63 def __init__(self, **traits):
63 64 # When an InteractiveShell is instantiated by our base class, it binds
64 65 # the current values of sys.stdout and sys.stderr.
65 66 with self._redirected_io():
66 67 super(InProcessKernel, self).__init__(**traits)
67 68
68 69 self.iopub_socket.on_trait_change(self._io_dispatch, 'message_sent')
69 70 self.shell.kernel = self
70 71
71 72 def execute_request(self, stream, ident, parent):
72 73 """ Override for temporary IO redirection. """
73 74 with self._redirected_io():
74 75 super(InProcessKernel, self).execute_request(stream, ident, parent)
75 76
76 77 def start(self):
77 78 """ Override registration of dispatchers for streams. """
78 79 self.shell.exit_now = False
79 80
80 81 def _abort_queue(self, stream):
81 82 """ The in-process kernel doesn't abort requests. """
82 83 pass
83 84
84 85 def _raw_input(self, prompt, ident, parent):
85 86 # Flush output before making the request.
86 87 self.raw_input_str = None
87 88 sys.stderr.flush()
88 89 sys.stdout.flush()
89 90
90 91 # Send the input request.
91 92 content = json_clean(dict(prompt=prompt))
92 93 msg = self.session.msg(u'input_request', content, parent)
93 94 for frontend in self.frontends:
94 95 if frontend.session.session == parent['header']['session']:
95 96 frontend.stdin_channel.call_handlers(msg)
96 97 break
97 98 else:
98 99 logging.error('No frontend found for raw_input request')
99 100 return str()
100 101
101 102 # Await a response.
102 103 while self.raw_input_str is None:
103 104 frontend.stdin_channel.process_events()
104 105 return self.raw_input_str
105 106
106 107 #-------------------------------------------------------------------------
107 108 # Protected interface
108 109 #-------------------------------------------------------------------------
109 110
110 111 @contextmanager
111 112 def _redirected_io(self):
112 113 """ Temporarily redirect IO to the kernel.
113 114 """
114 115 sys_stdout, sys_stderr = sys.stdout, sys.stderr
115 116 sys.stdout, sys.stderr = self.stdout, self.stderr
116 117 yield
117 118 sys.stdout, sys.stderr = sys_stdout, sys_stderr
118 119
119 120 #------ Trait change handlers --------------------------------------------
120 121
121 122 def _io_dispatch(self):
122 123 """ Called when a message is sent to the IO socket.
123 124 """
124 125 ident, msg = self.session.recv(self.iopub_socket, copy=False)
125 126 for frontend in self.frontends:
126 127 frontend.iopub_channel.call_handlers(msg)
127 128
128 129 #------ Trait initializers -----------------------------------------------
129 130
130 131 def _log_default(self):
131 132 return logging.getLogger(__name__)
132 133
133 134 def _session_default(self):
134 135 from IPython.kernel.zmq.session import Session
135 136 return Session(config=self.config)
136 137
137 138 def _shell_class_default(self):
138 139 return InProcessInteractiveShell
139 140
140 141 def _stdout_default(self):
141 142 from IPython.kernel.zmq.iostream import OutStream
142 143 return OutStream(self.session, self.iopub_socket, u'stdout')
143 144
144 145 def _stderr_default(self):
145 146 from IPython.kernel.zmq.iostream import OutStream
146 147 return OutStream(self.session, self.iopub_socket, u'stderr')
147 148
148 149 #-----------------------------------------------------------------------------
149 150 # Interactive shell subclass
150 151 #-----------------------------------------------------------------------------
151 152
152 153 class InProcessInteractiveShell(ZMQInteractiveShell):
153 154
154 kernel = Instance('IPython.inprocess.ipkernel.InProcessKernel')
155 kernel = Instance('IPython.kernel.inprocess.ipkernel.InProcessKernel')
155 156
156 157 #-------------------------------------------------------------------------
157 158 # InteractiveShell interface
158 159 #-------------------------------------------------------------------------
159 160
160 161 def enable_gui(self, gui=None):
161 162 """ Enable GUI integration for the kernel.
162 163 """
163 164 from IPython.kernel.zmq.eventloops import enable_gui
164 165 if not gui:
165 166 gui = self.kernel.gui
166 167 enable_gui(gui, kernel=self.kernel)
167 168
168 169 def enable_pylab(self, gui=None, import_all=True, welcome_message=False):
169 170 """ Activate pylab support at runtime.
170 171 """
171 172 if not gui:
172 173 gui = self.kernel.gui
173 174 super(InProcessInteractiveShell, self).enable_pylab(gui, import_all,
174 175 welcome_message)
175 176
176 177 InteractiveShellABC.register(InProcessInteractiveShell)
@@ -1,313 +1,314 b''
1 1 """ A kernel manager for in-process kernels. """
2 2
3 3 #-----------------------------------------------------------------------------
4 4 # Copyright (C) 2012 The IPython Development Team
5 5 #
6 6 # Distributed under the terms of the BSD License. The full license is in
7 7 # the file COPYING, distributed as part of this software.
8 8 #-----------------------------------------------------------------------------
9 9
10 10 #-----------------------------------------------------------------------------
11 11 # Imports
12 12 #-----------------------------------------------------------------------------
13 13
14 14 # Local imports.
15 15 from IPython.config.configurable import Configurable
16 from IPython.inprocess.socket import DummySocket
17 16 from IPython.utils.traitlets import Any, Instance, Type
18 17 from IPython.kernel import (
19 18 ShellChannelABC, IOPubChannelABC,
20 19 HBChannelABC, StdInChannelABC,
21 20 KernelManagerABC
22 21 )
23 22
23 from .socket import DummySocket
24
24 25 #-----------------------------------------------------------------------------
25 26 # Channel classes
26 27 #-----------------------------------------------------------------------------
27 28
28 29 class InProcessChannel(object):
29 30 """Base class for in-process channels."""
30 31
31 32 def __init__(self, manager):
32 33 super(InProcessChannel, self).__init__()
33 34 self.manager = manager
34 35 self._is_alive = False
35 36
36 37 #--------------------------------------------------------------------------
37 38 # Channel interface
38 39 #--------------------------------------------------------------------------
39 40
40 41 def is_alive(self):
41 42 return self._is_alive
42 43
43 44 def start(self):
44 45 self._is_alive = True
45 46
46 47 def stop(self):
47 48 self._is_alive = False
48 49
49 50 def call_handlers(self, msg):
50 51 """ This method is called in the main thread when a message arrives.
51 52
52 53 Subclasses should override this method to handle incoming messages.
53 54 """
54 55 raise NotImplementedError('call_handlers must be defined in a subclass.')
55 56
56 57 #--------------------------------------------------------------------------
57 58 # InProcessChannel interface
58 59 #--------------------------------------------------------------------------
59 60
60 61 def call_handlers_later(self, *args, **kwds):
61 62 """ Call the message handlers later.
62 63
63 64 The default implementation just calls the handlers immediately, but this
64 65 method exists so that GUI toolkits can defer calling the handlers until
65 66 after the event loop has run, as expected by GUI frontends.
66 67 """
67 68 self.call_handlers(*args, **kwds)
68 69
69 70 def process_events(self):
70 71 """ Process any pending GUI events.
71 72
72 73 This method will be never be called from a frontend without an event
73 74 loop (e.g., a terminal frontend).
74 75 """
75 76 raise NotImplementedError
76 77
77 78
78 79 class InProcessShellChannel(InProcessChannel):
79 80 """See `IPython.kernel.kernelmanager.ShellChannel` for docstrings."""
80 81
81 82 # flag for whether execute requests should be allowed to call raw_input
82 83 allow_stdin = True
83 84
84 85 #--------------------------------------------------------------------------
85 86 # ShellChannel interface
86 87 #--------------------------------------------------------------------------
87 88
88 89 def execute(self, code, silent=False, store_history=True,
89 90 user_variables=[], user_expressions={}, allow_stdin=None):
90 91 if allow_stdin is None:
91 92 allow_stdin = self.allow_stdin
92 93 content = dict(code=code, silent=silent, store_history=store_history,
93 94 user_variables=user_variables,
94 95 user_expressions=user_expressions,
95 96 allow_stdin=allow_stdin)
96 97 msg = self.manager.session.msg('execute_request', content)
97 98 self._dispatch_to_kernel(msg)
98 99 return msg['header']['msg_id']
99 100
100 101 def complete(self, text, line, cursor_pos, block=None):
101 102 content = dict(text=text, line=line, block=block, cursor_pos=cursor_pos)
102 103 msg = self.manager.session.msg('complete_request', content)
103 104 self._dispatch_to_kernel(msg)
104 105 return msg['header']['msg_id']
105 106
106 107 def object_info(self, oname, detail_level=0):
107 108 content = dict(oname=oname, detail_level=detail_level)
108 109 msg = self.manager.session.msg('object_info_request', content)
109 110 self._dispatch_to_kernel(msg)
110 111 return msg['header']['msg_id']
111 112
112 113 def history(self, raw=True, output=False, hist_access_type='range', **kwds):
113 114 content = dict(raw=raw, output=output,
114 115 hist_access_type=hist_access_type, **kwds)
115 116 msg = self.manager.session.msg('history_request', content)
116 117 self._dispatch_to_kernel(msg)
117 118 return msg['header']['msg_id']
118 119
119 120 def shutdown(self, restart=False):
120 121 # FIXME: What to do here?
121 122 raise NotImplementedError('Cannot shutdown in-process kernel')
122 123
123 124 #--------------------------------------------------------------------------
124 125 # Protected interface
125 126 #--------------------------------------------------------------------------
126 127
127 128 def _dispatch_to_kernel(self, msg):
128 129 """ Send a message to the kernel and handle a reply.
129 130 """
130 131 kernel = self.manager.kernel
131 132 if kernel is None:
132 133 raise RuntimeError('Cannot send request. No kernel exists.')
133 134
134 135 stream = DummySocket()
135 136 self.manager.session.send(stream, msg)
136 137 msg_parts = stream.recv_multipart()
137 138 kernel.dispatch_shell(stream, msg_parts)
138 139
139 140 idents, reply_msg = self.manager.session.recv(stream, copy=False)
140 141 self.call_handlers_later(reply_msg)
141 142
142 143
143 144 class InProcessIOPubChannel(InProcessChannel):
144 145 """See `IPython.kernel.kernelmanager.IOPubChannel` for docstrings."""
145 146
146 147 def flush(self, timeout=1.0):
147 148 pass
148 149
149 150
150 151 class InProcessStdInChannel(InProcessChannel):
151 152 """See `IPython.kernel.kernelmanager.StdInChannel` for docstrings."""
152 153
153 154 def input(self, string):
154 155 kernel = self.manager.kernel
155 156 if kernel is None:
156 157 raise RuntimeError('Cannot send input reply. No kernel exists.')
157 158 kernel.raw_input_str = string
158 159
159 160
160 161 class InProcessHBChannel(InProcessChannel):
161 162 """See `IPython.kernel.kernelmanager.HBChannel` for docstrings."""
162 163
163 164 time_to_dead = 3.0
164 165
165 166 def __init__(self, *args, **kwds):
166 167 super(InProcessHBChannel, self).__init__(*args, **kwds)
167 168 self._pause = True
168 169
169 170 def pause(self):
170 171 self._pause = True
171 172
172 173 def unpause(self):
173 174 self._pause = False
174 175
175 176 def is_beating(self):
176 177 return not self._pause
177 178
178 179
179 180 #-----------------------------------------------------------------------------
180 181 # Main kernel manager class
181 182 #-----------------------------------------------------------------------------
182 183
183 184 class InProcessKernelManager(Configurable):
184 185 """A manager for an in-process kernel.
185 186
186 187 This class implements the interface of
187 188 `IPython.kernel.kernelmanagerabc.KernelManagerABC` and allows
188 189 (asynchronous) frontends to be used seamlessly with an in-process kernel.
189 190
190 191 See `IPython.kernel.kernelmanager.KernelManager` for docstrings.
191 192 """
192 193
193 194 # The Session to use for building messages.
194 195 session = Instance('IPython.kernel.zmq.session.Session')
195 196 def _session_default(self):
196 197 from IPython.kernel.zmq.session import Session
197 198 return Session(config=self.config)
198 199
199 200 # The kernel process with which the KernelManager is communicating.
200 kernel = Instance('IPython.inprocess.ipkernel.InProcessKernel')
201 kernel = Instance('IPython.kernel.inprocess.ipkernel.InProcessKernel')
201 202
202 203 # The classes to use for the various channels.
203 204 shell_channel_class = Type(InProcessShellChannel)
204 205 iopub_channel_class = Type(InProcessIOPubChannel)
205 206 stdin_channel_class = Type(InProcessStdInChannel)
206 207 hb_channel_class = Type(InProcessHBChannel)
207 208
208 209 # Protected traits.
209 210 _shell_channel = Any
210 211 _iopub_channel = Any
211 212 _stdin_channel = Any
212 213 _hb_channel = Any
213 214
214 215 #--------------------------------------------------------------------------
215 216 # Channel management methods.
216 217 #--------------------------------------------------------------------------
217 218
218 219 def start_channels(self, shell=True, iopub=True, stdin=True, hb=True):
219 220 if shell:
220 221 self.shell_channel.start()
221 222 if iopub:
222 223 self.iopub_channel.start()
223 224 if stdin:
224 225 self.stdin_channel.start()
225 226 self.shell_channel.allow_stdin = True
226 227 else:
227 228 self.shell_channel.allow_stdin = False
228 229 if hb:
229 230 self.hb_channel.start()
230 231
231 232 def stop_channels(self):
232 233 if self.shell_channel.is_alive():
233 234 self.shell_channel.stop()
234 235 if self.iopub_channel.is_alive():
235 236 self.iopub_channel.stop()
236 237 if self.stdin_channel.is_alive():
237 238 self.stdin_channel.stop()
238 239 if self.hb_channel.is_alive():
239 240 self.hb_channel.stop()
240 241
241 242 @property
242 243 def channels_running(self):
243 244 return (self.shell_channel.is_alive() or self.iopub_channel.is_alive() or
244 245 self.stdin_channel.is_alive() or self.hb_channel.is_alive())
245 246
246 247 @property
247 248 def shell_channel(self):
248 249 if self._shell_channel is None:
249 250 self._shell_channel = self.shell_channel_class(self)
250 251 return self._shell_channel
251 252
252 253 @property
253 254 def iopub_channel(self):
254 255 if self._iopub_channel is None:
255 256 self._iopub_channel = self.iopub_channel_class(self)
256 257 return self._iopub_channel
257 258
258 259 @property
259 260 def stdin_channel(self):
260 261 if self._stdin_channel is None:
261 262 self._stdin_channel = self.stdin_channel_class(self)
262 263 return self._stdin_channel
263 264
264 265 @property
265 266 def hb_channel(self):
266 267 if self._hb_channel is None:
267 268 self._hb_channel = self.hb_channel_class(self)
268 269 return self._hb_channel
269 270
270 271 #--------------------------------------------------------------------------
271 272 # Kernel management methods:
272 273 #--------------------------------------------------------------------------
273 274
274 275 def start_kernel(self, **kwds):
275 from IPython.inprocess.ipkernel import InProcessKernel
276 from IPython.kernel.inprocess.ipkernel import InProcessKernel
276 277 self.kernel = InProcessKernel()
277 278 self.kernel.frontends.append(self)
278 279
279 280 def shutdown_kernel(self):
280 281 self._kill_kernel()
281 282
282 283 def restart_kernel(self, now=False, **kwds):
283 284 self.shutdown_kernel()
284 285 self.start_kernel(**kwds)
285 286
286 287 @property
287 288 def has_kernel(self):
288 289 return self.kernel is not None
289 290
290 291 def _kill_kernel(self):
291 292 self.kernel.frontends.remove(self)
292 293 self.kernel = None
293 294
294 295 def interrupt_kernel(self):
295 296 raise NotImplementedError("Cannot interrupt in-process kernel.")
296 297
297 298 def signal_kernel(self, signum):
298 299 raise NotImplementedError("Cannot signal in-process kernel.")
299 300
300 301 @property
301 302 def is_alive(self):
302 303 return True
303 304
304 305
305 306 #-----------------------------------------------------------------------------
306 307 # ABC Registration
307 308 #-----------------------------------------------------------------------------
308 309
309 310 ShellChannelABC.register(InProcessShellChannel)
310 311 IOPubChannelABC.register(InProcessIOPubChannel)
311 312 HBChannelABC.register(InProcessHBChannel)
312 313 StdInChannelABC.register(InProcessStdInChannel)
313 314 KernelManagerABC.register(InProcessKernelManager)
1 NO CONTENT: file renamed from IPython/inprocess/socket.py to IPython/kernel/inprocess/socket.py
1 NO CONTENT: file renamed from IPython/inprocess/tests/__init__.py to IPython/kernel/inprocess/tests/__init__.py
@@ -1,89 +1,89 b''
1 1 #-------------------------------------------------------------------------------
2 2 # Copyright (C) 2012 The IPython Development Team
3 3 #
4 4 # Distributed under the terms of the BSD License. The full license is in
5 5 # the file COPYING, distributed as part of this software.
6 6 #-------------------------------------------------------------------------------
7 7
8 8 #-----------------------------------------------------------------------------
9 9 # Imports
10 10 #-----------------------------------------------------------------------------
11 11 from __future__ import print_function
12 12
13 13 # Standard library imports
14 14 from StringIO import StringIO
15 15 import sys
16 16 import unittest
17 17
18 18 # Local imports
19 from IPython.inprocess.blockingkernelmanager import \
19 from IPython.kernel.inprocess.blockingkernelmanager import \
20 20 BlockingInProcessKernelManager
21 from IPython.inprocess.ipkernel import InProcessKernel
21 from IPython.kernel.inprocess.ipkernel import InProcessKernel
22 22 from IPython.testing.decorators import skipif_not_matplotlib
23 23 from IPython.utils.io import capture_output
24 24 from IPython.utils import py3compat
25 25
26 26 #-----------------------------------------------------------------------------
27 27 # Test case
28 28 #-----------------------------------------------------------------------------
29 29
30 30 class InProcessKernelTestCase(unittest.TestCase):
31 31
32 32 @skipif_not_matplotlib
33 33 def test_pylab(self):
34 34 """ Does pylab work in the in-process kernel?
35 35 """
36 36 km = BlockingInProcessKernelManager()
37 37 km.start_kernel()
38 38 km.shell_channel.execute('%pylab')
39 39 msg = get_stream_message(km)
40 40 self.assert_('Welcome to pylab' in msg['content']['data'])
41 41
42 42 def test_raw_input(self):
43 43 """ Does the in-process kernel handle raw_input correctly?
44 44 """
45 45 km = BlockingInProcessKernelManager()
46 46 km.start_kernel()
47 47
48 48 io = StringIO('foobar\n')
49 49 sys_stdin = sys.stdin
50 50 sys.stdin = io
51 51 try:
52 52 if py3compat.PY3:
53 53 km.shell_channel.execute('x = input()')
54 54 else:
55 55 km.shell_channel.execute('x = raw_input()')
56 56 finally:
57 57 sys.stdin = sys_stdin
58 58 self.assertEqual(km.kernel.shell.user_ns.get('x'), 'foobar')
59 59
60 60 def test_stdout(self):
61 61 """ Does the in-process kernel correctly capture IO?
62 62 """
63 63 kernel = InProcessKernel()
64 64
65 65 with capture_output() as io:
66 66 kernel.shell.run_cell('print("foo")')
67 67 self.assertEqual(io.stdout, 'foo\n')
68 68
69 69 km = BlockingInProcessKernelManager(kernel=kernel)
70 70 kernel.frontends.append(km)
71 71 km.shell_channel.execute('print("bar")')
72 72 msg = get_stream_message(km)
73 73 self.assertEqual(msg['content']['data'], 'bar\n')
74 74
75 75 #-----------------------------------------------------------------------------
76 76 # Utility functions
77 77 #-----------------------------------------------------------------------------
78 78
79 79 def get_stream_message(kernel_manager, timeout=5):
80 80 """ Gets a single stream message synchronously from the sub channel.
81 81 """
82 82 while True:
83 83 msg = kernel_manager.iopub_channel.get_msg(timeout=timeout)
84 84 if msg['header']['msg_type'] == 'stream':
85 85 return msg
86 86
87 87
88 88 if __name__ == '__main__':
89 89 unittest.main()
@@ -1,102 +1,102 b''
1 1 #-------------------------------------------------------------------------------
2 2 # Copyright (C) 2012 The IPython Development Team
3 3 #
4 4 # Distributed under the terms of the BSD License. The full license is in
5 5 # the file COPYING, distributed as part of this software.
6 6 #-------------------------------------------------------------------------------
7 7
8 8 #-----------------------------------------------------------------------------
9 9 # Imports
10 10 #-----------------------------------------------------------------------------
11 11 from __future__ import print_function
12 12
13 13 # Standard library imports
14 14 import unittest
15 15
16 16 # Local imports
17 from IPython.inprocess.blockingkernelmanager import \
17 from IPython.kernel.inprocess.blockingkernelmanager import \
18 18 BlockingInProcessKernelManager
19 from IPython.inprocess.ipkernel import InProcessKernel
19 from IPython.kernel.inprocess.ipkernel import InProcessKernel
20 20
21 21 #-----------------------------------------------------------------------------
22 22 # Test case
23 23 #-----------------------------------------------------------------------------
24 24
25 25 class InProcessKernelManagerTestCase(unittest.TestCase):
26 26
27 27 def test_inteface(self):
28 28 """ Does the in-process kernel manager implement the basic KM interface?
29 29 """
30 30 km = BlockingInProcessKernelManager()
31 31 self.assert_(not km.channels_running)
32 32 self.assert_(not km.has_kernel)
33 33
34 34 km.start_channels()
35 35 self.assert_(km.channels_running)
36 36
37 37 km.start_kernel()
38 38 self.assert_(km.has_kernel)
39 39 self.assert_(km.kernel is not None)
40 40
41 41 old_kernel = km.kernel
42 42 km.restart_kernel()
43 43 self.assert_(km.kernel is not None)
44 44 self.assertNotEquals(km.kernel, old_kernel)
45 45
46 46 km.shutdown_kernel()
47 47 self.assert_(not km.has_kernel)
48 48
49 49 self.assertRaises(NotImplementedError, km.interrupt_kernel)
50 50 self.assertRaises(NotImplementedError, km.signal_kernel, 9)
51 51
52 52 km.stop_channels()
53 53 self.assert_(not km.channels_running)
54 54
55 55 def test_execute(self):
56 56 """ Does executing code in an in-process kernel work?
57 57 """
58 58 km = BlockingInProcessKernelManager()
59 59 km.start_kernel()
60 60 km.shell_channel.execute('foo = 1')
61 61 self.assertEquals(km.kernel.shell.user_ns['foo'], 1)
62 62
63 63 def test_complete(self):
64 64 """ Does requesting completion from an in-process kernel work?
65 65 """
66 66 km = BlockingInProcessKernelManager()
67 67 km.start_kernel()
68 68 km.kernel.shell.push({'my_bar': 0, 'my_baz': 1})
69 69 km.shell_channel.complete('my_ba', 'my_ba', 5)
70 70 msg = km.shell_channel.get_msg()
71 71 self.assertEquals(msg['header']['msg_type'], 'complete_reply')
72 72 self.assertEquals(sorted(msg['content']['matches']),
73 73 ['my_bar', 'my_baz'])
74 74
75 75 def test_object_info(self):
76 76 """ Does requesting object information from an in-process kernel work?
77 77 """
78 78 km = BlockingInProcessKernelManager()
79 79 km.start_kernel()
80 80 km.kernel.shell.user_ns['foo'] = 1
81 81 km.shell_channel.object_info('foo')
82 82 msg = km.shell_channel.get_msg()
83 83 self.assertEquals(msg['header']['msg_type'], 'object_info_reply')
84 84 self.assertEquals(msg['content']['name'], 'foo')
85 85 self.assertEquals(msg['content']['type_name'], 'int')
86 86
87 87 def test_history(self):
88 88 """ Does requesting history from an in-process kernel work?
89 89 """
90 90 km = BlockingInProcessKernelManager()
91 91 km.start_kernel()
92 92 km.shell_channel.execute('%who')
93 93 km.shell_channel.history(hist_access_type='tail', n=1)
94 94 msg = km.shell_channel.get_msgs()[-1]
95 95 self.assertEquals(msg['header']['msg_type'], 'history_reply')
96 96 history = msg['content']['history']
97 97 self.assertEquals(len(history), 1)
98 98 self.assertEquals(history[0][2], '%who')
99 99
100 100
101 101 if __name__ == '__main__':
102 102 unittest.main()
1 NO CONTENT: file renamed from IPython/kernel/zmq/tests/test_kernelmanager.py to IPython/kernel/tests/test_kernelmanager.py
@@ -1,70 +1,70 b''
1 1 """Publishing
2 2 """
3 3
4 4 #-----------------------------------------------------------------------------
5 5 # Copyright (C) 2012 The IPython Development Team
6 6 #
7 7 # Distributed under the terms of the BSD License. The full license is in
8 8 # the file COPYING, distributed as part of this software.
9 9 #-----------------------------------------------------------------------------
10 10
11 11 #-----------------------------------------------------------------------------
12 12 # Imports
13 13 #-----------------------------------------------------------------------------
14 14
15 15 from IPython.config import Configurable
16 from IPython.inprocess.socket import SocketABC
16 from IPython.kernel.inprocess.socket import SocketABC
17 17 from IPython.utils.jsonutil import json_clean
18 18 from IPython.utils.traitlets import Instance, Dict, CBytes
19 19 from IPython.kernel.zmq.serialize import serialize_object
20 20 from IPython.kernel.zmq.session import Session, extract_header
21 21
22 22 #-----------------------------------------------------------------------------
23 23 # Code
24 24 #-----------------------------------------------------------------------------
25 25
26 26
27 27 class ZMQDataPublisher(Configurable):
28 28
29 29 topic = topic = CBytes(b'datapub')
30 30 session = Instance(Session)
31 31 pub_socket = Instance(SocketABC)
32 32 parent_header = Dict({})
33 33
34 34 def set_parent(self, parent):
35 35 """Set the parent for outbound messages."""
36 36 self.parent_header = extract_header(parent)
37 37
38 38 def publish_data(self, data):
39 39 """publish a data_message on the IOPub channel
40 40
41 41 Parameters
42 42 ----------
43 43
44 44 data : dict
45 45 The data to be published. Think of it as a namespace.
46 46 """
47 47 session = self.session
48 48 buffers = serialize_object(data,
49 49 buffer_threshold=session.buffer_threshold,
50 50 item_threshold=session.item_threshold,
51 51 )
52 52 content = json_clean(dict(keys=data.keys()))
53 53 session.send(self.pub_socket, 'data_message', content=content,
54 54 parent=self.parent_header,
55 55 buffers=buffers,
56 56 ident=self.topic,
57 57 )
58 58
59 59
60 60 def publish_data(data):
61 61 """publish a data_message on the IOPub channel
62 62
63 63 Parameters
64 64 ----------
65 65
66 66 data : dict
67 67 The data to be published. Think of it as a namespace.
68 68 """
69 69 from IPython.kernel.zmq.zmqshell import ZMQInteractiveShell
70 70 ZMQInteractiveShell.instance().data_pub.publish_data(data)
@@ -1,64 +1,64 b''
1 1 import __builtin__
2 2 import sys
3 3
4 4 from IPython.core.displayhook import DisplayHook
5 from IPython.inprocess.socket import SocketABC
5 from IPython.kernel.inprocess.socket import SocketABC
6 6 from IPython.utils.jsonutil import encode_images
7 7 from IPython.utils.traitlets import Instance, Dict
8 8 from session import extract_header, Session
9 9
10 10 class ZMQDisplayHook(object):
11 11 """A simple displayhook that publishes the object's repr over a ZeroMQ
12 12 socket."""
13 13 topic=None
14 14
15 15 def __init__(self, session, pub_socket):
16 16 self.session = session
17 17 self.pub_socket = pub_socket
18 18 self.parent_header = {}
19 19
20 20 def __call__(self, obj):
21 21 if obj is None:
22 22 return
23 23
24 24 __builtin__._ = obj
25 25 sys.stdout.flush()
26 26 sys.stderr.flush()
27 27 msg = self.session.send(self.pub_socket, u'pyout', {u'data':repr(obj)},
28 28 parent=self.parent_header, ident=self.topic)
29 29
30 30 def set_parent(self, parent):
31 31 self.parent_header = extract_header(parent)
32 32
33 33
34 34 class ZMQShellDisplayHook(DisplayHook):
35 35 """A displayhook subclass that publishes data using ZeroMQ. This is intended
36 36 to work with an InteractiveShell instance. It sends a dict of different
37 37 representations of the object."""
38 38 topic=None
39 39
40 40 session = Instance(Session)
41 41 pub_socket = Instance(SocketABC)
42 42 parent_header = Dict({})
43 43
44 44 def set_parent(self, parent):
45 45 """Set the parent for outbound messages."""
46 46 self.parent_header = extract_header(parent)
47 47
48 48 def start_displayhook(self):
49 49 self.msg = self.session.msg(u'pyout', {}, parent=self.parent_header)
50 50
51 51 def write_output_prompt(self):
52 52 """Write the output prompt."""
53 53 self.msg['content']['execution_count'] = self.prompt_count
54 54
55 55 def write_format_data(self, format_dict):
56 56 self.msg['content']['data'] = encode_images(format_dict)
57 57
58 58 def finish_displayhook(self):
59 59 """Finish up all displayhook activities."""
60 60 sys.stdout.flush()
61 61 sys.stderr.flush()
62 62 self.session.send(self.pub_socket, self.msg, ident=self.topic)
63 63 self.msg = None
64 64
@@ -1,591 +1,591 b''
1 1 """A ZMQ-based subclass of InteractiveShell.
2 2
3 3 This code is meant to ease the refactoring of the base InteractiveShell into
4 4 something with a cleaner architecture for 2-process use, without actually
5 5 breaking InteractiveShell itself. So we're doing something a bit ugly, where
6 6 we subclass and override what we want to fix. Once this is working well, we
7 7 can go back to the base class and refactor the code for a cleaner inheritance
8 8 implementation that doesn't rely on so much monkeypatching.
9 9
10 10 But this lets us maintain a fully working IPython as we develop the new
11 11 machinery. This should thus be thought of as scaffolding.
12 12 """
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16 from __future__ import print_function
17 17
18 18 # Stdlib
19 19 import os
20 20 import sys
21 21 import time
22 22
23 23 # System library imports
24 24 from zmq.eventloop import ioloop
25 25
26 26 # Our own
27 27 from IPython.core.interactiveshell import (
28 28 InteractiveShell, InteractiveShellABC
29 29 )
30 30 from IPython.core import page
31 31 from IPython.core.autocall import ZMQExitAutocall
32 32 from IPython.core.displaypub import DisplayPublisher
33 33 from IPython.core.error import UsageError
34 34 from IPython.core.magics import MacroToEdit, CodeMagics
35 35 from IPython.core.magic import magics_class, line_magic, Magics
36 36 from IPython.core.payloadpage import install_payload_page
37 from IPython.inprocess.socket import SocketABC
37 from IPython.kernel.inprocess.socket import SocketABC
38 38 from IPython.kernel import (
39 39 get_connection_file, get_connection_info, connect_qtconsole
40 40 )
41 41 from IPython.testing.skipdoctest import skip_doctest
42 42 from IPython.utils import io, openpy
43 43 from IPython.utils.jsonutil import json_clean, encode_images
44 44 from IPython.utils.process import arg_split
45 45 from IPython.utils import py3compat
46 46 from IPython.utils.traitlets import Instance, Type, Dict, CBool, CBytes
47 47 from IPython.utils.warn import warn, error
48 48 from IPython.kernel.zmq.displayhook import ZMQShellDisplayHook
49 49 from IPython.kernel.zmq.datapub import ZMQDataPublisher
50 50 from IPython.kernel.zmq.session import extract_header
51 51 from session import Session
52 52
53 53 #-----------------------------------------------------------------------------
54 54 # Functions and classes
55 55 #-----------------------------------------------------------------------------
56 56
57 57 class ZMQDisplayPublisher(DisplayPublisher):
58 58 """A display publisher that publishes data using a ZeroMQ PUB socket."""
59 59
60 60 session = Instance(Session)
61 61 pub_socket = Instance(SocketABC)
62 62 parent_header = Dict({})
63 63 topic = CBytes(b'displaypub')
64 64
65 65 def set_parent(self, parent):
66 66 """Set the parent for outbound messages."""
67 67 self.parent_header = extract_header(parent)
68 68
69 69 def _flush_streams(self):
70 70 """flush IO Streams prior to display"""
71 71 sys.stdout.flush()
72 72 sys.stderr.flush()
73 73
74 74 def publish(self, source, data, metadata=None):
75 75 self._flush_streams()
76 76 if metadata is None:
77 77 metadata = {}
78 78 self._validate_data(source, data, metadata)
79 79 content = {}
80 80 content['source'] = source
81 81 content['data'] = encode_images(data)
82 82 content['metadata'] = metadata
83 83 self.session.send(
84 84 self.pub_socket, u'display_data', json_clean(content),
85 85 parent=self.parent_header, ident=self.topic,
86 86 )
87 87
88 88 def clear_output(self, stdout=True, stderr=True, other=True):
89 89 content = dict(stdout=stdout, stderr=stderr, other=other)
90 90
91 91 if stdout:
92 92 print('\r', file=sys.stdout, end='')
93 93 if stderr:
94 94 print('\r', file=sys.stderr, end='')
95 95
96 96 self._flush_streams()
97 97
98 98 self.session.send(
99 99 self.pub_socket, u'clear_output', content,
100 100 parent=self.parent_header, ident=self.topic,
101 101 )
102 102
103 103 @magics_class
104 104 class KernelMagics(Magics):
105 105 #------------------------------------------------------------------------
106 106 # Magic overrides
107 107 #------------------------------------------------------------------------
108 108 # Once the base class stops inheriting from magic, this code needs to be
109 109 # moved into a separate machinery as well. For now, at least isolate here
110 110 # the magics which this class needs to implement differently from the base
111 111 # class, or that are unique to it.
112 112
113 113 @line_magic
114 114 def doctest_mode(self, parameter_s=''):
115 115 """Toggle doctest mode on and off.
116 116
117 117 This mode is intended to make IPython behave as much as possible like a
118 118 plain Python shell, from the perspective of how its prompts, exceptions
119 119 and output look. This makes it easy to copy and paste parts of a
120 120 session into doctests. It does so by:
121 121
122 122 - Changing the prompts to the classic ``>>>`` ones.
123 123 - Changing the exception reporting mode to 'Plain'.
124 124 - Disabling pretty-printing of output.
125 125
126 126 Note that IPython also supports the pasting of code snippets that have
127 127 leading '>>>' and '...' prompts in them. This means that you can paste
128 128 doctests from files or docstrings (even if they have leading
129 129 whitespace), and the code will execute correctly. You can then use
130 130 '%history -t' to see the translated history; this will give you the
131 131 input after removal of all the leading prompts and whitespace, which
132 132 can be pasted back into an editor.
133 133
134 134 With these features, you can switch into this mode easily whenever you
135 135 need to do testing and changes to doctests, without having to leave
136 136 your existing IPython session.
137 137 """
138 138
139 139 from IPython.utils.ipstruct import Struct
140 140
141 141 # Shorthands
142 142 shell = self.shell
143 143 disp_formatter = self.shell.display_formatter
144 144 ptformatter = disp_formatter.formatters['text/plain']
145 145 # dstore is a data store kept in the instance metadata bag to track any
146 146 # changes we make, so we can undo them later.
147 147 dstore = shell.meta.setdefault('doctest_mode', Struct())
148 148 save_dstore = dstore.setdefault
149 149
150 150 # save a few values we'll need to recover later
151 151 mode = save_dstore('mode', False)
152 152 save_dstore('rc_pprint', ptformatter.pprint)
153 153 save_dstore('rc_plain_text_only',disp_formatter.plain_text_only)
154 154 save_dstore('xmode', shell.InteractiveTB.mode)
155 155
156 156 if mode == False:
157 157 # turn on
158 158 ptformatter.pprint = False
159 159 disp_formatter.plain_text_only = True
160 160 shell.magic('xmode Plain')
161 161 else:
162 162 # turn off
163 163 ptformatter.pprint = dstore.rc_pprint
164 164 disp_formatter.plain_text_only = dstore.rc_plain_text_only
165 165 shell.magic("xmode " + dstore.xmode)
166 166
167 167 # Store new mode and inform on console
168 168 dstore.mode = bool(1-int(mode))
169 169 mode_label = ['OFF','ON'][dstore.mode]
170 170 print('Doctest mode is:', mode_label)
171 171
172 172 # Send the payload back so that clients can modify their prompt display
173 173 payload = dict(
174 174 source='IPython.kernel.zmq.zmqshell.ZMQInteractiveShell.doctest_mode',
175 175 mode=dstore.mode)
176 176 shell.payload_manager.write_payload(payload)
177 177
178 178
179 179 _find_edit_target = CodeMagics._find_edit_target
180 180
181 181 @skip_doctest
182 182 @line_magic
183 183 def edit(self, parameter_s='', last_call=['','']):
184 184 """Bring up an editor and execute the resulting code.
185 185
186 186 Usage:
187 187 %edit [options] [args]
188 188
189 189 %edit runs an external text editor. You will need to set the command for
190 190 this editor via the ``TerminalInteractiveShell.editor`` option in your
191 191 configuration file before it will work.
192 192
193 193 This command allows you to conveniently edit multi-line code right in
194 194 your IPython session.
195 195
196 196 If called without arguments, %edit opens up an empty editor with a
197 197 temporary file and will execute the contents of this file when you
198 198 close it (don't forget to save it!).
199 199
200 200
201 201 Options:
202 202
203 203 -n <number>: open the editor at a specified line number. By default,
204 204 the IPython editor hook uses the unix syntax 'editor +N filename', but
205 205 you can configure this by providing your own modified hook if your
206 206 favorite editor supports line-number specifications with a different
207 207 syntax.
208 208
209 209 -p: this will call the editor with the same data as the previous time
210 210 it was used, regardless of how long ago (in your current session) it
211 211 was.
212 212
213 213 -r: use 'raw' input. This option only applies to input taken from the
214 214 user's history. By default, the 'processed' history is used, so that
215 215 magics are loaded in their transformed version to valid Python. If
216 216 this option is given, the raw input as typed as the command line is
217 217 used instead. When you exit the editor, it will be executed by
218 218 IPython's own processor.
219 219
220 220 -x: do not execute the edited code immediately upon exit. This is
221 221 mainly useful if you are editing programs which need to be called with
222 222 command line arguments, which you can then do using %run.
223 223
224 224
225 225 Arguments:
226 226
227 227 If arguments are given, the following possibilites exist:
228 228
229 229 - The arguments are numbers or pairs of colon-separated numbers (like
230 230 1 4:8 9). These are interpreted as lines of previous input to be
231 231 loaded into the editor. The syntax is the same of the %macro command.
232 232
233 233 - If the argument doesn't start with a number, it is evaluated as a
234 234 variable and its contents loaded into the editor. You can thus edit
235 235 any string which contains python code (including the result of
236 236 previous edits).
237 237
238 238 - If the argument is the name of an object (other than a string),
239 239 IPython will try to locate the file where it was defined and open the
240 240 editor at the point where it is defined. You can use `%edit function`
241 241 to load an editor exactly at the point where 'function' is defined,
242 242 edit it and have the file be executed automatically.
243 243
244 244 If the object is a macro (see %macro for details), this opens up your
245 245 specified editor with a temporary file containing the macro's data.
246 246 Upon exit, the macro is reloaded with the contents of the file.
247 247
248 248 Note: opening at an exact line is only supported under Unix, and some
249 249 editors (like kedit and gedit up to Gnome 2.8) do not understand the
250 250 '+NUMBER' parameter necessary for this feature. Good editors like
251 251 (X)Emacs, vi, jed, pico and joe all do.
252 252
253 253 - If the argument is not found as a variable, IPython will look for a
254 254 file with that name (adding .py if necessary) and load it into the
255 255 editor. It will execute its contents with execfile() when you exit,
256 256 loading any code in the file into your interactive namespace.
257 257
258 258 After executing your code, %edit will return as output the code you
259 259 typed in the editor (except when it was an existing file). This way
260 260 you can reload the code in further invocations of %edit as a variable,
261 261 via _<NUMBER> or Out[<NUMBER>], where <NUMBER> is the prompt number of
262 262 the output.
263 263
264 264 Note that %edit is also available through the alias %ed.
265 265
266 266 This is an example of creating a simple function inside the editor and
267 267 then modifying it. First, start up the editor:
268 268
269 269 In [1]: ed
270 270 Editing... done. Executing edited code...
271 271 Out[1]: 'def foo():n print "foo() was defined in an editing session"n'
272 272
273 273 We can then call the function foo():
274 274
275 275 In [2]: foo()
276 276 foo() was defined in an editing session
277 277
278 278 Now we edit foo. IPython automatically loads the editor with the
279 279 (temporary) file where foo() was previously defined:
280 280
281 281 In [3]: ed foo
282 282 Editing... done. Executing edited code...
283 283
284 284 And if we call foo() again we get the modified version:
285 285
286 286 In [4]: foo()
287 287 foo() has now been changed!
288 288
289 289 Here is an example of how to edit a code snippet successive
290 290 times. First we call the editor:
291 291
292 292 In [5]: ed
293 293 Editing... done. Executing edited code...
294 294 hello
295 295 Out[5]: "print 'hello'n"
296 296
297 297 Now we call it again with the previous output (stored in _):
298 298
299 299 In [6]: ed _
300 300 Editing... done. Executing edited code...
301 301 hello world
302 302 Out[6]: "print 'hello world'n"
303 303
304 304 Now we call it with the output #8 (stored in _8, also as Out[8]):
305 305
306 306 In [7]: ed _8
307 307 Editing... done. Executing edited code...
308 308 hello again
309 309 Out[7]: "print 'hello again'n"
310 310 """
311 311
312 312 opts,args = self.parse_options(parameter_s,'prn:')
313 313
314 314 try:
315 315 filename, lineno, _ = CodeMagics._find_edit_target(self.shell, args, opts, last_call)
316 316 except MacroToEdit as e:
317 317 # TODO: Implement macro editing over 2 processes.
318 318 print("Macro editing not yet implemented in 2-process model.")
319 319 return
320 320
321 321 # Make sure we send to the client an absolute path, in case the working
322 322 # directory of client and kernel don't match
323 323 filename = os.path.abspath(filename)
324 324
325 325 payload = {
326 326 'source' : 'IPython.kernel.zmq.zmqshell.ZMQInteractiveShell.edit_magic',
327 327 'filename' : filename,
328 328 'line_number' : lineno
329 329 }
330 330 self.shell.payload_manager.write_payload(payload)
331 331
332 332 # A few magics that are adapted to the specifics of using pexpect and a
333 333 # remote terminal
334 334
335 335 @line_magic
336 336 def clear(self, arg_s):
337 337 """Clear the terminal."""
338 338 if os.name == 'posix':
339 339 self.shell.system("clear")
340 340 else:
341 341 self.shell.system("cls")
342 342
343 343 if os.name == 'nt':
344 344 # This is the usual name in windows
345 345 cls = line_magic('cls')(clear)
346 346
347 347 # Terminal pagers won't work over pexpect, but we do have our own pager
348 348
349 349 @line_magic
350 350 def less(self, arg_s):
351 351 """Show a file through the pager.
352 352
353 353 Files ending in .py are syntax-highlighted."""
354 354 if not arg_s:
355 355 raise UsageError('Missing filename.')
356 356
357 357 cont = open(arg_s).read()
358 358 if arg_s.endswith('.py'):
359 359 cont = self.shell.pycolorize(openpy.read_py_file(arg_s, skip_encoding_cookie=False))
360 360 else:
361 361 cont = open(arg_s).read()
362 362 page.page(cont)
363 363
364 364 more = line_magic('more')(less)
365 365
366 366 # Man calls a pager, so we also need to redefine it
367 367 if os.name == 'posix':
368 368 @line_magic
369 369 def man(self, arg_s):
370 370 """Find the man page for the given command and display in pager."""
371 371 page.page(self.shell.getoutput('man %s | col -b' % arg_s,
372 372 split=False))
373 373
374 374 @line_magic
375 375 def connect_info(self, arg_s):
376 376 """Print information for connecting other clients to this kernel
377 377
378 378 It will print the contents of this session's connection file, as well as
379 379 shortcuts for local clients.
380 380
381 381 In the simplest case, when called from the most recently launched kernel,
382 382 secondary clients can be connected, simply with:
383 383
384 384 $> ipython <app> --existing
385 385
386 386 """
387 387
388 388 from IPython.core.application import BaseIPythonApplication as BaseIPApp
389 389
390 390 if BaseIPApp.initialized():
391 391 app = BaseIPApp.instance()
392 392 security_dir = app.profile_dir.security_dir
393 393 profile = app.profile
394 394 else:
395 395 profile = 'default'
396 396 security_dir = ''
397 397
398 398 try:
399 399 connection_file = get_connection_file()
400 400 info = get_connection_info(unpack=False)
401 401 except Exception as e:
402 402 error("Could not get connection info: %r" % e)
403 403 return
404 404
405 405 # add profile flag for non-default profile
406 406 profile_flag = "--profile %s" % profile if profile != 'default' else ""
407 407
408 408 # if it's in the security dir, truncate to basename
409 409 if security_dir == os.path.dirname(connection_file):
410 410 connection_file = os.path.basename(connection_file)
411 411
412 412
413 413 print (info + '\n')
414 414 print ("Paste the above JSON into a file, and connect with:\n"
415 415 " $> ipython <app> --existing <file>\n"
416 416 "or, if you are local, you can connect with just:\n"
417 417 " $> ipython <app> --existing {0} {1}\n"
418 418 "or even just:\n"
419 419 " $> ipython <app> --existing {1}\n"
420 420 "if this is the most recent IPython session you have started.".format(
421 421 connection_file, profile_flag
422 422 )
423 423 )
424 424
425 425 @line_magic
426 426 def qtconsole(self, arg_s):
427 427 """Open a qtconsole connected to this kernel.
428 428
429 429 Useful for connecting a qtconsole to running notebooks, for better
430 430 debugging.
431 431 """
432 432
433 433 # %qtconsole should imply bind_kernel for engines:
434 434 try:
435 435 from IPython.parallel import bind_kernel
436 436 except ImportError:
437 437 # technically possible, because parallel has higher pyzmq min-version
438 438 pass
439 439 else:
440 440 bind_kernel()
441 441
442 442 try:
443 443 p = connect_qtconsole(argv=arg_split(arg_s, os.name=='posix'))
444 444 except Exception as e:
445 445 error("Could not start qtconsole: %r" % e)
446 446 return
447 447
448 448 def safe_unicode(e):
449 449 """unicode(e) with various fallbacks. Used for exceptions, which may not be
450 450 safe to call unicode() on.
451 451 """
452 452 try:
453 453 return unicode(e)
454 454 except UnicodeError:
455 455 pass
456 456
457 457 try:
458 458 return py3compat.str_to_unicode(str(e))
459 459 except UnicodeError:
460 460 pass
461 461
462 462 try:
463 463 return py3compat.str_to_unicode(repr(e))
464 464 except UnicodeError:
465 465 pass
466 466
467 467 return u'Unrecoverably corrupt evalue'
468 468
469 469
470 470 class ZMQInteractiveShell(InteractiveShell):
471 471 """A subclass of InteractiveShell for ZMQ."""
472 472
473 473 displayhook_class = Type(ZMQShellDisplayHook)
474 474 display_pub_class = Type(ZMQDisplayPublisher)
475 475 data_pub_class = Type(ZMQDataPublisher)
476 476
477 477 # Override the traitlet in the parent class, because there's no point using
478 478 # readline for the kernel. Can be removed when the readline code is moved
479 479 # to the terminal frontend.
480 480 colors_force = CBool(True)
481 481 readline_use = CBool(False)
482 482 # autoindent has no meaning in a zmqshell, and attempting to enable it
483 483 # will print a warning in the absence of readline.
484 484 autoindent = CBool(False)
485 485
486 486 exiter = Instance(ZMQExitAutocall)
487 487 def _exiter_default(self):
488 488 return ZMQExitAutocall(self)
489 489
490 490 def _exit_now_changed(self, name, old, new):
491 491 """stop eventloop when exit_now fires"""
492 492 if new:
493 493 loop = ioloop.IOLoop.instance()
494 494 loop.add_timeout(time.time()+0.1, loop.stop)
495 495
496 496 keepkernel_on_exit = None
497 497
498 498 # Over ZeroMQ, GUI control isn't done with PyOS_InputHook as there is no
499 499 # interactive input being read; we provide event loop support in ipkernel
500 500 from .eventloops import enable_gui
501 501 enable_gui = staticmethod(enable_gui)
502 502
503 503 def init_environment(self):
504 504 """Configure the user's environment.
505 505
506 506 """
507 507 env = os.environ
508 508 # These two ensure 'ls' produces nice coloring on BSD-derived systems
509 509 env['TERM'] = 'xterm-color'
510 510 env['CLICOLOR'] = '1'
511 511 # Since normal pagers don't work at all (over pexpect we don't have
512 512 # single-key control of the subprocess), try to disable paging in
513 513 # subprocesses as much as possible.
514 514 env['PAGER'] = 'cat'
515 515 env['GIT_PAGER'] = 'cat'
516 516
517 517 # And install the payload version of page.
518 518 install_payload_page()
519 519
520 520 def auto_rewrite_input(self, cmd):
521 521 """Called to show the auto-rewritten input for autocall and friends.
522 522
523 523 FIXME: this payload is currently not correctly processed by the
524 524 frontend.
525 525 """
526 526 new = self.prompt_manager.render('rewrite') + cmd
527 527 payload = dict(
528 528 source='IPython.kernel.zmq.zmqshell.ZMQInteractiveShell.auto_rewrite_input',
529 529 transformed_input=new,
530 530 )
531 531 self.payload_manager.write_payload(payload)
532 532
533 533 def ask_exit(self):
534 534 """Engage the exit actions."""
535 535 self.exit_now = True
536 536 payload = dict(
537 537 source='IPython.kernel.zmq.zmqshell.ZMQInteractiveShell.ask_exit',
538 538 exit=True,
539 539 keepkernel=self.keepkernel_on_exit,
540 540 )
541 541 self.payload_manager.write_payload(payload)
542 542
543 543 def _showtraceback(self, etype, evalue, stb):
544 544
545 545 exc_content = {
546 546 u'traceback' : stb,
547 547 u'ename' : unicode(etype.__name__),
548 548 u'evalue' : safe_unicode(evalue)
549 549 }
550 550
551 551 dh = self.displayhook
552 552 # Send exception info over pub socket for other clients than the caller
553 553 # to pick up
554 554 topic = None
555 555 if dh.topic:
556 556 topic = dh.topic.replace(b'pyout', b'pyerr')
557 557
558 558 exc_msg = dh.session.send(dh.pub_socket, u'pyerr', json_clean(exc_content), dh.parent_header, ident=topic)
559 559
560 560 # FIXME - Hack: store exception info in shell object. Right now, the
561 561 # caller is reading this info after the fact, we need to fix this logic
562 562 # to remove this hack. Even uglier, we need to store the error status
563 563 # here, because in the main loop, the logic that sets it is being
564 564 # skipped because runlines swallows the exceptions.
565 565 exc_content[u'status'] = u'error'
566 566 self._reply_content = exc_content
567 567 # /FIXME
568 568
569 569 return exc_content
570 570
571 571 def set_next_input(self, text):
572 572 """Send the specified text to the frontend to be presented at the next
573 573 input cell."""
574 574 payload = dict(
575 575 source='IPython.kernel.zmq.zmqshell.ZMQInteractiveShell.set_next_input',
576 576 text=text
577 577 )
578 578 self.payload_manager.write_payload(payload)
579 579
580 580 #-------------------------------------------------------------------------
581 581 # Things related to magics
582 582 #-------------------------------------------------------------------------
583 583
584 584 def init_magics(self):
585 585 super(ZMQInteractiveShell, self).init_magics()
586 586 self.register_magics(KernelMagics)
587 587 self.magics_manager.register_alias('ed', 'edit')
588 588
589 589
590 590
591 591 InteractiveShellABC.register(ZMQInteractiveShell)
@@ -1,598 +1,602 b''
1 1 # -*- coding: utf-8 -*-
2 2 """IPython Test Suite Runner.
3 3
4 4 This module provides a main entry point to a user script to test IPython
5 5 itself from the command line. There are two ways of running this script:
6 6
7 7 1. With the syntax `iptest all`. This runs our entire test suite by
8 8 calling this script (with different arguments) recursively. This
9 9 causes modules and package to be tested in different processes, using nose
10 10 or trial where appropriate.
11 11 2. With the regular nose syntax, like `iptest -vvs IPython`. In this form
12 12 the script simply calls nose, but with special command line flags and
13 13 plugins loaded.
14 14
15 15 """
16 16
17 17 #-----------------------------------------------------------------------------
18 18 # Copyright (C) 2009-2011 The IPython Development Team
19 19 #
20 20 # Distributed under the terms of the BSD License. The full license is in
21 21 # the file COPYING, distributed as part of this software.
22 22 #-----------------------------------------------------------------------------
23 23
24 24 #-----------------------------------------------------------------------------
25 25 # Imports
26 26 #-----------------------------------------------------------------------------
27 27 from __future__ import print_function
28 28
29 29 # Stdlib
30 30 import glob
31 31 import os
32 32 import os.path as path
33 33 import signal
34 34 import sys
35 35 import subprocess
36 36 import tempfile
37 37 import time
38 38 import warnings
39 39
40 40 # Note: monkeypatch!
41 41 # We need to monkeypatch a small problem in nose itself first, before importing
42 42 # it for actual use. This should get into nose upstream, but its release cycle
43 43 # is slow and we need it for our parametric tests to work correctly.
44 44 from IPython.testing import nosepatch
45 45
46 46 # Monkeypatch extra assert methods into nose.tools if they're not already there.
47 47 # This can be dropped once we no longer test on Python 2.6
48 48 from IPython.testing import nose_assert_methods
49 49
50 50 # Now, proceed to import nose itself
51 51 import nose.plugins.builtin
52 52 from nose.plugins.xunit import Xunit
53 53 from nose import SkipTest
54 54 from nose.core import TestProgram
55 55
56 56 # Our own imports
57 57 from IPython.utils import py3compat
58 58 from IPython.utils.importstring import import_item
59 59 from IPython.utils.path import get_ipython_module_path, get_ipython_package_dir
60 60 from IPython.utils.process import find_cmd, pycmd2argv
61 61 from IPython.utils.sysinfo import sys_info
62 62 from IPython.utils.tempdir import TemporaryDirectory
63 63 from IPython.utils.warn import warn
64 64
65 65 from IPython.testing import globalipapp
66 66 from IPython.testing.plugin.ipdoctest import IPythonDoctest
67 67 from IPython.external.decorators import KnownFailure, knownfailureif
68 68
69 69 pjoin = path.join
70 70
71 71
72 72 #-----------------------------------------------------------------------------
73 73 # Globals
74 74 #-----------------------------------------------------------------------------
75 75
76 76
77 77 #-----------------------------------------------------------------------------
78 78 # Warnings control
79 79 #-----------------------------------------------------------------------------
80 80
81 81 # Twisted generates annoying warnings with Python 2.6, as will do other code
82 82 # that imports 'sets' as of today
83 83 warnings.filterwarnings('ignore', 'the sets module is deprecated',
84 84 DeprecationWarning )
85 85
86 86 # This one also comes from Twisted
87 87 warnings.filterwarnings('ignore', 'the sha module is deprecated',
88 88 DeprecationWarning)
89 89
90 90 # Wx on Fedora11 spits these out
91 91 warnings.filterwarnings('ignore', 'wxPython/wxWidgets release number mismatch',
92 92 UserWarning)
93 93
94 94 # ------------------------------------------------------------------------------
95 95 # Monkeypatch Xunit to count known failures as skipped.
96 96 # ------------------------------------------------------------------------------
97 97 def monkeypatch_xunit():
98 98 try:
99 99 knownfailureif(True)(lambda: None)()
100 100 except Exception as e:
101 101 KnownFailureTest = type(e)
102 102
103 103 def addError(self, test, err, capt=None):
104 104 if issubclass(err[0], KnownFailureTest):
105 105 err = (SkipTest,) + err[1:]
106 106 return self.orig_addError(test, err, capt)
107 107
108 108 Xunit.orig_addError = Xunit.addError
109 109 Xunit.addError = addError
110 110
111 111 #-----------------------------------------------------------------------------
112 112 # Logic for skipping doctests
113 113 #-----------------------------------------------------------------------------
114 114 def extract_version(mod):
115 115 return mod.__version__
116 116
117 117 def test_for(item, min_version=None, callback=extract_version):
118 118 """Test to see if item is importable, and optionally check against a minimum
119 119 version.
120 120
121 121 If min_version is given, the default behavior is to check against the
122 122 `__version__` attribute of the item, but specifying `callback` allows you to
123 123 extract the value you are interested in. e.g::
124 124
125 125 In [1]: import sys
126 126
127 127 In [2]: from IPython.testing.iptest import test_for
128 128
129 129 In [3]: test_for('sys', (2,6), callback=lambda sys: sys.version_info)
130 130 Out[3]: True
131 131
132 132 """
133 133 try:
134 134 check = import_item(item)
135 135 except (ImportError, RuntimeError):
136 136 # GTK reports Runtime error if it can't be initialized even if it's
137 137 # importable.
138 138 return False
139 139 else:
140 140 if min_version:
141 141 if callback:
142 142 # extra processing step to get version to compare
143 143 check = callback(check)
144 144
145 145 return check >= min_version
146 146 else:
147 147 return True
148 148
149 149 # Global dict where we can store information on what we have and what we don't
150 150 # have available at test run time
151 151 have = {}
152 152
153 153 have['curses'] = test_for('_curses')
154 154 have['matplotlib'] = test_for('matplotlib')
155 155 have['numpy'] = test_for('numpy')
156 156 have['pexpect'] = test_for('IPython.external.pexpect')
157 157 have['pymongo'] = test_for('pymongo')
158 158 have['pygments'] = test_for('pygments')
159 159 have['qt'] = test_for('IPython.external.qt')
160 160 have['rpy2'] = test_for('rpy2')
161 161 have['sqlite3'] = test_for('sqlite3')
162 162 have['cython'] = test_for('Cython')
163 163 have['oct2py'] = test_for('oct2py')
164 164 have['tornado'] = test_for('tornado.version_info', (2,1,0), callback=None)
165 165 have['jinja2'] = test_for('jinja2')
166 166 have['wx'] = test_for('wx')
167 167 have['wx.aui'] = test_for('wx.aui')
168 168 have['azure'] = test_for('azure')
169 169
170 170 min_zmq = (2,1,11)
171 171
172 172 have['zmq'] = test_for('zmq.pyzmq_version_info', min_zmq, callback=lambda x: x())
173 173
174 174 #-----------------------------------------------------------------------------
175 175 # Functions and classes
176 176 #-----------------------------------------------------------------------------
177 177
178 178 def report():
179 179 """Return a string with a summary report of test-related variables."""
180 180
181 181 out = [ sys_info(), '\n']
182 182
183 183 avail = []
184 184 not_avail = []
185 185
186 186 for k, is_avail in have.items():
187 187 if is_avail:
188 188 avail.append(k)
189 189 else:
190 190 not_avail.append(k)
191 191
192 192 if avail:
193 193 out.append('\nTools and libraries available at test time:\n')
194 194 avail.sort()
195 195 out.append(' ' + ' '.join(avail)+'\n')
196 196
197 197 if not_avail:
198 198 out.append('\nTools and libraries NOT available at test time:\n')
199 199 not_avail.sort()
200 200 out.append(' ' + ' '.join(not_avail)+'\n')
201 201
202 202 return ''.join(out)
203 203
204 204
205 205 def make_exclude():
206 206 """Make patterns of modules and packages to exclude from testing.
207 207
208 208 For the IPythonDoctest plugin, we need to exclude certain patterns that
209 209 cause testing problems. We should strive to minimize the number of
210 210 skipped modules, since this means untested code.
211 211
212 212 These modules and packages will NOT get scanned by nose at all for tests.
213 213 """
214 214 # Simple utility to make IPython paths more readably, we need a lot of
215 215 # these below
216 216 ipjoin = lambda *paths: pjoin('IPython', *paths)
217 217
218 218 exclusions = [ipjoin('external'),
219 219 ipjoin('quarantine'),
220 220 ipjoin('deathrow'),
221 221 # This guy is probably attic material
222 222 ipjoin('testing', 'mkdoctests'),
223 223 # Testing inputhook will need a lot of thought, to figure out
224 224 # how to have tests that don't lock up with the gui event
225 225 # loops in the picture
226 226 ipjoin('lib', 'inputhook'),
227 227 # Config files aren't really importable stand-alone
228 228 ipjoin('config', 'profile'),
229 229 # The notebook 'static' directory contains JS, css and other
230 230 # files for web serving. Occasionally projects may put a .py
231 231 # file in there (MathJax ships a conf.py), so we might as
232 232 # well play it safe and skip the whole thing.
233 233 ipjoin('frontend', 'html', 'notebook', 'static')
234 234 ]
235 235 if not have['sqlite3']:
236 236 exclusions.append(ipjoin('core', 'tests', 'test_history'))
237 237 exclusions.append(ipjoin('core', 'history'))
238 238 if not have['wx']:
239 239 exclusions.append(ipjoin('lib', 'inputhookwx'))
240 240
241 if 'IPython.kernel.inprocess' not in sys.argv:
242 exclusions.append(ipjoin('kernel', 'inprocess'))
243
241 244 # FIXME: temporarily disable autoreload tests, as they can produce
242 245 # spurious failures in subsequent tests (cythonmagic).
243 246 exclusions.append(ipjoin('extensions', 'autoreload'))
244 247 exclusions.append(ipjoin('extensions', 'tests', 'test_autoreload'))
245 248
246 249 # We do this unconditionally, so that the test suite doesn't import
247 250 # gtk, changing the default encoding and masking some unicode bugs.
248 251 exclusions.append(ipjoin('lib', 'inputhookgtk'))
249 exclusions.append(ipjoin('zmq', 'gui', 'gtkembed'))
252 exclusions.append(ipjoin('kernel', 'zmq', 'gui', 'gtkembed'))
250 253
251 254 # These have to be skipped on win32 because the use echo, rm, cd, etc.
252 255 # See ticket https://github.com/ipython/ipython/issues/87
253 256 if sys.platform == 'win32':
254 257 exclusions.append(ipjoin('testing', 'plugin', 'test_exampleip'))
255 258 exclusions.append(ipjoin('testing', 'plugin', 'dtexample'))
256 259
257 260 if not have['pexpect']:
258 261 exclusions.extend([ipjoin('lib', 'irunner'),
259 262 ipjoin('lib', 'tests', 'test_irunner'),
260 263 ipjoin('frontend', 'terminal', 'console'),
261 264 ])
262 265
263 266 if not have['zmq']:
264 exclusions.append(ipjoin('zmq'))
267 exclusions.append(ipjoin('kernel'))
265 268 exclusions.append(ipjoin('frontend', 'qt'))
266 269 exclusions.append(ipjoin('frontend', 'html'))
267 270 exclusions.append(ipjoin('frontend', 'consoleapp.py'))
268 271 exclusions.append(ipjoin('frontend', 'terminal', 'console'))
269 272 exclusions.append(ipjoin('parallel'))
270 273 elif not have['qt'] or not have['pygments']:
271 274 exclusions.append(ipjoin('frontend', 'qt'))
272 275
273 276 if not have['pymongo']:
274 277 exclusions.append(ipjoin('parallel', 'controller', 'mongodb'))
275 278 exclusions.append(ipjoin('parallel', 'tests', 'test_mongodb'))
276 279
277 280 if not have['matplotlib']:
278 281 exclusions.extend([ipjoin('core', 'pylabtools'),
279 282 ipjoin('core', 'tests', 'test_pylabtools'),
280 ipjoin('zmq', 'pylab'),
283 ipjoin('kernel', 'zmq', 'pylab'),
281 284 ])
282 285
283 286 if not have['cython']:
284 287 exclusions.extend([ipjoin('extensions', 'cythonmagic')])
285 288 exclusions.extend([ipjoin('extensions', 'tests', 'test_cythonmagic')])
286 289
287 290 if not have['oct2py']:
288 291 exclusions.extend([ipjoin('extensions', 'octavemagic')])
289 292 exclusions.extend([ipjoin('extensions', 'tests', 'test_octavemagic')])
290 293
291 294 if not have['tornado']:
292 295 exclusions.append(ipjoin('frontend', 'html'))
293 296
294 297 if not have['jinja2']:
295 298 exclusions.append(ipjoin('frontend', 'html', 'notebook', 'notebookapp'))
296 299
297 300 if not have['rpy2'] or not have['numpy']:
298 301 exclusions.append(ipjoin('extensions', 'rmagic'))
299 302 exclusions.append(ipjoin('extensions', 'tests', 'test_rmagic'))
300 303
301 304 if not have['azure']:
302 305 exclusions.append(ipjoin('frontend', 'html', 'notebook', 'azurenbmanager'))
303 306
304 307 # This is needed for the reg-exp to match on win32 in the ipdoctest plugin.
305 308 if sys.platform == 'win32':
306 309 exclusions = [s.replace('\\','\\\\') for s in exclusions]
307 310
308 311 # check for any exclusions that don't seem to exist:
309 312 parent, _ = os.path.split(get_ipython_package_dir())
310 313 for exclusion in exclusions:
311 314 if exclusion.endswith(('deathrow', 'quarantine')):
312 315 # ignore deathrow/quarantine, which exist in dev, but not install
313 316 continue
314 317 fullpath = pjoin(parent, exclusion)
315 318 if not os.path.exists(fullpath) and not glob.glob(fullpath + '.*'):
316 319 warn("Excluding nonexistent file: %r" % exclusion)
317 320
318 321 return exclusions
319 322
320 323
321 324 class IPTester(object):
322 325 """Call that calls iptest or trial in a subprocess.
323 326 """
324 327 #: string, name of test runner that will be called
325 328 runner = None
326 329 #: list, parameters for test runner
327 330 params = None
328 331 #: list, arguments of system call to be made to call test runner
329 332 call_args = None
330 333 #: list, subprocesses we start (for cleanup)
331 334 processes = None
332 335 #: str, coverage xml output file
333 336 coverage_xml = None
334 337
335 338 def __init__(self, runner='iptest', params=None):
336 339 """Create new test runner."""
337 340 p = os.path
338 341 if runner == 'iptest':
339 342 iptest_app = get_ipython_module_path('IPython.testing.iptest')
340 343 self.runner = pycmd2argv(iptest_app) + sys.argv[1:]
341 344 else:
342 345 raise Exception('Not a valid test runner: %s' % repr(runner))
343 346 if params is None:
344 347 params = []
345 348 if isinstance(params, str):
346 349 params = [params]
347 350 self.params = params
348 351
349 352 # Assemble call
350 353 self.call_args = self.runner+self.params
351 354
352 355 # Find the section we're testing (IPython.foo)
353 356 for sect in self.params:
354 357 if sect.startswith('IPython'): break
355 358 else:
356 359 raise ValueError("Section not found", self.params)
357 360
358 361 if '--with-xunit' in self.call_args:
359 362
360 363 self.call_args.append('--xunit-file')
361 364 # FIXME: when Windows uses subprocess.call, these extra quotes are unnecessary:
362 365 xunit_file = path.abspath(sect+'.xunit.xml')
363 366 if sys.platform == 'win32':
364 367 xunit_file = '"%s"' % xunit_file
365 368 self.call_args.append(xunit_file)
366 369
367 370 if '--with-xml-coverage' in self.call_args:
368 371 self.coverage_xml = path.abspath(sect+".coverage.xml")
369 372 self.call_args.remove('--with-xml-coverage')
370 373 self.call_args = ["coverage", "run", "--source="+sect] + self.call_args[1:]
371 374
372 375 # Store anything we start to clean up on deletion
373 376 self.processes = []
374 377
375 378 def _run_cmd(self):
376 379 with TemporaryDirectory() as IPYTHONDIR:
377 380 env = os.environ.copy()
378 381 env['IPYTHONDIR'] = IPYTHONDIR
379 382 # print >> sys.stderr, '*** CMD:', ' '.join(self.call_args) # dbg
380 383 subp = subprocess.Popen(self.call_args, env=env)
381 384 self.processes.append(subp)
382 385 # If this fails, the process will be left in self.processes and
383 386 # cleaned up later, but if the wait call succeeds, then we can
384 387 # clear the stored process.
385 388 retcode = subp.wait()
386 389 self.processes.pop()
387 390 return retcode
388 391
389 392 def run(self):
390 393 """Run the stored commands"""
391 394 try:
392 395 retcode = self._run_cmd()
393 396 except KeyboardInterrupt:
394 397 return -signal.SIGINT
395 398 except:
396 399 import traceback
397 400 traceback.print_exc()
398 401 return 1 # signal failure
399 402
400 403 if self.coverage_xml:
401 404 subprocess.call(["coverage", "xml", "-o", self.coverage_xml])
402 405 return retcode
403 406
404 407 def __del__(self):
405 408 """Cleanup on exit by killing any leftover processes."""
406 409 for subp in self.processes:
407 410 if subp.poll() is not None:
408 411 continue # process is already dead
409 412
410 413 try:
411 414 print('Cleaning up stale PID: %d' % subp.pid)
412 415 subp.kill()
413 416 except: # (OSError, WindowsError) ?
414 417 # This is just a best effort, if we fail or the process was
415 418 # really gone, ignore it.
416 419 pass
417 420 else:
418 421 for i in range(10):
419 422 if subp.poll() is None:
420 423 time.sleep(0.1)
421 424 else:
422 425 break
423 426
424 427 if subp.poll() is None:
425 428 # The process did not die...
426 429 print('... failed. Manual cleanup may be required.')
427 430
428 431 def make_runners(inc_slow=False):
429 432 """Define the top-level packages that need to be tested.
430 433 """
431 434
432 435 # Packages to be tested via nose, that only depend on the stdlib
433 nose_pkg_names = ['config', 'core', 'extensions', 'frontend', 'kernel', 'lib',
434 'testing', 'utils', 'nbformat', 'inprocess' ]
436 nose_pkg_names = ['config', 'core', 'extensions', 'frontend', 'lib',
437 'testing', 'utils', 'nbformat' ]
435 438
436 439 if have['zmq']:
437 nose_pkg_names.append('zmq')
440 nose_pkg_names.append('kernel')
441 nose_pkg_names.append('kernel.inprocess')
438 442 if inc_slow:
439 443 nose_pkg_names.append('parallel')
440 444
441 445 # For debugging this code, only load quick stuff
442 446 #nose_pkg_names = ['core', 'extensions'] # dbg
443 447
444 448 # Make fully qualified package names prepending 'IPython.' to our name lists
445 449 nose_packages = ['IPython.%s' % m for m in nose_pkg_names ]
446 450
447 451 # Make runners
448 452 runners = [ (v, IPTester('iptest', params=v)) for v in nose_packages ]
449 453
450 454 return runners
451 455
452 456
453 457 def run_iptest():
454 458 """Run the IPython test suite using nose.
455 459
456 460 This function is called when this script is **not** called with the form
457 461 `iptest all`. It simply calls nose with appropriate command line flags
458 462 and accepts all of the standard nose arguments.
459 463 """
460 464 # Apply our monkeypatch to Xunit
461 465 if '--with-xunit' in sys.argv and not hasattr(Xunit, 'orig_addError'):
462 466 monkeypatch_xunit()
463 467
464 468 warnings.filterwarnings('ignore',
465 469 'This will be removed soon. Use IPython.testing.util instead')
466 470
467 471 argv = sys.argv + [ '--detailed-errors', # extra info in tracebacks
468 472
469 473 '--with-ipdoctest',
470 474 '--ipdoctest-tests','--ipdoctest-extension=txt',
471 475
472 476 # We add --exe because of setuptools' imbecility (it
473 477 # blindly does chmod +x on ALL files). Nose does the
474 478 # right thing and it tries to avoid executables,
475 479 # setuptools unfortunately forces our hand here. This
476 480 # has been discussed on the distutils list and the
477 481 # setuptools devs refuse to fix this problem!
478 482 '--exe',
479 483 ]
480 484 if '-a' not in argv and '-A' not in argv:
481 485 argv = argv + ['-a', '!crash']
482 486
483 487 if nose.__version__ >= '0.11':
484 488 # I don't fully understand why we need this one, but depending on what
485 489 # directory the test suite is run from, if we don't give it, 0 tests
486 490 # get run. Specifically, if the test suite is run from the source dir
487 491 # with an argument (like 'iptest.py IPython.core', 0 tests are run,
488 492 # even if the same call done in this directory works fine). It appears
489 493 # that if the requested package is in the current dir, nose bails early
490 494 # by default. Since it's otherwise harmless, leave it in by default
491 495 # for nose >= 0.11, though unfortunately nose 0.10 doesn't support it.
492 496 argv.append('--traverse-namespace')
493 497
494 498 # use our plugin for doctesting. It will remove the standard doctest plugin
495 499 # if it finds it enabled
496 500 plugins = [IPythonDoctest(make_exclude()), KnownFailure()]
497 501
498 502 # We need a global ipython running in this process, but the special
499 503 # in-process group spawns its own IPython kernels, so for *that* group we
500 504 # must avoid also opening the global one (otherwise there's a conflict of
501 505 # singletons). Ultimately the solution to this problem is to refactor our
502 506 # assumptions about what needs to be a singleton and what doesn't (app
503 507 # objects should, individual shells shouldn't). But for now, this
504 508 # workaround allows the test suite for the inprocess module to complete.
505 if not 'IPython.inprocess' in sys.argv:
509 if not 'IPython.kernel.inprocess' in sys.argv:
506 510 globalipapp.start_ipython()
507 511
508 512 # Now nose can run
509 513 TestProgram(argv=argv, addplugins=plugins)
510 514
511 515
512 516 def run_iptestall(inc_slow=False):
513 517 """Run the entire IPython test suite by calling nose and trial.
514 518
515 519 This function constructs :class:`IPTester` instances for all IPython
516 520 modules and package and then runs each of them. This causes the modules
517 521 and packages of IPython to be tested each in their own subprocess using
518 522 nose.
519 523
520 524 Parameters
521 525 ----------
522 526
523 527 inc_slow : bool, optional
524 528 Include slow tests, like IPython.parallel. By default, these tests aren't
525 529 run.
526 530 """
527 531
528 532 runners = make_runners(inc_slow=inc_slow)
529 533
530 534 # Run the test runners in a temporary dir so we can nuke it when finished
531 535 # to clean up any junk files left over by accident. This also makes it
532 536 # robust against being run in non-writeable directories by mistake, as the
533 537 # temp dir will always be user-writeable.
534 538 curdir = os.getcwdu()
535 539 testdir = tempfile.gettempdir()
536 540 os.chdir(testdir)
537 541
538 542 # Run all test runners, tracking execution time
539 543 failed = []
540 544 t_start = time.time()
541 545 try:
542 546 for (name, runner) in runners:
543 547 print('*'*70)
544 548 print('IPython test group:',name)
545 549 res = runner.run()
546 550 if res:
547 551 failed.append( (name, runner) )
548 552 if res == -signal.SIGINT:
549 553 print("Interrupted")
550 554 break
551 555 finally:
552 556 os.chdir(curdir)
553 557 t_end = time.time()
554 558 t_tests = t_end - t_start
555 559 nrunners = len(runners)
556 560 nfail = len(failed)
557 561 # summarize results
558 562 print()
559 563 print('*'*70)
560 564 print('Test suite completed for system with the following information:')
561 565 print(report())
562 566 print('Ran %s test groups in %.3fs' % (nrunners, t_tests))
563 567 print()
564 568 print('Status:')
565 569 if not failed:
566 570 print('OK')
567 571 else:
568 572 # If anything went wrong, point out what command to rerun manually to
569 573 # see the actual errors and individual summary
570 574 print('ERROR - %s out of %s test groups failed.' % (nfail, nrunners))
571 575 for name, failed_runner in failed:
572 576 print('-'*40)
573 577 print('Runner failed:',name)
574 578 print('You may wish to rerun this one individually, with:')
575 579 failed_call_args = [py3compat.cast_unicode(x) for x in failed_runner.call_args]
576 580 print(u' '.join(failed_call_args))
577 581 print()
578 582 # Ensure that our exit code indicates failure
579 583 sys.exit(1)
580 584
581 585
582 586 def main():
583 587 for arg in sys.argv[1:]:
584 588 if arg.startswith('IPython'):
585 589 # This is in-process
586 590 run_iptest()
587 591 else:
588 592 if "--all" in sys.argv:
589 593 sys.argv.remove("--all")
590 594 inc_slow = True
591 595 else:
592 596 inc_slow = False
593 597 # This starts subprocesses
594 598 run_iptestall(inc_slow=inc_slow)
595 599
596 600
597 601 if __name__ == '__main__':
598 602 main()
General Comments 0
You need to be logged in to leave comments. Login now