##// END OF EJS Templates
Use Instance trait for user_ns instead of Dict....
Thomas Kluyver -
Show More
@@ -1,409 +1,409 b''
1 1 # encoding: utf-8
2 2 """
3 3 A mixin for :class:`~IPython.core.application.Application` classes that
4 4 launch InteractiveShell instances, load extensions, etc.
5 5
6 6 Authors
7 7 -------
8 8
9 9 * Min Ragan-Kelley
10 10 """
11 11
12 12 #-----------------------------------------------------------------------------
13 13 # Copyright (C) 2008-2011 The IPython Development Team
14 14 #
15 15 # Distributed under the terms of the BSD License. The full license is in
16 16 # the file COPYING, distributed as part of this software.
17 17 #-----------------------------------------------------------------------------
18 18
19 19 #-----------------------------------------------------------------------------
20 20 # Imports
21 21 #-----------------------------------------------------------------------------
22 22
23 23 from __future__ import absolute_import
24 24
25 25 import glob
26 26 import os
27 27 import sys
28 28
29 29 from IPython.config.application import boolean_flag
30 30 from IPython.config.configurable import Configurable
31 31 from IPython.config.loader import Config
32 32 from IPython.core import pylabtools
33 33 from IPython.utils import py3compat
34 34 from IPython.utils.contexts import preserve_keys
35 35 from IPython.utils.path import filefind
36 36 from IPython.utils.traitlets import (
37 37 Unicode, Instance, List, Bool, CaselessStrEnum, Dict
38 38 )
39 39 from IPython.lib.inputhook import guis
40 40
41 41 #-----------------------------------------------------------------------------
42 42 # Aliases and Flags
43 43 #-----------------------------------------------------------------------------
44 44
45 45 gui_keys = tuple(sorted([ key for key in guis if key is not None ]))
46 46
47 47 backend_keys = sorted(pylabtools.backends.keys())
48 48 backend_keys.insert(0, 'auto')
49 49
50 50 shell_flags = {}
51 51
52 52 addflag = lambda *args: shell_flags.update(boolean_flag(*args))
53 53 addflag('autoindent', 'InteractiveShell.autoindent',
54 54 'Turn on autoindenting.', 'Turn off autoindenting.'
55 55 )
56 56 addflag('automagic', 'InteractiveShell.automagic',
57 57 """Turn on the auto calling of magic commands. Type %%magic at the
58 58 IPython prompt for more information.""",
59 59 'Turn off the auto calling of magic commands.'
60 60 )
61 61 addflag('pdb', 'InteractiveShell.pdb',
62 62 "Enable auto calling the pdb debugger after every exception.",
63 63 "Disable auto calling the pdb debugger after every exception."
64 64 )
65 65 # pydb flag doesn't do any config, as core.debugger switches on import,
66 66 # which is before parsing. This just allows the flag to be passed.
67 67 shell_flags.update(dict(
68 68 pydb = ({},
69 69 """Use the third party 'pydb' package as debugger, instead of pdb.
70 70 Requires that pydb is installed."""
71 71 )
72 72 ))
73 73 addflag('pprint', 'PlainTextFormatter.pprint',
74 74 "Enable auto pretty printing of results.",
75 75 "Disable auto pretty printing of results."
76 76 )
77 77 addflag('color-info', 'InteractiveShell.color_info',
78 78 """IPython can display information about objects via a set of func-
79 79 tions, and optionally can use colors for this, syntax highlighting
80 80 source code and various other elements. However, because this
81 81 information is passed through a pager (like 'less') and many pagers get
82 82 confused with color codes, this option is off by default. You can test
83 83 it and turn it on permanently in your ipython_config.py file if it
84 84 works for you. Test it and turn it on permanently if it works with
85 85 your system. The magic function %%color_info allows you to toggle this
86 86 interactively for testing.""",
87 87 "Disable using colors for info related things."
88 88 )
89 89 addflag('deep-reload', 'InteractiveShell.deep_reload',
90 90 """Enable deep (recursive) reloading by default. IPython can use the
91 91 deep_reload module which reloads changes in modules recursively (it
92 92 replaces the reload() function, so you don't need to change anything to
93 93 use it). deep_reload() forces a full reload of modules whose code may
94 94 have changed, which the default reload() function does not. When
95 95 deep_reload is off, IPython will use the normal reload(), but
96 96 deep_reload will still be available as dreload(). This feature is off
97 97 by default [which means that you have both normal reload() and
98 98 dreload()].""",
99 99 "Disable deep (recursive) reloading by default."
100 100 )
101 101 nosep_config = Config()
102 102 nosep_config.InteractiveShell.separate_in = ''
103 103 nosep_config.InteractiveShell.separate_out = ''
104 104 nosep_config.InteractiveShell.separate_out2 = ''
105 105
106 106 shell_flags['nosep']=(nosep_config, "Eliminate all spacing between prompts.")
107 107 shell_flags['pylab'] = (
108 108 {'InteractiveShellApp' : {'pylab' : 'auto'}},
109 109 """Pre-load matplotlib and numpy for interactive use with
110 110 the default matplotlib backend."""
111 111 )
112 112 shell_flags['matplotlib'] = (
113 113 {'InteractiveShellApp' : {'matplotlib' : 'auto'}},
114 114 """Configure matplotlib for interactive use with
115 115 the default matplotlib backend."""
116 116 )
117 117
118 118 # it's possible we don't want short aliases for *all* of these:
119 119 shell_aliases = dict(
120 120 autocall='InteractiveShell.autocall',
121 121 colors='InteractiveShell.colors',
122 122 logfile='InteractiveShell.logfile',
123 123 logappend='InteractiveShell.logappend',
124 124 c='InteractiveShellApp.code_to_run',
125 125 m='InteractiveShellApp.module_to_run',
126 126 ext='InteractiveShellApp.extra_extension',
127 127 gui='InteractiveShellApp.gui',
128 128 pylab='InteractiveShellApp.pylab',
129 129 matplotlib='InteractiveShellApp.matplotlib',
130 130 )
131 131 shell_aliases['cache-size'] = 'InteractiveShell.cache_size'
132 132
133 133 #-----------------------------------------------------------------------------
134 134 # Main classes and functions
135 135 #-----------------------------------------------------------------------------
136 136
137 137 class InteractiveShellApp(Configurable):
138 138 """A Mixin for applications that start InteractiveShell instances.
139 139
140 140 Provides configurables for loading extensions and executing files
141 141 as part of configuring a Shell environment.
142 142
143 143 The following methods should be called by the :meth:`initialize` method
144 144 of the subclass:
145 145
146 146 - :meth:`init_path`
147 147 - :meth:`init_shell` (to be implemented by the subclass)
148 148 - :meth:`init_gui_pylab`
149 149 - :meth:`init_extensions`
150 150 - :meth:`init_code`
151 151 """
152 152 extensions = List(Unicode, config=True,
153 153 help="A list of dotted module names of IPython extensions to load."
154 154 )
155 155 extra_extension = Unicode('', config=True,
156 156 help="dotted module name of an IPython extension to load."
157 157 )
158 158 def _extra_extension_changed(self, name, old, new):
159 159 if new:
160 160 # add to self.extensions
161 161 self.extensions.append(new)
162 162
163 163 # Extensions that are always loaded (not configurable)
164 164 default_extensions = List(Unicode, [u'storemagic'], config=False)
165 165
166 166 exec_files = List(Unicode, config=True,
167 167 help="""List of files to run at IPython startup."""
168 168 )
169 169 file_to_run = Unicode('', config=True,
170 170 help="""A file to be run""")
171 171
172 172 exec_lines = List(Unicode, config=True,
173 173 help="""lines of code to run at IPython startup."""
174 174 )
175 175 code_to_run = Unicode('', config=True,
176 176 help="Execute the given command string."
177 177 )
178 178 module_to_run = Unicode('', config=True,
179 179 help="Run the module as a script."
180 180 )
181 181 gui = CaselessStrEnum(gui_keys, config=True,
182 182 help="Enable GUI event loop integration with any of {0}.".format(gui_keys)
183 183 )
184 184 matplotlib = CaselessStrEnum(backend_keys,
185 185 config=True,
186 186 help="""Configure matplotlib for interactive use with
187 187 the default matplotlib backend."""
188 188 )
189 189 pylab = CaselessStrEnum(backend_keys,
190 190 config=True,
191 191 help="""Pre-load matplotlib and numpy for interactive use,
192 192 selecting a particular matplotlib backend and loop integration.
193 193 """
194 194 )
195 195 pylab_import_all = Bool(True, config=True,
196 196 help="""If true, IPython will populate the user namespace with numpy, pylab, etc.
197 197 and an 'import *' is done from numpy and pylab, when using pylab mode.
198 198
199 199 When False, pylab mode should not import any names into the user namespace.
200 200 """
201 201 )
202 202 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
203 203
204 user_ns = Dict(default_value=None)
204 user_ns = Instance(dict, args=None, allow_none=True)
205 205 def _user_ns_changed(self, name, old, new):
206 206 if self.shell is not None:
207 207 self.shell.user_ns = new
208 208 self.shell.init_user_ns()
209 209
210 210 def init_path(self):
211 211 """Add current working directory, '', to sys.path"""
212 212 if sys.path[0] != '':
213 213 sys.path.insert(0, '')
214 214
215 215 def init_shell(self):
216 216 raise NotImplementedError("Override in subclasses")
217 217
218 218 def init_gui_pylab(self):
219 219 """Enable GUI event loop integration, taking pylab into account."""
220 220 enable = False
221 221 shell = self.shell
222 222 if self.pylab:
223 223 enable = lambda key: shell.enable_pylab(key, import_all=self.pylab_import_all)
224 224 key = self.pylab
225 225 elif self.matplotlib:
226 226 enable = shell.enable_matplotlib
227 227 key = self.matplotlib
228 228 elif self.gui:
229 229 enable = shell.enable_gui
230 230 key = self.gui
231 231
232 232 if not enable:
233 233 return
234 234
235 235 try:
236 236 r = enable(key)
237 237 except ImportError:
238 238 self.log.warn("Eventloop or matplotlib integration failed. Is matplotlib installed?")
239 239 self.shell.showtraceback()
240 240 return
241 241 except Exception:
242 242 self.log.warn("GUI event loop or pylab initialization failed")
243 243 self.shell.showtraceback()
244 244 return
245 245
246 246 if isinstance(r, tuple):
247 247 gui, backend = r[:2]
248 248 self.log.info("Enabling GUI event loop integration, "
249 249 "eventloop=%s, matplotlib=%s", gui, backend)
250 250 if key == "auto":
251 251 print ("Using matplotlib backend: %s" % backend)
252 252 else:
253 253 gui = r
254 254 self.log.info("Enabling GUI event loop integration, "
255 255 "eventloop=%s", gui)
256 256
257 257 def init_extensions(self):
258 258 """Load all IPython extensions in IPythonApp.extensions.
259 259
260 260 This uses the :meth:`ExtensionManager.load_extensions` to load all
261 261 the extensions listed in ``self.extensions``.
262 262 """
263 263 try:
264 264 self.log.debug("Loading IPython extensions...")
265 265 extensions = self.default_extensions + self.extensions
266 266 for ext in extensions:
267 267 try:
268 268 self.log.info("Loading IPython extension: %s" % ext)
269 269 self.shell.extension_manager.load_extension(ext)
270 270 except:
271 271 self.log.warn("Error in loading extension: %s" % ext +
272 272 "\nCheck your config files in %s" % self.profile_dir.location
273 273 )
274 274 self.shell.showtraceback()
275 275 except:
276 276 self.log.warn("Unknown error in loading extensions:")
277 277 self.shell.showtraceback()
278 278
279 279 def init_code(self):
280 280 """run the pre-flight code, specified via exec_lines"""
281 281 self._run_startup_files()
282 282 self._run_exec_lines()
283 283 self._run_exec_files()
284 284 self._run_cmd_line_code()
285 285 self._run_module()
286 286
287 287 # flush output, so itwon't be attached to the first cell
288 288 sys.stdout.flush()
289 289 sys.stderr.flush()
290 290
291 291 # Hide variables defined here from %who etc.
292 292 self.shell.user_ns_hidden.update(self.shell.user_ns)
293 293
294 294 def _run_exec_lines(self):
295 295 """Run lines of code in IPythonApp.exec_lines in the user's namespace."""
296 296 if not self.exec_lines:
297 297 return
298 298 try:
299 299 self.log.debug("Running code from IPythonApp.exec_lines...")
300 300 for line in self.exec_lines:
301 301 try:
302 302 self.log.info("Running code in user namespace: %s" %
303 303 line)
304 304 self.shell.run_cell(line, store_history=False)
305 305 except:
306 306 self.log.warn("Error in executing line in user "
307 307 "namespace: %s" % line)
308 308 self.shell.showtraceback()
309 309 except:
310 310 self.log.warn("Unknown error in handling IPythonApp.exec_lines:")
311 311 self.shell.showtraceback()
312 312
313 313 def _exec_file(self, fname):
314 314 try:
315 315 full_filename = filefind(fname, [u'.', self.ipython_dir])
316 316 except IOError as e:
317 317 self.log.warn("File not found: %r"%fname)
318 318 return
319 319 # Make sure that the running script gets a proper sys.argv as if it
320 320 # were run from a system shell.
321 321 save_argv = sys.argv
322 322 sys.argv = [full_filename] + self.extra_args[1:]
323 323 # protect sys.argv from potential unicode strings on Python 2:
324 324 if not py3compat.PY3:
325 325 sys.argv = [ py3compat.cast_bytes(a) for a in sys.argv ]
326 326 try:
327 327 if os.path.isfile(full_filename):
328 328 self.log.info("Running file in user namespace: %s" %
329 329 full_filename)
330 330 # Ensure that __file__ is always defined to match Python
331 331 # behavior.
332 332 with preserve_keys(self.shell.user_ns, '__file__'):
333 333 self.shell.user_ns['__file__'] = fname
334 334 if full_filename.endswith('.ipy'):
335 335 self.shell.safe_execfile_ipy(full_filename)
336 336 else:
337 337 # default to python, even without extension
338 338 self.shell.safe_execfile(full_filename,
339 339 self.shell.user_ns)
340 340 finally:
341 341 sys.argv = save_argv
342 342
343 343 def _run_startup_files(self):
344 344 """Run files from profile startup directory"""
345 345 startup_dir = self.profile_dir.startup_dir
346 346 startup_files = []
347 347 if os.environ.get('PYTHONSTARTUP', False):
348 348 startup_files.append(os.environ['PYTHONSTARTUP'])
349 349 startup_files += glob.glob(os.path.join(startup_dir, '*.py'))
350 350 startup_files += glob.glob(os.path.join(startup_dir, '*.ipy'))
351 351 if not startup_files:
352 352 return
353 353
354 354 self.log.debug("Running startup files from %s...", startup_dir)
355 355 try:
356 356 for fname in sorted(startup_files):
357 357 self._exec_file(fname)
358 358 except:
359 359 self.log.warn("Unknown error in handling startup files:")
360 360 self.shell.showtraceback()
361 361
362 362 def _run_exec_files(self):
363 363 """Run files from IPythonApp.exec_files"""
364 364 if not self.exec_files:
365 365 return
366 366
367 367 self.log.debug("Running files in IPythonApp.exec_files...")
368 368 try:
369 369 for fname in self.exec_files:
370 370 self._exec_file(fname)
371 371 except:
372 372 self.log.warn("Unknown error in handling IPythonApp.exec_files:")
373 373 self.shell.showtraceback()
374 374
375 375 def _run_cmd_line_code(self):
376 376 """Run code or file specified at the command-line"""
377 377 if self.code_to_run:
378 378 line = self.code_to_run
379 379 try:
380 380 self.log.info("Running code given at command line (c=): %s" %
381 381 line)
382 382 self.shell.run_cell(line, store_history=False)
383 383 except:
384 384 self.log.warn("Error in executing line in user namespace: %s" %
385 385 line)
386 386 self.shell.showtraceback()
387 387
388 388 # Like Python itself, ignore the second if the first of these is present
389 389 elif self.file_to_run:
390 390 fname = self.file_to_run
391 391 try:
392 392 self._exec_file(fname)
393 393 except:
394 394 self.log.warn("Error in executing file in user namespace: %s" %
395 395 fname)
396 396 self.shell.showtraceback()
397 397
398 398 def _run_module(self):
399 399 """Run module specified at the command-line."""
400 400 if self.module_to_run:
401 401 # Make sure that the module gets a proper sys.argv as if it were
402 402 # run using `python -m`.
403 403 save_argv = sys.argv
404 404 sys.argv = [sys.executable] + self.extra_args
405 405 try:
406 406 self.shell.safe_run_module(self.module_to_run,
407 407 self.shell.user_ns)
408 408 finally:
409 409 sys.argv = save_argv
@@ -1,811 +1,811 b''
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 user_ns = Dict(default_value=None)
90 user_ns = Instance(dict, args=None, allow_none=True)
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 134 _sys_eval_input = Any()
135 135
136 136 # set of aborted msg_ids
137 137 aborted = Set()
138 138
139 139
140 140 def __init__(self, **kwargs):
141 141 super(Kernel, self).__init__(**kwargs)
142 142
143 143 # Initialize the InteractiveShell subclass
144 144 self.shell = self.shell_class.instance(parent=self,
145 145 profile_dir = self.profile_dir,
146 146 user_module = self.user_module,
147 147 user_ns = self.user_ns,
148 148 )
149 149 self.shell.displayhook.session = self.session
150 150 self.shell.displayhook.pub_socket = self.iopub_socket
151 151 self.shell.displayhook.topic = self._topic('pyout')
152 152 self.shell.display_pub.session = self.session
153 153 self.shell.display_pub.pub_socket = self.iopub_socket
154 154 self.shell.data_pub.session = self.session
155 155 self.shell.data_pub.pub_socket = self.iopub_socket
156 156
157 157 # TMP - hack while developing
158 158 self.shell._reply_content = None
159 159
160 160 # Build dict of handlers for message types
161 161 msg_types = [ 'execute_request', 'complete_request',
162 162 'object_info_request', 'history_request',
163 163 'kernel_info_request',
164 164 'connect_request', 'shutdown_request',
165 165 'apply_request',
166 166 ]
167 167 self.shell_handlers = {}
168 168 for msg_type in msg_types:
169 169 self.shell_handlers[msg_type] = getattr(self, msg_type)
170 170
171 171 control_msg_types = msg_types + [ 'clear_request', 'abort_request' ]
172 172 self.control_handlers = {}
173 173 for msg_type in control_msg_types:
174 174 self.control_handlers[msg_type] = getattr(self, msg_type)
175 175
176 176 def dispatch_control(self, msg):
177 177 """dispatch control requests"""
178 178 idents,msg = self.session.feed_identities(msg, copy=False)
179 179 try:
180 180 msg = self.session.unserialize(msg, content=True, copy=False)
181 181 except:
182 182 self.log.error("Invalid Control Message", exc_info=True)
183 183 return
184 184
185 185 self.log.debug("Control received: %s", msg)
186 186
187 187 header = msg['header']
188 188 msg_id = header['msg_id']
189 189 msg_type = header['msg_type']
190 190
191 191 handler = self.control_handlers.get(msg_type, None)
192 192 if handler is None:
193 193 self.log.error("UNKNOWN CONTROL MESSAGE TYPE: %r", msg_type)
194 194 else:
195 195 try:
196 196 handler(self.control_stream, idents, msg)
197 197 except Exception:
198 198 self.log.error("Exception in control handler:", exc_info=True)
199 199
200 200 def dispatch_shell(self, stream, msg):
201 201 """dispatch shell requests"""
202 202 # flush control requests first
203 203 if self.control_stream:
204 204 self.control_stream.flush()
205 205
206 206 idents,msg = self.session.feed_identities(msg, copy=False)
207 207 try:
208 208 msg = self.session.unserialize(msg, content=True, copy=False)
209 209 except:
210 210 self.log.error("Invalid Message", exc_info=True)
211 211 return
212 212
213 213 header = msg['header']
214 214 msg_id = header['msg_id']
215 215 msg_type = msg['header']['msg_type']
216 216
217 217 # Print some info about this message and leave a '--->' marker, so it's
218 218 # easier to trace visually the message chain when debugging. Each
219 219 # handler prints its message at the end.
220 220 self.log.debug('\n*** MESSAGE TYPE:%s***', msg_type)
221 221 self.log.debug(' Content: %s\n --->\n ', msg['content'])
222 222
223 223 if msg_id in self.aborted:
224 224 self.aborted.remove(msg_id)
225 225 # is it safe to assume a msg_id will not be resubmitted?
226 226 reply_type = msg_type.split('_')[0] + '_reply'
227 227 status = {'status' : 'aborted'}
228 228 md = {'engine' : self.ident}
229 229 md.update(status)
230 230 reply_msg = self.session.send(stream, reply_type, metadata=md,
231 231 content=status, parent=msg, ident=idents)
232 232 return
233 233
234 234 handler = self.shell_handlers.get(msg_type, None)
235 235 if handler is None:
236 236 self.log.error("UNKNOWN MESSAGE TYPE: %r", msg_type)
237 237 else:
238 238 # ensure default_int_handler during handler call
239 239 sig = signal(SIGINT, default_int_handler)
240 240 try:
241 241 handler(stream, idents, msg)
242 242 except Exception:
243 243 self.log.error("Exception in message handler:", exc_info=True)
244 244 finally:
245 245 signal(SIGINT, sig)
246 246
247 247 def enter_eventloop(self):
248 248 """enter eventloop"""
249 249 self.log.info("entering eventloop")
250 250 # restore default_int_handler
251 251 signal(SIGINT, default_int_handler)
252 252 while self.eventloop is not None:
253 253 try:
254 254 self.eventloop(self)
255 255 except KeyboardInterrupt:
256 256 # Ctrl-C shouldn't crash the kernel
257 257 self.log.error("KeyboardInterrupt caught in kernel")
258 258 continue
259 259 else:
260 260 # eventloop exited cleanly, this means we should stop (right?)
261 261 self.eventloop = None
262 262 break
263 263 self.log.info("exiting eventloop")
264 264
265 265 def start(self):
266 266 """register dispatchers for streams"""
267 267 self.shell.exit_now = False
268 268 if self.control_stream:
269 269 self.control_stream.on_recv(self.dispatch_control, copy=False)
270 270
271 271 def make_dispatcher(stream):
272 272 def dispatcher(msg):
273 273 return self.dispatch_shell(stream, msg)
274 274 return dispatcher
275 275
276 276 for s in self.shell_streams:
277 277 s.on_recv(make_dispatcher(s), copy=False)
278 278
279 279 # publish idle status
280 280 self._publish_status('starting')
281 281
282 282 def do_one_iteration(self):
283 283 """step eventloop just once"""
284 284 if self.control_stream:
285 285 self.control_stream.flush()
286 286 for stream in self.shell_streams:
287 287 # handle at most one request per iteration
288 288 stream.flush(zmq.POLLIN, 1)
289 289 stream.flush(zmq.POLLOUT)
290 290
291 291
292 292 def record_ports(self, ports):
293 293 """Record the ports that this kernel is using.
294 294
295 295 The creator of the Kernel instance must call this methods if they
296 296 want the :meth:`connect_request` method to return the port numbers.
297 297 """
298 298 self._recorded_ports = ports
299 299
300 300 #---------------------------------------------------------------------------
301 301 # Kernel request handlers
302 302 #---------------------------------------------------------------------------
303 303
304 304 def _make_metadata(self, other=None):
305 305 """init metadata dict, for execute/apply_reply"""
306 306 new_md = {
307 307 'dependencies_met' : True,
308 308 'engine' : self.ident,
309 309 'started': datetime.now(),
310 310 }
311 311 if other:
312 312 new_md.update(other)
313 313 return new_md
314 314
315 315 def _publish_pyin(self, code, parent, execution_count):
316 316 """Publish the code request on the pyin stream."""
317 317
318 318 self.session.send(self.iopub_socket, u'pyin',
319 319 {u'code':code, u'execution_count': execution_count},
320 320 parent=parent, ident=self._topic('pyin')
321 321 )
322 322
323 323 def _publish_status(self, status, parent=None):
324 324 """send status (busy/idle) on IOPub"""
325 325 self.session.send(self.iopub_socket,
326 326 u'status',
327 327 {u'execution_state': status},
328 328 parent=parent,
329 329 ident=self._topic('status'),
330 330 )
331 331
332 332
333 333 def execute_request(self, stream, ident, parent):
334 334 """handle an execute_request"""
335 335
336 336 self._publish_status(u'busy', parent)
337 337
338 338 try:
339 339 content = parent[u'content']
340 340 code = content[u'code']
341 341 silent = content[u'silent']
342 342 store_history = content.get(u'store_history', not silent)
343 343 except:
344 344 self.log.error("Got bad msg: ")
345 345 self.log.error("%s", parent)
346 346 return
347 347
348 348 md = self._make_metadata(parent['metadata'])
349 349
350 350 shell = self.shell # we'll need this a lot here
351 351
352 352 # Replace raw_input. Note that is not sufficient to replace
353 353 # raw_input in the user namespace.
354 354 if content.get('allow_stdin', False):
355 355 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
356 356 input = lambda prompt='': eval(raw_input(prompt))
357 357 else:
358 358 raw_input = input = lambda prompt='' : self._no_raw_input()
359 359
360 360 if py3compat.PY3:
361 361 self._sys_raw_input = __builtin__.input
362 362 __builtin__.input = raw_input
363 363 else:
364 364 self._sys_raw_input = __builtin__.raw_input
365 365 self._sys_eval_input = __builtin__.input
366 366 __builtin__.raw_input = raw_input
367 367 __builtin__.input = input
368 368
369 369 # Set the parent message of the display hook and out streams.
370 370 shell.displayhook.set_parent(parent)
371 371 shell.display_pub.set_parent(parent)
372 372 shell.data_pub.set_parent(parent)
373 373 try:
374 374 sys.stdout.set_parent(parent)
375 375 except AttributeError:
376 376 pass
377 377 try:
378 378 sys.stderr.set_parent(parent)
379 379 except AttributeError:
380 380 pass
381 381
382 382 # Re-broadcast our input for the benefit of listening clients, and
383 383 # start computing output
384 384 if not silent:
385 385 self._publish_pyin(code, parent, shell.execution_count)
386 386
387 387 reply_content = {}
388 388 try:
389 389 # FIXME: the shell calls the exception handler itself.
390 390 shell.run_cell(code, store_history=store_history, silent=silent)
391 391 except:
392 392 status = u'error'
393 393 # FIXME: this code right now isn't being used yet by default,
394 394 # because the run_cell() call above directly fires off exception
395 395 # reporting. This code, therefore, is only active in the scenario
396 396 # where runlines itself has an unhandled exception. We need to
397 397 # uniformize this, for all exception construction to come from a
398 398 # single location in the codbase.
399 399 etype, evalue, tb = sys.exc_info()
400 400 tb_list = traceback.format_exception(etype, evalue, tb)
401 401 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
402 402 else:
403 403 status = u'ok'
404 404 finally:
405 405 # Restore raw_input.
406 406 if py3compat.PY3:
407 407 __builtin__.input = self._sys_raw_input
408 408 else:
409 409 __builtin__.raw_input = self._sys_raw_input
410 410 __builtin__.input = self._sys_eval_input
411 411
412 412 reply_content[u'status'] = status
413 413
414 414 # Return the execution counter so clients can display prompts
415 415 reply_content['execution_count'] = shell.execution_count - 1
416 416
417 417 # FIXME - fish exception info out of shell, possibly left there by
418 418 # runlines. We'll need to clean up this logic later.
419 419 if shell._reply_content is not None:
420 420 reply_content.update(shell._reply_content)
421 421 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='execute')
422 422 reply_content['engine_info'] = e_info
423 423 # reset after use
424 424 shell._reply_content = None
425 425
426 426 if 'traceback' in reply_content:
427 427 self.log.info("Exception in execute request:\n%s", '\n'.join(reply_content['traceback']))
428 428
429 429
430 430 # At this point, we can tell whether the main code execution succeeded
431 431 # or not. If it did, we proceed to evaluate user_variables/expressions
432 432 if reply_content['status'] == 'ok':
433 433 reply_content[u'user_variables'] = \
434 434 shell.user_variables(content.get(u'user_variables', []))
435 435 reply_content[u'user_expressions'] = \
436 436 shell.user_expressions(content.get(u'user_expressions', {}))
437 437 else:
438 438 # If there was an error, don't even try to compute variables or
439 439 # expressions
440 440 reply_content[u'user_variables'] = {}
441 441 reply_content[u'user_expressions'] = {}
442 442
443 443 # Payloads should be retrieved regardless of outcome, so we can both
444 444 # recover partial output (that could have been generated early in a
445 445 # block, before an error) and clear the payload system always.
446 446 reply_content[u'payload'] = shell.payload_manager.read_payload()
447 447 # Be agressive about clearing the payload because we don't want
448 448 # it to sit in memory until the next execute_request comes in.
449 449 shell.payload_manager.clear_payload()
450 450
451 451 # Flush output before sending the reply.
452 452 sys.stdout.flush()
453 453 sys.stderr.flush()
454 454 # FIXME: on rare occasions, the flush doesn't seem to make it to the
455 455 # clients... This seems to mitigate the problem, but we definitely need
456 456 # to better understand what's going on.
457 457 if self._execute_sleep:
458 458 time.sleep(self._execute_sleep)
459 459
460 460 # Send the reply.
461 461 reply_content = json_clean(reply_content)
462 462
463 463 md['status'] = reply_content['status']
464 464 if reply_content['status'] == 'error' and \
465 465 reply_content['ename'] == 'UnmetDependency':
466 466 md['dependencies_met'] = False
467 467
468 468 reply_msg = self.session.send(stream, u'execute_reply',
469 469 reply_content, parent, metadata=md,
470 470 ident=ident)
471 471
472 472 self.log.debug("%s", reply_msg)
473 473
474 474 if not silent and reply_msg['content']['status'] == u'error':
475 475 self._abort_queues()
476 476
477 477 self._publish_status(u'idle', parent)
478 478
479 479 def complete_request(self, stream, ident, parent):
480 480 txt, matches = self._complete(parent)
481 481 matches = {'matches' : matches,
482 482 'matched_text' : txt,
483 483 'status' : 'ok'}
484 484 matches = json_clean(matches)
485 485 completion_msg = self.session.send(stream, 'complete_reply',
486 486 matches, parent, ident)
487 487 self.log.debug("%s", completion_msg)
488 488
489 489 def object_info_request(self, stream, ident, parent):
490 490 content = parent['content']
491 491 object_info = self.shell.object_inspect(content['oname'],
492 492 detail_level = content.get('detail_level', 0)
493 493 )
494 494 # Before we send this object over, we scrub it for JSON usage
495 495 oinfo = json_clean(object_info)
496 496 msg = self.session.send(stream, 'object_info_reply',
497 497 oinfo, parent, ident)
498 498 self.log.debug("%s", msg)
499 499
500 500 def history_request(self, stream, ident, parent):
501 501 # We need to pull these out, as passing **kwargs doesn't work with
502 502 # unicode keys before Python 2.6.5.
503 503 hist_access_type = parent['content']['hist_access_type']
504 504 raw = parent['content']['raw']
505 505 output = parent['content']['output']
506 506 if hist_access_type == 'tail':
507 507 n = parent['content']['n']
508 508 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
509 509 include_latest=True)
510 510
511 511 elif hist_access_type == 'range':
512 512 session = parent['content']['session']
513 513 start = parent['content']['start']
514 514 stop = parent['content']['stop']
515 515 hist = self.shell.history_manager.get_range(session, start, stop,
516 516 raw=raw, output=output)
517 517
518 518 elif hist_access_type == 'search':
519 519 n = parent['content'].get('n')
520 520 unique = parent['content'].get('unique', False)
521 521 pattern = parent['content']['pattern']
522 522 hist = self.shell.history_manager.search(
523 523 pattern, raw=raw, output=output, n=n, unique=unique)
524 524
525 525 else:
526 526 hist = []
527 527 hist = list(hist)
528 528 content = {'history' : hist}
529 529 content = json_clean(content)
530 530 msg = self.session.send(stream, 'history_reply',
531 531 content, parent, ident)
532 532 self.log.debug("Sending history reply with %i entries", len(hist))
533 533
534 534 def connect_request(self, stream, ident, parent):
535 535 if self._recorded_ports is not None:
536 536 content = self._recorded_ports.copy()
537 537 else:
538 538 content = {}
539 539 msg = self.session.send(stream, 'connect_reply',
540 540 content, parent, ident)
541 541 self.log.debug("%s", msg)
542 542
543 543 def kernel_info_request(self, stream, ident, parent):
544 544 vinfo = {
545 545 'protocol_version': protocol_version,
546 546 'ipython_version': ipython_version,
547 547 'language_version': language_version,
548 548 'language': 'python',
549 549 }
550 550 msg = self.session.send(stream, 'kernel_info_reply',
551 551 vinfo, parent, ident)
552 552 self.log.debug("%s", msg)
553 553
554 554 def shutdown_request(self, stream, ident, parent):
555 555 self.shell.exit_now = True
556 556 content = dict(status='ok')
557 557 content.update(parent['content'])
558 558 self.session.send(stream, u'shutdown_reply', content, parent, ident=ident)
559 559 # same content, but different msg_id for broadcasting on IOPub
560 560 self._shutdown_message = self.session.msg(u'shutdown_reply',
561 561 content, parent
562 562 )
563 563
564 564 self._at_shutdown()
565 565 # call sys.exit after a short delay
566 566 loop = ioloop.IOLoop.instance()
567 567 loop.add_timeout(time.time()+0.1, loop.stop)
568 568
569 569 #---------------------------------------------------------------------------
570 570 # Engine methods
571 571 #---------------------------------------------------------------------------
572 572
573 573 def apply_request(self, stream, ident, parent):
574 574 try:
575 575 content = parent[u'content']
576 576 bufs = parent[u'buffers']
577 577 msg_id = parent['header']['msg_id']
578 578 except:
579 579 self.log.error("Got bad msg: %s", parent, exc_info=True)
580 580 return
581 581
582 582 self._publish_status(u'busy', parent)
583 583
584 584 # Set the parent message of the display hook and out streams.
585 585 shell = self.shell
586 586 shell.displayhook.set_parent(parent)
587 587 shell.display_pub.set_parent(parent)
588 588 shell.data_pub.set_parent(parent)
589 589 try:
590 590 sys.stdout.set_parent(parent)
591 591 except AttributeError:
592 592 pass
593 593 try:
594 594 sys.stderr.set_parent(parent)
595 595 except AttributeError:
596 596 pass
597 597
598 598 # pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
599 599 # self.iopub_socket.send(pyin_msg)
600 600 # self.session.send(self.iopub_socket, u'pyin', {u'code':code},parent=parent)
601 601 md = self._make_metadata(parent['metadata'])
602 602 try:
603 603 working = shell.user_ns
604 604
605 605 prefix = "_"+str(msg_id).replace("-","")+"_"
606 606
607 607 f,args,kwargs = unpack_apply_message(bufs, working, copy=False)
608 608
609 609 fname = getattr(f, '__name__', 'f')
610 610
611 611 fname = prefix+"f"
612 612 argname = prefix+"args"
613 613 kwargname = prefix+"kwargs"
614 614 resultname = prefix+"result"
615 615
616 616 ns = { fname : f, argname : args, kwargname : kwargs , resultname : None }
617 617 # print ns
618 618 working.update(ns)
619 619 code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname)
620 620 try:
621 621 exec code in shell.user_global_ns, shell.user_ns
622 622 result = working.get(resultname)
623 623 finally:
624 624 for key in ns.iterkeys():
625 625 working.pop(key)
626 626
627 627 result_buf = serialize_object(result,
628 628 buffer_threshold=self.session.buffer_threshold,
629 629 item_threshold=self.session.item_threshold,
630 630 )
631 631
632 632 except:
633 633 # invoke IPython traceback formatting
634 634 shell.showtraceback()
635 635 # FIXME - fish exception info out of shell, possibly left there by
636 636 # run_code. We'll need to clean up this logic later.
637 637 reply_content = {}
638 638 if shell._reply_content is not None:
639 639 reply_content.update(shell._reply_content)
640 640 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='apply')
641 641 reply_content['engine_info'] = e_info
642 642 # reset after use
643 643 shell._reply_content = None
644 644
645 645 self.session.send(self.iopub_socket, u'pyerr', reply_content, parent=parent,
646 646 ident=self._topic('pyerr'))
647 647 self.log.info("Exception in apply request:\n%s", '\n'.join(reply_content['traceback']))
648 648 result_buf = []
649 649
650 650 if reply_content['ename'] == 'UnmetDependency':
651 651 md['dependencies_met'] = False
652 652 else:
653 653 reply_content = {'status' : 'ok'}
654 654
655 655 # put 'ok'/'error' status in header, for scheduler introspection:
656 656 md['status'] = reply_content['status']
657 657
658 658 # flush i/o
659 659 sys.stdout.flush()
660 660 sys.stderr.flush()
661 661
662 662 reply_msg = self.session.send(stream, u'apply_reply', reply_content,
663 663 parent=parent, ident=ident,buffers=result_buf, metadata=md)
664 664
665 665 self._publish_status(u'idle', parent)
666 666
667 667 #---------------------------------------------------------------------------
668 668 # Control messages
669 669 #---------------------------------------------------------------------------
670 670
671 671 def abort_request(self, stream, ident, parent):
672 672 """abort a specifig msg by id"""
673 673 msg_ids = parent['content'].get('msg_ids', None)
674 674 if isinstance(msg_ids, basestring):
675 675 msg_ids = [msg_ids]
676 676 if not msg_ids:
677 677 self.abort_queues()
678 678 for mid in msg_ids:
679 679 self.aborted.add(str(mid))
680 680
681 681 content = dict(status='ok')
682 682 reply_msg = self.session.send(stream, 'abort_reply', content=content,
683 683 parent=parent, ident=ident)
684 684 self.log.debug("%s", reply_msg)
685 685
686 686 def clear_request(self, stream, idents, parent):
687 687 """Clear our namespace."""
688 688 self.shell.reset(False)
689 689 msg = self.session.send(stream, 'clear_reply', ident=idents, parent=parent,
690 690 content = dict(status='ok'))
691 691
692 692
693 693 #---------------------------------------------------------------------------
694 694 # Protected interface
695 695 #---------------------------------------------------------------------------
696 696
697 697 def _wrap_exception(self, method=None):
698 698 # import here, because _wrap_exception is only used in parallel,
699 699 # and parallel has higher min pyzmq version
700 700 from IPython.parallel.error import wrap_exception
701 701 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method=method)
702 702 content = wrap_exception(e_info)
703 703 return content
704 704
705 705 def _topic(self, topic):
706 706 """prefixed topic for IOPub messages"""
707 707 if self.int_id >= 0:
708 708 base = "engine.%i" % self.int_id
709 709 else:
710 710 base = "kernel.%s" % self.ident
711 711
712 712 return py3compat.cast_bytes("%s.%s" % (base, topic))
713 713
714 714 def _abort_queues(self):
715 715 for stream in self.shell_streams:
716 716 if stream:
717 717 self._abort_queue(stream)
718 718
719 719 def _abort_queue(self, stream):
720 720 poller = zmq.Poller()
721 721 poller.register(stream.socket, zmq.POLLIN)
722 722 while True:
723 723 idents,msg = self.session.recv(stream, zmq.NOBLOCK, content=True)
724 724 if msg is None:
725 725 return
726 726
727 727 self.log.info("Aborting:")
728 728 self.log.info("%s", msg)
729 729 msg_type = msg['header']['msg_type']
730 730 reply_type = msg_type.split('_')[0] + '_reply'
731 731
732 732 status = {'status' : 'aborted'}
733 733 md = {'engine' : self.ident}
734 734 md.update(status)
735 735 reply_msg = self.session.send(stream, reply_type, metadata=md,
736 736 content=status, parent=msg, ident=idents)
737 737 self.log.debug("%s", reply_msg)
738 738 # We need to wait a bit for requests to come in. This can probably
739 739 # be set shorter for true asynchronous clients.
740 740 poller.poll(50)
741 741
742 742
743 743 def _no_raw_input(self):
744 744 """Raise StdinNotImplentedError if active frontend doesn't support
745 745 stdin."""
746 746 raise StdinNotImplementedError("raw_input was called, but this "
747 747 "frontend does not support stdin.")
748 748
749 749 def _raw_input(self, prompt, ident, parent):
750 750 # Flush output before making the request.
751 751 sys.stderr.flush()
752 752 sys.stdout.flush()
753 753 # flush the stdin socket, to purge stale replies
754 754 while True:
755 755 try:
756 756 self.stdin_socket.recv_multipart(zmq.NOBLOCK)
757 757 except zmq.ZMQError as e:
758 758 if e.errno == zmq.EAGAIN:
759 759 break
760 760 else:
761 761 raise
762 762
763 763 # Send the input request.
764 764 content = json_clean(dict(prompt=prompt))
765 765 self.session.send(self.stdin_socket, u'input_request', content, parent,
766 766 ident=ident)
767 767
768 768 # Await a response.
769 769 while True:
770 770 try:
771 771 ident, reply = self.session.recv(self.stdin_socket, 0)
772 772 except Exception:
773 773 self.log.warn("Invalid Message:", exc_info=True)
774 774 except KeyboardInterrupt:
775 775 # re-raise KeyboardInterrupt, to truncate traceback
776 776 raise KeyboardInterrupt
777 777 else:
778 778 break
779 779 try:
780 780 value = py3compat.unicode_to_str(reply['content']['value'])
781 781 except:
782 782 self.log.error("Got bad raw_input reply: ")
783 783 self.log.error("%s", parent)
784 784 value = ''
785 785 if value == '\x04':
786 786 # EOF
787 787 raise EOFError
788 788 return value
789 789
790 790 def _complete(self, msg):
791 791 c = msg['content']
792 792 try:
793 793 cpos = int(c['cursor_pos'])
794 794 except:
795 795 # If we don't get something that we can convert to an integer, at
796 796 # least attempt the completion guessing the cursor is at the end of
797 797 # the text, if there's any, and otherwise of the line
798 798 cpos = len(c['text'])
799 799 if cpos==0:
800 800 cpos = len(c['line'])
801 801 return self.shell.complete(c['text'], c['line'], cpos)
802 802
803 803 def _at_shutdown(self):
804 804 """Actions taken at shutdown by the kernel, called by python's atexit.
805 805 """
806 806 # io.rprint("Kernel at_shutdown") # dbg
807 807 if self._shutdown_message is not None:
808 808 self.session.send(self.iopub_socket, self._shutdown_message, ident=self._topic('shutdown'))
809 809 self.log.debug("%s", self._shutdown_message)
810 810 [ s.flush(zmq.POLLOUT) for s in self.shell_streams ]
811 811
General Comments 0
You need to be logged in to leave comments. Login now