##// END OF EJS Templates
Merge pull request #4120 from minrk/input-py2...
Thomas Kluyver -
r12336:f87f983a merge
parent child Browse files
Show More
@@ -1,204 +1,252
1 1 """test the IPython Kernel"""
2 2
3 3 #-------------------------------------------------------------------------------
4 4 # Copyright (C) 2013 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 import os
15 15 import shutil
16 16 import sys
17 17 import tempfile
18 18
19 19 from contextlib import contextmanager
20 20 from subprocess import PIPE
21 21
22 22 import nose.tools as nt
23 23
24 24 from IPython.kernel import KernelManager
25 25 from IPython.kernel.tests.test_message_spec import execute, flush_channels
26 26 from IPython.testing import decorators as dec
27 from IPython.utils import path
27 from IPython.utils import path, py3compat
28 28
29 29 #-------------------------------------------------------------------------------
30 30 # Tests
31 31 #-------------------------------------------------------------------------------
32 32 IPYTHONDIR = None
33 33 save_env = None
34 34 save_get_ipython_dir = None
35 35
36 STARTUP_TIMEOUT = 60
37 TIMEOUT = 15
38
36 39 def setup():
37 40 """setup temporary IPYTHONDIR for tests"""
38 41 global IPYTHONDIR
39 42 global save_env
40 43 global save_get_ipython_dir
41 44
42 45 IPYTHONDIR = tempfile.mkdtemp()
43 46
44 47 save_env = os.environ.copy()
45 48 os.environ["IPYTHONDIR"] = IPYTHONDIR
46 49
47 50 save_get_ipython_dir = path.get_ipython_dir
48 51 path.get_ipython_dir = lambda : IPYTHONDIR
49 52
50 53
51 54 def teardown():
52 55 path.get_ipython_dir = save_get_ipython_dir
53 56 os.environ = save_env
54 57
55 58 try:
56 59 shutil.rmtree(IPYTHONDIR)
57 60 except (OSError, IOError):
58 61 # no such file
59 62 pass
60 63
61 64
62 65 @contextmanager
63 66 def new_kernel():
64 67 """start a kernel in a subprocess, and wait for it to be ready
65 68
66 69 Returns
67 70 -------
68 71 kernel_manager: connected KernelManager instance
69 72 """
70 73 KM = KernelManager()
71 74
72 75 KM.start_kernel(stdout=PIPE, stderr=PIPE)
73 76 KC = KM.client()
74 77 KC.start_channels()
75 78
76 79 # wait for kernel to be ready
77 80 KC.shell_channel.execute("import sys")
78 KC.shell_channel.get_msg(block=True, timeout=5)
81 KC.shell_channel.get_msg(block=True, timeout=STARTUP_TIMEOUT)
79 82 flush_channels(KC)
80 83 try:
81 84 yield KC
82 85 finally:
83 86 KC.stop_channels()
84 87 KM.shutdown_kernel()
85 88
86 89
87 90 def assemble_output(iopub):
88 91 """assemble stdout/err from an execution"""
89 92 stdout = ''
90 93 stderr = ''
91 94 while True:
92 95 msg = iopub.get_msg(block=True, timeout=1)
93 96 msg_type = msg['msg_type']
94 97 content = msg['content']
95 98 if msg_type == 'status' and content['execution_state'] == 'idle':
96 99 # idle message signals end of output
97 100 break
98 101 elif msg['msg_type'] == 'stream':
99 102 if content['name'] == 'stdout':
100 103 stdout = stdout + content['data']
101 104 elif content['name'] == 'stderr':
102 105 stderr = stderr + content['data']
103 106 else:
104 107 raise KeyError("bad stream: %r" % content['name'])
105 108 else:
106 109 # other output, ignored
107 110 pass
108 111 return stdout, stderr
109 112
110 113
111 114 def _check_mp_mode(kc, expected=False, stream="stdout"):
112 115 execute(kc=kc, code="import sys")
113 116 flush_channels(kc)
114 117 msg_id, content = execute(kc=kc, code="print (sys.%s._check_mp_mode())" % stream)
115 118 stdout, stderr = assemble_output(kc.iopub_channel)
116 119 nt.assert_equal(eval(stdout.strip()), expected)
117 120
118 121
122 # printing tests
123
119 124 def test_simple_print():
120 125 """simple print statement in kernel"""
121 126 with new_kernel() as kc:
122 127 iopub = kc.iopub_channel
123 128 msg_id, content = execute(kc=kc, code="print ('hi')")
124 129 stdout, stderr = assemble_output(iopub)
125 130 nt.assert_equal(stdout, 'hi\n')
126 131 nt.assert_equal(stderr, '')
127 132 _check_mp_mode(kc, expected=False)
128 print ('hello')
129 133
130 134
131 135 @dec.knownfailureif(sys.platform == 'win32', "subprocess prints fail on Windows")
132 136 def test_subprocess_print():
133 137 """printing from forked mp.Process"""
134 138 with new_kernel() as kc:
135 139 iopub = kc.iopub_channel
136 140
137 141 _check_mp_mode(kc, expected=False)
138 142 flush_channels(kc)
139 143 np = 5
140 144 code = '\n'.join([
141 145 "from __future__ import print_function",
142 146 "import multiprocessing as mp",
143 147 "pool = [mp.Process(target=print, args=('hello', i,)) for i in range(%i)]" % np,
144 148 "for p in pool: p.start()",
145 149 "for p in pool: p.join()"
146 150 ])
147 151
148 152 expected = '\n'.join([
149 153 "hello %s" % i for i in range(np)
150 154 ]) + '\n'
151 155
152 156 msg_id, content = execute(kc=kc, code=code)
153 157 stdout, stderr = assemble_output(iopub)
154 158 nt.assert_equal(stdout.count("hello"), np, stdout)
155 159 for n in range(np):
156 160 nt.assert_equal(stdout.count(str(n)), 1, stdout)
157 161 nt.assert_equal(stderr, '')
158 162 _check_mp_mode(kc, expected=False)
159 163 _check_mp_mode(kc, expected=False, stream="stderr")
160 164
161 165
162 166 def test_subprocess_noprint():
163 167 """mp.Process without print doesn't trigger iostream mp_mode"""
164 168 with new_kernel() as kc:
165 169 iopub = kc.iopub_channel
166 170
167 171 np = 5
168 172 code = '\n'.join([
169 173 "import multiprocessing as mp",
170 174 "pool = [mp.Process(target=range, args=(i,)) for i in range(%i)]" % np,
171 175 "for p in pool: p.start()",
172 176 "for p in pool: p.join()"
173 177 ])
174 178
175 179 msg_id, content = execute(kc=kc, code=code)
176 180 stdout, stderr = assemble_output(iopub)
177 181 nt.assert_equal(stdout, '')
178 182 nt.assert_equal(stderr, '')
179 183
180 184 _check_mp_mode(kc, expected=False)
181 185 _check_mp_mode(kc, expected=False, stream="stderr")
182 186
183 187
184 188 @dec.knownfailureif(sys.platform == 'win32', "subprocess prints fail on Windows")
185 189 def test_subprocess_error():
186 190 """error in mp.Process doesn't crash"""
187 191 with new_kernel() as kc:
188 192 iopub = kc.iopub_channel
189 193
190 194 code = '\n'.join([
191 195 "import multiprocessing as mp",
192 196 "p = mp.Process(target=int, args=('hi',))",
193 197 "p.start()",
194 198 "p.join()",
195 199 ])
196 200
197 201 msg_id, content = execute(kc=kc, code=code)
198 202 stdout, stderr = assemble_output(iopub)
199 203 nt.assert_equal(stdout, '')
200 204 nt.assert_true("ValueError" in stderr, stderr)
201 205
202 206 _check_mp_mode(kc, expected=False)
203 207 _check_mp_mode(kc, expected=False, stream="stderr")
204 208
209
210 # raw_input tests
211
212 def test_raw_input():
213 """test [raw_]input"""
214 with new_kernel() as kc:
215 iopub = kc.iopub_channel
216
217 input_f = "input" if py3compat.PY3 else "raw_input"
218 theprompt = "prompt> "
219 code = 'print({input_f}("{theprompt}"))'.format(**locals())
220 msg_id = kc.execute(code, allow_stdin=True)
221 msg = kc.get_stdin_msg(block=True, timeout=TIMEOUT)
222 nt.assert_equal(msg['header']['msg_type'], u'input_request')
223 content = msg['content']
224 nt.assert_equal(content['prompt'], theprompt)
225 text = "some text"
226 kc.input(text)
227 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
228 nt.assert_equal(reply['content']['status'], 'ok')
229 stdout, stderr = assemble_output(iopub)
230 nt.assert_equal(stdout, text + "\n")
231
232
233 @dec.skipif(py3compat.PY3)
234 def test_eval_input():
235 """test input() on Python 2"""
236 with new_kernel() as kc:
237 iopub = kc.iopub_channel
238
239 input_f = "input" if py3compat.PY3 else "raw_input"
240 theprompt = "prompt> "
241 code = 'print(input("{theprompt}"))'.format(**locals())
242 msg_id = kc.execute(code, allow_stdin=True)
243 msg = kc.get_stdin_msg(block=True, timeout=TIMEOUT)
244 nt.assert_equal(msg['header']['msg_type'], u'input_request')
245 content = msg['content']
246 nt.assert_equal(content['prompt'], theprompt)
247 kc.input("1+1")
248 reply = kc.get_shell_msg(block=True, timeout=TIMEOUT)
249 nt.assert_equal(reply['content']['status'], 'ok')
250 stdout, stderr = assemble_output(iopub)
251 nt.assert_equal(stdout, "2\n")
252
@@ -1,806 +1,811
1 1 #!/usr/bin/env python
2 2 """A simple interactive kernel that talks to a frontend over 0MQ.
3 3
4 4 Things to do:
5 5
6 6 * Implement `set_parent` logic. Right before doing exec, the Kernel should
7 7 call set_parent on all the PUB objects with the message about to be executed.
8 8 * Implement random port and security key logic.
9 9 * Implement control messages.
10 10 * Implement event loop and poll version.
11 11 """
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16 from __future__ import print_function
17 17
18 18 # Standard library imports
19 19 import __builtin__
20 20 import sys
21 21 import time
22 22 import traceback
23 23 import logging
24 24 import uuid
25 25
26 26 from datetime import datetime
27 27 from signal import (
28 28 signal, default_int_handler, SIGINT
29 29 )
30 30
31 31 # System library imports
32 32 import zmq
33 33 from zmq.eventloop import ioloop
34 34 from zmq.eventloop.zmqstream import ZMQStream
35 35
36 36 # Local imports
37 37 from IPython.config.configurable import Configurable
38 38 from IPython.core.error import StdinNotImplementedError
39 39 from IPython.core import release
40 40 from IPython.utils import py3compat
41 41 from IPython.utils.jsonutil import json_clean
42 42 from IPython.utils.traitlets import (
43 43 Any, Instance, Float, Dict, List, Set, Integer, Unicode,
44 44 Type
45 45 )
46 46
47 47 from serialize import serialize_object, unpack_apply_message
48 48 from session import Session
49 49 from zmqshell import ZMQInteractiveShell
50 50
51 51
52 52 #-----------------------------------------------------------------------------
53 53 # Main kernel class
54 54 #-----------------------------------------------------------------------------
55 55
56 56 protocol_version = list(release.kernel_protocol_version_info)
57 57 ipython_version = list(release.version_info)
58 58 language_version = list(sys.version_info[:3])
59 59
60 60
61 61 class Kernel(Configurable):
62 62
63 63 #---------------------------------------------------------------------------
64 64 # Kernel interface
65 65 #---------------------------------------------------------------------------
66 66
67 67 # attribute to override with a GUI
68 68 eventloop = Any(None)
69 69 def _eventloop_changed(self, name, old, new):
70 70 """schedule call to eventloop from IOLoop"""
71 71 loop = ioloop.IOLoop.instance()
72 72 loop.add_timeout(time.time()+0.1, self.enter_eventloop)
73 73
74 74 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
75 75 shell_class = Type(ZMQInteractiveShell)
76 76
77 77 session = Instance(Session)
78 78 profile_dir = Instance('IPython.core.profiledir.ProfileDir')
79 79 shell_streams = List()
80 80 control_stream = Instance(ZMQStream)
81 81 iopub_socket = Instance(zmq.Socket)
82 82 stdin_socket = Instance(zmq.Socket)
83 83 log = Instance(logging.Logger)
84 84
85 85 user_module = Any()
86 86 def _user_module_changed(self, name, old, new):
87 87 if self.shell is not None:
88 88 self.shell.user_module = new
89 89
90 90 user_ns = Dict(default_value=None)
91 91 def _user_ns_changed(self, name, old, new):
92 92 if self.shell is not None:
93 93 self.shell.user_ns = new
94 94 self.shell.init_user_ns()
95 95
96 96 # identities:
97 97 int_id = Integer(-1)
98 98 ident = Unicode()
99 99
100 100 def _ident_default(self):
101 101 return unicode(uuid.uuid4())
102 102
103 103
104 104 # Private interface
105 105
106 106 # Time to sleep after flushing the stdout/err buffers in each execute
107 107 # cycle. While this introduces a hard limit on the minimal latency of the
108 108 # execute cycle, it helps prevent output synchronization problems for
109 109 # clients.
110 110 # Units are in seconds. The minimum zmq latency on local host is probably
111 111 # ~150 microseconds, set this to 500us for now. We may need to increase it
112 112 # a little if it's not enough after more interactive testing.
113 113 _execute_sleep = Float(0.0005, config=True)
114 114
115 115 # Frequency of the kernel's event loop.
116 116 # Units are in seconds, kernel subclasses for GUI toolkits may need to
117 117 # adapt to milliseconds.
118 118 _poll_interval = Float(0.05, config=True)
119 119
120 120 # If the shutdown was requested over the network, we leave here the
121 121 # necessary reply message so it can be sent by our registered atexit
122 122 # handler. This ensures that the reply is only sent to clients truly at
123 123 # the end of our shutdown process (which happens after the underlying
124 124 # IPython shell's own shutdown).
125 125 _shutdown_message = None
126 126
127 127 # This is a dict of port number that the kernel is listening on. It is set
128 128 # by record_ports and used by connect_request.
129 129 _recorded_ports = Dict()
130 130
131 131 # A reference to the Python builtin 'raw_input' function.
132 132 # (i.e., __builtin__.raw_input for Python 2.7, builtins.input for Python 3)
133 133 _sys_raw_input = Any()
134 _sys_eval_input = Any()
134 135
135 136 # set of aborted msg_ids
136 137 aborted = Set()
137 138
138 139
139 140 def __init__(self, **kwargs):
140 141 super(Kernel, self).__init__(**kwargs)
141 142
142 143 # Initialize the InteractiveShell subclass
143 144 self.shell = self.shell_class.instance(parent=self,
144 145 profile_dir = self.profile_dir,
145 146 user_module = self.user_module,
146 147 user_ns = self.user_ns,
147 148 )
148 149 self.shell.displayhook.session = self.session
149 150 self.shell.displayhook.pub_socket = self.iopub_socket
150 151 self.shell.displayhook.topic = self._topic('pyout')
151 152 self.shell.display_pub.session = self.session
152 153 self.shell.display_pub.pub_socket = self.iopub_socket
153 154 self.shell.data_pub.session = self.session
154 155 self.shell.data_pub.pub_socket = self.iopub_socket
155 156
156 157 # TMP - hack while developing
157 158 self.shell._reply_content = None
158 159
159 160 # Build dict of handlers for message types
160 161 msg_types = [ 'execute_request', 'complete_request',
161 162 'object_info_request', 'history_request',
162 163 'kernel_info_request',
163 164 'connect_request', 'shutdown_request',
164 165 'apply_request',
165 166 ]
166 167 self.shell_handlers = {}
167 168 for msg_type in msg_types:
168 169 self.shell_handlers[msg_type] = getattr(self, msg_type)
169 170
170 171 control_msg_types = msg_types + [ 'clear_request', 'abort_request' ]
171 172 self.control_handlers = {}
172 173 for msg_type in control_msg_types:
173 174 self.control_handlers[msg_type] = getattr(self, msg_type)
174 175
175 176 def dispatch_control(self, msg):
176 177 """dispatch control requests"""
177 178 idents,msg = self.session.feed_identities(msg, copy=False)
178 179 try:
179 180 msg = self.session.unserialize(msg, content=True, copy=False)
180 181 except:
181 182 self.log.error("Invalid Control Message", exc_info=True)
182 183 return
183 184
184 185 self.log.debug("Control received: %s", msg)
185 186
186 187 header = msg['header']
187 188 msg_id = header['msg_id']
188 189 msg_type = header['msg_type']
189 190
190 191 handler = self.control_handlers.get(msg_type, None)
191 192 if handler is None:
192 193 self.log.error("UNKNOWN CONTROL MESSAGE TYPE: %r", msg_type)
193 194 else:
194 195 try:
195 196 handler(self.control_stream, idents, msg)
196 197 except Exception:
197 198 self.log.error("Exception in control handler:", exc_info=True)
198 199
199 200 def dispatch_shell(self, stream, msg):
200 201 """dispatch shell requests"""
201 202 # flush control requests first
202 203 if self.control_stream:
203 204 self.control_stream.flush()
204 205
205 206 idents,msg = self.session.feed_identities(msg, copy=False)
206 207 try:
207 208 msg = self.session.unserialize(msg, content=True, copy=False)
208 209 except:
209 210 self.log.error("Invalid Message", exc_info=True)
210 211 return
211 212
212 213 header = msg['header']
213 214 msg_id = header['msg_id']
214 215 msg_type = msg['header']['msg_type']
215 216
216 217 # Print some info about this message and leave a '--->' marker, so it's
217 218 # easier to trace visually the message chain when debugging. Each
218 219 # handler prints its message at the end.
219 220 self.log.debug('\n*** MESSAGE TYPE:%s***', msg_type)
220 221 self.log.debug(' Content: %s\n --->\n ', msg['content'])
221 222
222 223 if msg_id in self.aborted:
223 224 self.aborted.remove(msg_id)
224 225 # is it safe to assume a msg_id will not be resubmitted?
225 226 reply_type = msg_type.split('_')[0] + '_reply'
226 227 status = {'status' : 'aborted'}
227 228 md = {'engine' : self.ident}
228 229 md.update(status)
229 230 reply_msg = self.session.send(stream, reply_type, metadata=md,
230 231 content=status, parent=msg, ident=idents)
231 232 return
232 233
233 234 handler = self.shell_handlers.get(msg_type, None)
234 235 if handler is None:
235 236 self.log.error("UNKNOWN MESSAGE TYPE: %r", msg_type)
236 237 else:
237 238 # ensure default_int_handler during handler call
238 239 sig = signal(SIGINT, default_int_handler)
239 240 try:
240 241 handler(stream, idents, msg)
241 242 except Exception:
242 243 self.log.error("Exception in message handler:", exc_info=True)
243 244 finally:
244 245 signal(SIGINT, sig)
245 246
246 247 def enter_eventloop(self):
247 248 """enter eventloop"""
248 249 self.log.info("entering eventloop")
249 250 # restore default_int_handler
250 251 signal(SIGINT, default_int_handler)
251 252 while self.eventloop is not None:
252 253 try:
253 254 self.eventloop(self)
254 255 except KeyboardInterrupt:
255 256 # Ctrl-C shouldn't crash the kernel
256 257 self.log.error("KeyboardInterrupt caught in kernel")
257 258 continue
258 259 else:
259 260 # eventloop exited cleanly, this means we should stop (right?)
260 261 self.eventloop = None
261 262 break
262 263 self.log.info("exiting eventloop")
263 264
264 265 def start(self):
265 266 """register dispatchers for streams"""
266 267 self.shell.exit_now = False
267 268 if self.control_stream:
268 269 self.control_stream.on_recv(self.dispatch_control, copy=False)
269 270
270 271 def make_dispatcher(stream):
271 272 def dispatcher(msg):
272 273 return self.dispatch_shell(stream, msg)
273 274 return dispatcher
274 275
275 276 for s in self.shell_streams:
276 277 s.on_recv(make_dispatcher(s), copy=False)
277 278
278 279 # publish idle status
279 280 self._publish_status('starting')
280 281
281 282 def do_one_iteration(self):
282 283 """step eventloop just once"""
283 284 if self.control_stream:
284 285 self.control_stream.flush()
285 286 for stream in self.shell_streams:
286 287 # handle at most one request per iteration
287 288 stream.flush(zmq.POLLIN, 1)
288 289 stream.flush(zmq.POLLOUT)
289 290
290 291
291 292 def record_ports(self, ports):
292 293 """Record the ports that this kernel is using.
293 294
294 295 The creator of the Kernel instance must call this methods if they
295 296 want the :meth:`connect_request` method to return the port numbers.
296 297 """
297 298 self._recorded_ports = ports
298 299
299 300 #---------------------------------------------------------------------------
300 301 # Kernel request handlers
301 302 #---------------------------------------------------------------------------
302 303
303 304 def _make_metadata(self, other=None):
304 305 """init metadata dict, for execute/apply_reply"""
305 306 new_md = {
306 307 'dependencies_met' : True,
307 308 'engine' : self.ident,
308 309 'started': datetime.now(),
309 310 }
310 311 if other:
311 312 new_md.update(other)
312 313 return new_md
313 314
314 315 def _publish_pyin(self, code, parent, execution_count):
315 316 """Publish the code request on the pyin stream."""
316 317
317 318 self.session.send(self.iopub_socket, u'pyin',
318 319 {u'code':code, u'execution_count': execution_count},
319 320 parent=parent, ident=self._topic('pyin')
320 321 )
321 322
322 323 def _publish_status(self, status, parent=None):
323 324 """send status (busy/idle) on IOPub"""
324 325 self.session.send(self.iopub_socket,
325 326 u'status',
326 327 {u'execution_state': status},
327 328 parent=parent,
328 329 ident=self._topic('status'),
329 330 )
330 331
331 332
332 333 def execute_request(self, stream, ident, parent):
333 334 """handle an execute_request"""
334 335
335 336 self._publish_status(u'busy', parent)
336 337
337 338 try:
338 339 content = parent[u'content']
339 340 code = content[u'code']
340 341 silent = content[u'silent']
341 342 store_history = content.get(u'store_history', not silent)
342 343 except:
343 344 self.log.error("Got bad msg: ")
344 345 self.log.error("%s", parent)
345 346 return
346 347
347 348 md = self._make_metadata(parent['metadata'])
348 349
349 350 shell = self.shell # we'll need this a lot here
350 351
351 352 # Replace raw_input. Note that is not sufficient to replace
352 353 # raw_input in the user namespace.
353 354 if content.get('allow_stdin', False):
354 355 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
356 input = lambda prompt='': eval(raw_input(prompt))
355 357 else:
356 raw_input = lambda prompt='' : self._no_raw_input()
358 raw_input = input = lambda prompt='' : self._no_raw_input()
357 359
358 360 if py3compat.PY3:
359 361 self._sys_raw_input = __builtin__.input
360 362 __builtin__.input = raw_input
361 363 else:
362 364 self._sys_raw_input = __builtin__.raw_input
365 self._sys_eval_input = __builtin__.input
363 366 __builtin__.raw_input = raw_input
367 __builtin__.input = input
364 368
365 369 # Set the parent message of the display hook and out streams.
366 370 shell.displayhook.set_parent(parent)
367 371 shell.display_pub.set_parent(parent)
368 372 shell.data_pub.set_parent(parent)
369 373 try:
370 374 sys.stdout.set_parent(parent)
371 375 except AttributeError:
372 376 pass
373 377 try:
374 378 sys.stderr.set_parent(parent)
375 379 except AttributeError:
376 380 pass
377 381
378 382 # Re-broadcast our input for the benefit of listening clients, and
379 383 # start computing output
380 384 if not silent:
381 385 self._publish_pyin(code, parent, shell.execution_count)
382 386
383 387 reply_content = {}
384 388 try:
385 389 # FIXME: the shell calls the exception handler itself.
386 390 shell.run_cell(code, store_history=store_history, silent=silent)
387 391 except:
388 392 status = u'error'
389 393 # FIXME: this code right now isn't being used yet by default,
390 394 # because the run_cell() call above directly fires off exception
391 395 # reporting. This code, therefore, is only active in the scenario
392 396 # where runlines itself has an unhandled exception. We need to
393 397 # uniformize this, for all exception construction to come from a
394 398 # single location in the codbase.
395 399 etype, evalue, tb = sys.exc_info()
396 400 tb_list = traceback.format_exception(etype, evalue, tb)
397 401 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
398 402 else:
399 403 status = u'ok'
400 404 finally:
401 405 # Restore raw_input.
402 406 if py3compat.PY3:
403 407 __builtin__.input = self._sys_raw_input
404 408 else:
405 409 __builtin__.raw_input = self._sys_raw_input
410 __builtin__.input = self._sys_eval_input
406 411
407 412 reply_content[u'status'] = status
408 413
409 414 # Return the execution counter so clients can display prompts
410 415 reply_content['execution_count'] = shell.execution_count - 1
411 416
412 417 # FIXME - fish exception info out of shell, possibly left there by
413 418 # runlines. We'll need to clean up this logic later.
414 419 if shell._reply_content is not None:
415 420 reply_content.update(shell._reply_content)
416 421 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='execute')
417 422 reply_content['engine_info'] = e_info
418 423 # reset after use
419 424 shell._reply_content = None
420 425
421 426 if 'traceback' in reply_content:
422 427 self.log.info("Exception in execute request:\n%s", '\n'.join(reply_content['traceback']))
423 428
424 429
425 430 # At this point, we can tell whether the main code execution succeeded
426 431 # or not. If it did, we proceed to evaluate user_variables/expressions
427 432 if reply_content['status'] == 'ok':
428 433 reply_content[u'user_variables'] = \
429 434 shell.user_variables(content.get(u'user_variables', []))
430 435 reply_content[u'user_expressions'] = \
431 436 shell.user_expressions(content.get(u'user_expressions', {}))
432 437 else:
433 438 # If there was an error, don't even try to compute variables or
434 439 # expressions
435 440 reply_content[u'user_variables'] = {}
436 441 reply_content[u'user_expressions'] = {}
437 442
438 443 # Payloads should be retrieved regardless of outcome, so we can both
439 444 # recover partial output (that could have been generated early in a
440 445 # block, before an error) and clear the payload system always.
441 446 reply_content[u'payload'] = shell.payload_manager.read_payload()
442 447 # Be agressive about clearing the payload because we don't want
443 448 # it to sit in memory until the next execute_request comes in.
444 449 shell.payload_manager.clear_payload()
445 450
446 451 # Flush output before sending the reply.
447 452 sys.stdout.flush()
448 453 sys.stderr.flush()
449 454 # FIXME: on rare occasions, the flush doesn't seem to make it to the
450 455 # clients... This seems to mitigate the problem, but we definitely need
451 456 # to better understand what's going on.
452 457 if self._execute_sleep:
453 458 time.sleep(self._execute_sleep)
454 459
455 460 # Send the reply.
456 461 reply_content = json_clean(reply_content)
457 462
458 463 md['status'] = reply_content['status']
459 464 if reply_content['status'] == 'error' and \
460 465 reply_content['ename'] == 'UnmetDependency':
461 466 md['dependencies_met'] = False
462 467
463 468 reply_msg = self.session.send(stream, u'execute_reply',
464 469 reply_content, parent, metadata=md,
465 470 ident=ident)
466 471
467 472 self.log.debug("%s", reply_msg)
468 473
469 474 if not silent and reply_msg['content']['status'] == u'error':
470 475 self._abort_queues()
471 476
472 477 self._publish_status(u'idle', parent)
473 478
474 479 def complete_request(self, stream, ident, parent):
475 480 txt, matches = self._complete(parent)
476 481 matches = {'matches' : matches,
477 482 'matched_text' : txt,
478 483 'status' : 'ok'}
479 484 matches = json_clean(matches)
480 485 completion_msg = self.session.send(stream, 'complete_reply',
481 486 matches, parent, ident)
482 487 self.log.debug("%s", completion_msg)
483 488
484 489 def object_info_request(self, stream, ident, parent):
485 490 content = parent['content']
486 491 object_info = self.shell.object_inspect(content['oname'],
487 492 detail_level = content.get('detail_level', 0)
488 493 )
489 494 # Before we send this object over, we scrub it for JSON usage
490 495 oinfo = json_clean(object_info)
491 496 msg = self.session.send(stream, 'object_info_reply',
492 497 oinfo, parent, ident)
493 498 self.log.debug("%s", msg)
494 499
495 500 def history_request(self, stream, ident, parent):
496 501 # We need to pull these out, as passing **kwargs doesn't work with
497 502 # unicode keys before Python 2.6.5.
498 503 hist_access_type = parent['content']['hist_access_type']
499 504 raw = parent['content']['raw']
500 505 output = parent['content']['output']
501 506 if hist_access_type == 'tail':
502 507 n = parent['content']['n']
503 508 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
504 509 include_latest=True)
505 510
506 511 elif hist_access_type == 'range':
507 512 session = parent['content']['session']
508 513 start = parent['content']['start']
509 514 stop = parent['content']['stop']
510 515 hist = self.shell.history_manager.get_range(session, start, stop,
511 516 raw=raw, output=output)
512 517
513 518 elif hist_access_type == 'search':
514 519 n = parent['content'].get('n')
515 520 unique = parent['content'].get('unique', False)
516 521 pattern = parent['content']['pattern']
517 522 hist = self.shell.history_manager.search(
518 523 pattern, raw=raw, output=output, n=n, unique=unique)
519 524
520 525 else:
521 526 hist = []
522 527 hist = list(hist)
523 528 content = {'history' : hist}
524 529 content = json_clean(content)
525 530 msg = self.session.send(stream, 'history_reply',
526 531 content, parent, ident)
527 532 self.log.debug("Sending history reply with %i entries", len(hist))
528 533
529 534 def connect_request(self, stream, ident, parent):
530 535 if self._recorded_ports is not None:
531 536 content = self._recorded_ports.copy()
532 537 else:
533 538 content = {}
534 539 msg = self.session.send(stream, 'connect_reply',
535 540 content, parent, ident)
536 541 self.log.debug("%s", msg)
537 542
538 543 def kernel_info_request(self, stream, ident, parent):
539 544 vinfo = {
540 545 'protocol_version': protocol_version,
541 546 'ipython_version': ipython_version,
542 547 'language_version': language_version,
543 548 'language': 'python',
544 549 }
545 550 msg = self.session.send(stream, 'kernel_info_reply',
546 551 vinfo, parent, ident)
547 552 self.log.debug("%s", msg)
548 553
549 554 def shutdown_request(self, stream, ident, parent):
550 555 self.shell.exit_now = True
551 556 content = dict(status='ok')
552 557 content.update(parent['content'])
553 558 self.session.send(stream, u'shutdown_reply', content, parent, ident=ident)
554 559 # same content, but different msg_id for broadcasting on IOPub
555 560 self._shutdown_message = self.session.msg(u'shutdown_reply',
556 561 content, parent
557 562 )
558 563
559 564 self._at_shutdown()
560 565 # call sys.exit after a short delay
561 566 loop = ioloop.IOLoop.instance()
562 567 loop.add_timeout(time.time()+0.1, loop.stop)
563 568
564 569 #---------------------------------------------------------------------------
565 570 # Engine methods
566 571 #---------------------------------------------------------------------------
567 572
568 573 def apply_request(self, stream, ident, parent):
569 574 try:
570 575 content = parent[u'content']
571 576 bufs = parent[u'buffers']
572 577 msg_id = parent['header']['msg_id']
573 578 except:
574 579 self.log.error("Got bad msg: %s", parent, exc_info=True)
575 580 return
576 581
577 582 self._publish_status(u'busy', parent)
578 583
579 584 # Set the parent message of the display hook and out streams.
580 585 shell = self.shell
581 586 shell.displayhook.set_parent(parent)
582 587 shell.display_pub.set_parent(parent)
583 588 shell.data_pub.set_parent(parent)
584 589 try:
585 590 sys.stdout.set_parent(parent)
586 591 except AttributeError:
587 592 pass
588 593 try:
589 594 sys.stderr.set_parent(parent)
590 595 except AttributeError:
591 596 pass
592 597
593 598 # pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
594 599 # self.iopub_socket.send(pyin_msg)
595 600 # self.session.send(self.iopub_socket, u'pyin', {u'code':code},parent=parent)
596 601 md = self._make_metadata(parent['metadata'])
597 602 try:
598 603 working = shell.user_ns
599 604
600 605 prefix = "_"+str(msg_id).replace("-","")+"_"
601 606
602 607 f,args,kwargs = unpack_apply_message(bufs, working, copy=False)
603 608
604 609 fname = getattr(f, '__name__', 'f')
605 610
606 611 fname = prefix+"f"
607 612 argname = prefix+"args"
608 613 kwargname = prefix+"kwargs"
609 614 resultname = prefix+"result"
610 615
611 616 ns = { fname : f, argname : args, kwargname : kwargs , resultname : None }
612 617 # print ns
613 618 working.update(ns)
614 619 code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname)
615 620 try:
616 621 exec code in shell.user_global_ns, shell.user_ns
617 622 result = working.get(resultname)
618 623 finally:
619 624 for key in ns.iterkeys():
620 625 working.pop(key)
621 626
622 627 result_buf = serialize_object(result,
623 628 buffer_threshold=self.session.buffer_threshold,
624 629 item_threshold=self.session.item_threshold,
625 630 )
626 631
627 632 except:
628 633 # invoke IPython traceback formatting
629 634 shell.showtraceback()
630 635 # FIXME - fish exception info out of shell, possibly left there by
631 636 # run_code. We'll need to clean up this logic later.
632 637 reply_content = {}
633 638 if shell._reply_content is not None:
634 639 reply_content.update(shell._reply_content)
635 640 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='apply')
636 641 reply_content['engine_info'] = e_info
637 642 # reset after use
638 643 shell._reply_content = None
639 644
640 645 self.session.send(self.iopub_socket, u'pyerr', reply_content, parent=parent,
641 646 ident=self._topic('pyerr'))
642 647 self.log.info("Exception in apply request:\n%s", '\n'.join(reply_content['traceback']))
643 648 result_buf = []
644 649
645 650 if reply_content['ename'] == 'UnmetDependency':
646 651 md['dependencies_met'] = False
647 652 else:
648 653 reply_content = {'status' : 'ok'}
649 654
650 655 # put 'ok'/'error' status in header, for scheduler introspection:
651 656 md['status'] = reply_content['status']
652 657
653 658 # flush i/o
654 659 sys.stdout.flush()
655 660 sys.stderr.flush()
656 661
657 662 reply_msg = self.session.send(stream, u'apply_reply', reply_content,
658 663 parent=parent, ident=ident,buffers=result_buf, metadata=md)
659 664
660 665 self._publish_status(u'idle', parent)
661 666
662 667 #---------------------------------------------------------------------------
663 668 # Control messages
664 669 #---------------------------------------------------------------------------
665 670
666 671 def abort_request(self, stream, ident, parent):
667 672 """abort a specifig msg by id"""
668 673 msg_ids = parent['content'].get('msg_ids', None)
669 674 if isinstance(msg_ids, basestring):
670 675 msg_ids = [msg_ids]
671 676 if not msg_ids:
672 677 self.abort_queues()
673 678 for mid in msg_ids:
674 679 self.aborted.add(str(mid))
675 680
676 681 content = dict(status='ok')
677 682 reply_msg = self.session.send(stream, 'abort_reply', content=content,
678 683 parent=parent, ident=ident)
679 684 self.log.debug("%s", reply_msg)
680 685
681 686 def clear_request(self, stream, idents, parent):
682 687 """Clear our namespace."""
683 688 self.shell.reset(False)
684 689 msg = self.session.send(stream, 'clear_reply', ident=idents, parent=parent,
685 690 content = dict(status='ok'))
686 691
687 692
688 693 #---------------------------------------------------------------------------
689 694 # Protected interface
690 695 #---------------------------------------------------------------------------
691 696
692 697 def _wrap_exception(self, method=None):
693 698 # import here, because _wrap_exception is only used in parallel,
694 699 # and parallel has higher min pyzmq version
695 700 from IPython.parallel.error import wrap_exception
696 701 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method=method)
697 702 content = wrap_exception(e_info)
698 703 return content
699 704
700 705 def _topic(self, topic):
701 706 """prefixed topic for IOPub messages"""
702 707 if self.int_id >= 0:
703 708 base = "engine.%i" % self.int_id
704 709 else:
705 710 base = "kernel.%s" % self.ident
706 711
707 712 return py3compat.cast_bytes("%s.%s" % (base, topic))
708 713
709 714 def _abort_queues(self):
710 715 for stream in self.shell_streams:
711 716 if stream:
712 717 self._abort_queue(stream)
713 718
714 719 def _abort_queue(self, stream):
715 720 poller = zmq.Poller()
716 721 poller.register(stream.socket, zmq.POLLIN)
717 722 while True:
718 723 idents,msg = self.session.recv(stream, zmq.NOBLOCK, content=True)
719 724 if msg is None:
720 725 return
721 726
722 727 self.log.info("Aborting:")
723 728 self.log.info("%s", msg)
724 729 msg_type = msg['header']['msg_type']
725 730 reply_type = msg_type.split('_')[0] + '_reply'
726 731
727 732 status = {'status' : 'aborted'}
728 733 md = {'engine' : self.ident}
729 734 md.update(status)
730 735 reply_msg = self.session.send(stream, reply_type, metadata=md,
731 736 content=status, parent=msg, ident=idents)
732 737 self.log.debug("%s", reply_msg)
733 738 # We need to wait a bit for requests to come in. This can probably
734 739 # be set shorter for true asynchronous clients.
735 740 poller.poll(50)
736 741
737 742
738 743 def _no_raw_input(self):
739 744 """Raise StdinNotImplentedError if active frontend doesn't support
740 745 stdin."""
741 746 raise StdinNotImplementedError("raw_input was called, but this "
742 747 "frontend does not support stdin.")
743 748
744 749 def _raw_input(self, prompt, ident, parent):
745 750 # Flush output before making the request.
746 751 sys.stderr.flush()
747 752 sys.stdout.flush()
748 753 # flush the stdin socket, to purge stale replies
749 754 while True:
750 755 try:
751 756 self.stdin_socket.recv_multipart(zmq.NOBLOCK)
752 757 except zmq.ZMQError as e:
753 758 if e.errno == zmq.EAGAIN:
754 759 break
755 760 else:
756 761 raise
757 762
758 763 # Send the input request.
759 764 content = json_clean(dict(prompt=prompt))
760 765 self.session.send(self.stdin_socket, u'input_request', content, parent,
761 766 ident=ident)
762 767
763 768 # Await a response.
764 769 while True:
765 770 try:
766 771 ident, reply = self.session.recv(self.stdin_socket, 0)
767 772 except Exception:
768 773 self.log.warn("Invalid Message:", exc_info=True)
769 774 except KeyboardInterrupt:
770 775 # re-raise KeyboardInterrupt, to truncate traceback
771 776 raise KeyboardInterrupt
772 777 else:
773 778 break
774 779 try:
775 780 value = py3compat.unicode_to_str(reply['content']['value'])
776 781 except:
777 782 self.log.error("Got bad raw_input reply: ")
778 783 self.log.error("%s", parent)
779 784 value = ''
780 785 if value == '\x04':
781 786 # EOF
782 787 raise EOFError
783 788 return value
784 789
785 790 def _complete(self, msg):
786 791 c = msg['content']
787 792 try:
788 793 cpos = int(c['cursor_pos'])
789 794 except:
790 795 # If we don't get something that we can convert to an integer, at
791 796 # least attempt the completion guessing the cursor is at the end of
792 797 # the text, if there's any, and otherwise of the line
793 798 cpos = len(c['text'])
794 799 if cpos==0:
795 800 cpos = len(c['line'])
796 801 return self.shell.complete(c['text'], c['line'], cpos)
797 802
798 803 def _at_shutdown(self):
799 804 """Actions taken at shutdown by the kernel, called by python's atexit.
800 805 """
801 806 # io.rprint("Kernel at_shutdown") # dbg
802 807 if self._shutdown_message is not None:
803 808 self.session.send(self.iopub_socket, self._shutdown_message, ident=self._topic('shutdown'))
804 809 self.log.debug("%s", self._shutdown_message)
805 810 [ s.flush(zmq.POLLOUT) for s in self.shell_streams ]
806 811
@@ -1,271 +1,322
1 1 {
2 2 "metadata": {
3 "name": "Frontend-Kernel Model"
3 "name": ""
4 4 },
5 5 "nbformat": 3,
6 6 "nbformat_minor": 0,
7 7 "worksheets": [
8 8 {
9 9 "cells": [
10 10 {
11 11 "cell_type": "heading",
12 12 "level": 1,
13 13 "metadata": {},
14 14 "source": [
15 15 "The Frontend/Kernel Model"
16 16 ]
17 17 },
18 18 {
19 19 "cell_type": "markdown",
20 20 "metadata": {},
21 21 "source": [
22 22 "The traditional IPython (`ipython`) consists of a single process that combines a terminal based UI with the process that runs the users code.\n",
23 23 "\n",
24 24 "While this traditional application still exists, the modern IPython consists of two processes:\n",
25 25 "\n",
26 26 "* Kernel: this is the process that runs the users code.\n",
27 27 "* Frontend: this is the process that provides the user interface where the user types code and sees results.\n",
28 28 "\n",
29 29 "IPython currently has 3 frontends:\n",
30 30 "\n",
31 31 "* Terminal Console (`ipython console`)\n",
32 32 "* Qt Console (`ipython qtconsole`)\n",
33 33 "* Notebook (`ipython notebook`)\n",
34 34 "\n",
35 35 "The Kernel and Frontend communicate over a ZeroMQ/JSON based messaging protocol, which allows multiple Frontends (even of different types) to communicate with a single Kernel. This opens the door for all sorts of interesting things, such as connecting a Console or Qt Console to a Notebook's Kernel. For example, you may want to connect a Qt console to your Notebook's Kernel and use it as a help\n",
36 36 "browser, calling `??` on objects in the Qt console (whose pager is more flexible than the\n",
37 37 "one in the notebook). \n",
38 38 "\n",
39 39 "This Notebook describes how you would connect another Frontend to a Kernel that is associated with a Notebook."
40 40 ]
41 41 },
42 42 {
43 43 "cell_type": "heading",
44 44 "level": 2,
45 45 "metadata": {},
46 46 "source": [
47 47 "Manual connection"
48 48 ]
49 49 },
50 50 {
51 51 "cell_type": "markdown",
52 52 "metadata": {},
53 53 "source": [
54 54 "To connect another Frontend to a Kernel manually, you first need to find out the connection information for the Kernel using the `%connect_info` magic:"
55 55 ]
56 56 },
57 57 {
58 58 "cell_type": "code",
59 59 "collapsed": false,
60 60 "input": [
61 61 "%connect_info"
62 62 ],
63 63 "language": "python",
64 64 "metadata": {},
65 65 "outputs": [
66 66 {
67 67 "output_type": "stream",
68 68 "stream": "stdout",
69 69 "text": [
70 70 "{\n",
71 71 " \"stdin_port\": 52858, \n",
72 72 " \"ip\": \"127.0.0.1\", \n",
73 73 " \"hb_port\": 52859, \n",
74 74 " \"key\": \"7efd45ca-d8a2-41b0-9cea-d9116d0fb883\", \n",
75 75 " \"shell_port\": 52856, \n",
76 76 " \"iopub_port\": 52857\n",
77 77 "}\n",
78 78 "\n",
79 79 "Paste the above JSON into a file, and connect with:\n",
80 80 " $> ipython <app> --existing <file>\n",
81 81 "or, if you are local, you can connect with just:\n",
82 82 " $> ipython <app> --existing kernel-b3bac7c1-8b2c-4536-8082-8d1df24f99ac.json \n",
83 83 "or even just:\n",
84 84 " $> ipython <app> --existing \n",
85 85 "if this is the most recent IPython session you have started.\n"
86 86 ]
87 87 }
88 88 ],
89 89 "prompt_number": 6
90 90 },
91 91 {
92 92 "cell_type": "markdown",
93 93 "metadata": {},
94 94 "source": [
95 95 "You can see that this magic displays everything you need to connect to this Notebook's Kernel."
96 96 ]
97 97 },
98 98 {
99 99 "cell_type": "heading",
100 100 "level": 2,
101 101 "metadata": {},
102 102 "source": [
103 103 "Automatic connection using a new Qt Console"
104 104 ]
105 105 },
106 106 {
107 107 "cell_type": "markdown",
108 108 "metadata": {},
109 109 "source": [
110 110 "You can also start a new Qt Console connected to your current Kernel by using the `%qtconsole` magic. This will detect the necessary connection\n",
111 111 "information and start the Qt Console for you automatically."
112 112 ]
113 113 },
114 114 {
115 115 "cell_type": "code",
116 116 "collapsed": false,
117 117 "input": [
118 118 "a = 10"
119 119 ],
120 120 "language": "python",
121 121 "metadata": {},
122 122 "outputs": [],
123 123 "prompt_number": 1
124 124 },
125 125 {
126 126 "cell_type": "code",
127 127 "collapsed": false,
128 128 "input": [
129 129 "%qtconsole"
130 130 ],
131 131 "language": "python",
132 132 "metadata": {},
133 133 "outputs": [],
134 134 "prompt_number": 2
135 135 },
136 136 {
137 137 "cell_type": "heading",
138 138 "level": 2,
139 139 "metadata": {},
140 140 "source": [
141 141 "The kernel's `raw_input` and `%debug`"
142 142 ]
143 143 },
144 144 {
145 145 "cell_type": "markdown",
146 146 "metadata": {},
147 147 "source": [
148 148 "The Notebook has added support for `raw_input` and `%debug`, as of 1.0."
149 149 ]
150 150 },
151 151 {
152 152 "cell_type": "code",
153 153 "collapsed": false,
154 154 "input": [
155 "# Python 3 compat\n",
156 "try:\n",
157 " raw_input\n",
158 "except NameError:\n",
159 " raw_input = input"
160 ],
161 "language": "python",
162 "metadata": {},
163 "outputs": [],
164 "prompt_number": 1
165 },
166 {
167 "cell_type": "code",
168 "collapsed": false,
169 "input": [
155 170 "name = raw_input(\"What is your name? \")\n",
156 171 "name"
157 172 ],
158 173 "language": "python",
159 174 "metadata": {},
160 175 "outputs": [
161 176 {
162 177 "name": "stdout",
163 178 "output_type": "stream",
164 179 "stream": "stdout",
165 180 "text": [
166 181 "What is your name? Sir Robin\n"
167 182 ]
168 183 },
169 184 {
170 185 "metadata": {},
171 186 "output_type": "pyout",
172 "prompt_number": 1,
187 "prompt_number": 2,
173 188 "text": [
174 "u'Sir Robin'"
189 "'Sir Robin'"
175 190 ]
176 191 }
177 192 ],
178 "prompt_number": 1
193 "prompt_number": 2
194 },
195 {
196 "cell_type": "markdown",
197 "metadata": {},
198 "source": [
199 "**Python 2-only**: the eval input works as well (`input` is just `eval(raw_input(prompt))`)"
200 ]
201 },
202 {
203 "cell_type": "code",
204 "collapsed": false,
205 "input": [
206 "fingers = input(\"How many fingers? \")\n",
207 "fingers, type(fingers)"
208 ],
209 "language": "python",
210 "metadata": {},
211 "outputs": [
212 {
213 "name": "stdout",
214 "output_type": "stream",
215 "stream": "stdout",
216 "text": [
217 "How many fingers? 4\n"
218 ]
219 },
220 {
221 "metadata": {},
222 "output_type": "pyout",
223 "prompt_number": 3,
224 "text": [
225 "(4, int)"
226 ]
227 }
228 ],
229 "prompt_number": 3
179 230 },
180 231 {
181 232 "cell_type": "code",
182 233 "collapsed": false,
183 234 "input": [
184 235 "def div(x, y):\n",
185 236 " return x/y\n",
186 237 "\n",
187 238 "div(1,0)"
188 239 ],
189 240 "language": "python",
190 241 "metadata": {},
191 242 "outputs": [
192 243 {
193 244 "ename": "ZeroDivisionError",
194 245 "evalue": "integer division or modulo by zero",
195 246 "output_type": "pyerr",
196 247 "traceback": [
197 248 "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[1;31mZeroDivisionError\u001b[0m Traceback (most recent call last)",
198 "\u001b[1;32m<ipython-input-2-a5097cc0c0c5>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[0;32m 2\u001b[0m \u001b[1;32mreturn\u001b[0m \u001b[0mx\u001b[0m\u001b[1;33m/\u001b[0m\u001b[0my\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 3\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 4\u001b[1;33m \u001b[0mdiv\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
199 "\u001b[1;32m<ipython-input-2-a5097cc0c0c5>\u001b[0m in \u001b[0;36mdiv\u001b[1;34m(x, y)\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0mdiv\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mx\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0my\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m \u001b[1;32mreturn\u001b[0m \u001b[0mx\u001b[0m\u001b[1;33m/\u001b[0m\u001b[0my\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 3\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 4\u001b[0m \u001b[0mdiv\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
249 "\u001b[1;32m<ipython-input-4-a5097cc0c0c5>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[0;32m 2\u001b[0m \u001b[1;32mreturn\u001b[0m \u001b[0mx\u001b[0m\u001b[1;33m/\u001b[0m\u001b[0my\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 3\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 4\u001b[1;33m \u001b[0mdiv\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
250 "\u001b[1;32m<ipython-input-4-a5097cc0c0c5>\u001b[0m in \u001b[0;36mdiv\u001b[1;34m(x, y)\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0mdiv\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mx\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0my\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m \u001b[1;32mreturn\u001b[0m \u001b[0mx\u001b[0m\u001b[1;33m/\u001b[0m\u001b[0my\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 3\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 4\u001b[0m \u001b[0mdiv\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
200 251 "\u001b[1;31mZeroDivisionError\u001b[0m: integer division or modulo by zero"
201 252 ]
202 253 }
203 254 ],
204 "prompt_number": 2
255 "prompt_number": 4
205 256 },
206 257 {
207 258 "cell_type": "code",
208 259 "collapsed": false,
209 260 "input": [
210 261 "%debug"
211 262 ],
212 263 "language": "python",
213 264 "metadata": {},
214 265 "outputs": [
215 266 {
216 267 "output_type": "stream",
217 268 "stream": "stdout",
218 269 "text": [
219 "> \u001b[1;32m<ipython-input-2-a5097cc0c0c5>\u001b[0m(2)\u001b[0;36mdiv\u001b[1;34m()\u001b[0m\n",
270 "> \u001b[1;32m<ipython-input-4-a5097cc0c0c5>\u001b[0m(2)\u001b[0;36mdiv\u001b[1;34m()\u001b[0m\n",
220 271 "\u001b[1;32m 1 \u001b[1;33m\u001b[1;32mdef\u001b[0m \u001b[0mdiv\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mx\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0my\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
221 272 "\u001b[0m\u001b[1;32m----> 2 \u001b[1;33m \u001b[1;32mreturn\u001b[0m \u001b[0mx\u001b[0m\u001b[1;33m/\u001b[0m\u001b[0my\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
222 273 "\u001b[0m\u001b[1;32m 3 \u001b[1;33m\u001b[1;33m\u001b[0m\u001b[0m\n",
223 274 "\u001b[0m\n"
224 275 ]
225 276 },
226 277 {
227 278 "name": "stdout",
228 279 "output_type": "stream",
229 280 "stream": "stdout",
230 281 "text": [
231 282 "ipdb> x\n"
232 283 ]
233 284 },
234 285 {
235 286 "output_type": "stream",
236 287 "stream": "stdout",
237 288 "text": [
238 289 "1\n"
239 290 ]
240 291 },
241 292 {
242 293 "name": "stdout",
243 294 "output_type": "stream",
244 295 "stream": "stdout",
245 296 "text": [
246 297 "ipdb> y\n"
247 298 ]
248 299 },
249 300 {
250 301 "output_type": "stream",
251 302 "stream": "stdout",
252 303 "text": [
253 304 "0\n"
254 305 ]
255 306 },
256 307 {
257 308 "name": "stdout",
258 309 "output_type": "stream",
259 310 "stream": "stdout",
260 311 "text": [
261 312 "ipdb> exit\n"
262 313 ]
263 314 }
264 315 ],
265 "prompt_number": 3
316 "prompt_number": 5
266 317 }
267 318 ],
268 319 "metadata": {}
269 320 }
270 321 ]
271 322 } No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now