##// END OF EJS Templates
Adjustment to console signal-handling...
MinRK -
Show More
@@ -1,137 +1,149 b''
1 """ A minimal application using the ZMQ-based terminal IPython frontend.
1 """ A minimal application using the ZMQ-based terminal IPython frontend.
2
2
3 This is not a complete console app, as subprocess will not be able to receive
3 This is not a complete console app, as subprocess will not be able to receive
4 input, there is no real readline support, among other limitations.
4 input, there is no real readline support, among other limitations.
5
5
6 Authors:
6 Authors:
7
7
8 * Min RK
8 * Min RK
9 * Paul Ivanov
9 * Paul Ivanov
10
10
11 """
11 """
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 import signal
16 import signal
17 import sys
17 import sys
18 import time
18 import time
19
19
20 from IPython.frontend.terminal.ipapp import TerminalIPythonApp, frontend_flags as term_flags
20 from IPython.frontend.terminal.ipapp import TerminalIPythonApp, frontend_flags as term_flags
21
21
22 from IPython.utils.traitlets import (
22 from IPython.utils.traitlets import (
23 Dict, List, Unicode, Int, CaselessStrEnum, CBool, Any
23 Dict, List, Unicode, Int, CaselessStrEnum, CBool, Any
24 )
24 )
25 from IPython.utils.warn import warn,error
26
25 from IPython.zmq.ipkernel import IPKernelApp
27 from IPython.zmq.ipkernel import IPKernelApp
26 from IPython.zmq.session import Session, default_secure
28 from IPython.zmq.session import Session, default_secure
27 from IPython.zmq.zmqshell import ZMQInteractiveShell
29 from IPython.zmq.zmqshell import ZMQInteractiveShell
28 from IPython.frontend.kernelmixinapp import (
30 from IPython.frontend.kernelmixinapp import (
29 IPythonMixinConsoleApp, app_aliases, app_flags, aliases, app_aliases, flags
31 IPythonMixinConsoleApp, app_aliases, app_flags, aliases, app_aliases, flags
30 )
32 )
31
33
32 from IPython.frontend.terminal.console.interactiveshell import ZMQTerminalInteractiveShell
34 from IPython.frontend.terminal.console.interactiveshell import ZMQTerminalInteractiveShell
33
35
34 #-----------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
35 # Globals
37 # Globals
36 #-----------------------------------------------------------------------------
38 #-----------------------------------------------------------------------------
37
39
38 _examples = """
40 _examples = """
39 ipython console # start the ZMQ-based console
41 ipython console # start the ZMQ-based console
40 ipython console --existing # connect to an existing ipython session
42 ipython console --existing # connect to an existing ipython session
41 """
43 """
42
44
43 #-----------------------------------------------------------------------------
45 #-----------------------------------------------------------------------------
44 # Flags and Aliases
46 # Flags and Aliases
45 #-----------------------------------------------------------------------------
47 #-----------------------------------------------------------------------------
46
48
47 # copy flags from mixin:
49 # copy flags from mixin:
48 flags = dict(flags)
50 flags = dict(flags)
49 # start with mixin frontend flags:
51 # start with mixin frontend flags:
50 frontend_flags = dict(app_flags)
52 frontend_flags = dict(app_flags)
51 # add TerminalIPApp flags:
53 # add TerminalIPApp flags:
52 frontend_flags.update(term_flags)
54 frontend_flags.update(term_flags)
53 # pylab is not frontend-specific in two-process IPython
55 # pylab is not frontend-specific in two-process IPython
54 frontend_flags.pop('pylab')
56 frontend_flags.pop('pylab')
55 # disable quick startup, as it won't propagate to the kernel anyway
57 # disable quick startup, as it won't propagate to the kernel anyway
56 frontend_flags.pop('quick')
58 frontend_flags.pop('quick')
57 # update full dict with frontend flags:
59 # update full dict with frontend flags:
58 flags.update(frontend_flags)
60 flags.update(frontend_flags)
59
61
60 # copy flags from mixin
62 # copy flags from mixin
61 aliases = dict(aliases)
63 aliases = dict(aliases)
62 # start with mixin frontend flags
64 # start with mixin frontend flags
63 frontend_aliases = dict(app_aliases)
65 frontend_aliases = dict(app_aliases)
64 # load updated frontend flags into full dict
66 # load updated frontend flags into full dict
65 aliases.update(frontend_aliases)
67 aliases.update(frontend_aliases)
66
68
67 # get flags&aliases into sets, and remove a couple that
69 # get flags&aliases into sets, and remove a couple that
68 # shouldn't be scrubbed from backend flags:
70 # shouldn't be scrubbed from backend flags:
69 frontend_aliases = set(frontend_aliases.keys())
71 frontend_aliases = set(frontend_aliases.keys())
70 frontend_flags = set(frontend_flags.keys())
72 frontend_flags = set(frontend_flags.keys())
71
73
72
74
73 #-----------------------------------------------------------------------------
75 #-----------------------------------------------------------------------------
74 # Classes
76 # Classes
75 #-----------------------------------------------------------------------------
77 #-----------------------------------------------------------------------------
76
78
77
79
78 class ZMQTerminalIPythonApp(TerminalIPythonApp, IPythonMixinConsoleApp):
80 class ZMQTerminalIPythonApp(TerminalIPythonApp, IPythonMixinConsoleApp):
79 name = "ipython-console"
81 name = "ipython-console"
80 """Start a terminal frontend to the IPython zmq kernel."""
82 """Start a terminal frontend to the IPython zmq kernel."""
81
83
82 description = """
84 description = """
83 The IPython terminal-based Console.
85 The IPython terminal-based Console.
84
86
85 This launches a Console application inside a terminal.
87 This launches a Console application inside a terminal.
86
88
87 The Console supports various extra features beyond the traditional
89 The Console supports various extra features beyond the traditional
88 single-process Terminal IPython shell, such as connecting to an
90 single-process Terminal IPython shell, such as connecting to an
89 existing ipython session, via:
91 existing ipython session, via:
90
92
91 ipython console --existing
93 ipython console --existing
92
94
93 where the previous session could have been created by another ipython
95 where the previous session could have been created by another ipython
94 console, an ipython qtconsole, or by opening an ipython notebook.
96 console, an ipython qtconsole, or by opening an ipython notebook.
95
97
96 """
98 """
97 examples = _examples
99 examples = _examples
98
100
99 classes = List([IPKernelApp, ZMQTerminalInteractiveShell, Session])
101 classes = List([IPKernelApp, ZMQTerminalInteractiveShell, Session])
100 flags = Dict(flags)
102 flags = Dict(flags)
101 aliases = Dict(aliases)
103 aliases = Dict(aliases)
102 subcommands = Dict()
104 subcommands = Dict()
103
105
104 def parse_command_line(self, argv=None):
106 def parse_command_line(self, argv=None):
105 super(ZMQTerminalIPythonApp, self).parse_command_line(argv)
107 super(ZMQTerminalIPythonApp, self).parse_command_line(argv)
106 self.swallow_args(frontend_aliases,frontend_flags,argv=argv)
108 self.swallow_args(frontend_aliases,frontend_flags,argv=argv)
107
109
108 def init_shell(self):
110 def init_shell(self):
109 IPythonMixinConsoleApp.initialize(self)
111 IPythonMixinConsoleApp.initialize(self)
110 # relay sigint to kernel
112 # relay sigint to kernel
111 signal.signal(signal.SIGINT, self.handle_sigint)
113 signal.signal(signal.SIGINT, self.handle_sigint)
112 self.shell = ZMQTerminalInteractiveShell.instance(config=self.config,
114 self.shell = ZMQTerminalInteractiveShell.instance(config=self.config,
113 display_banner=False, profile_dir=self.profile_dir,
115 display_banner=False, profile_dir=self.profile_dir,
114 ipython_dir=self.ipython_dir, kernel_manager=self.kernel_manager)
116 ipython_dir=self.ipython_dir, kernel_manager=self.kernel_manager)
115
117
116 def handle_sigint(self, *args):
118 def handle_sigint(self, *args):
117 self.shell.write('KeyboardInterrupt\n')
119 if self.shell._executing:
118 if self.kernel_manager.has_kernel:
120 if self.kernel_manager.has_kernel:
119 self.kernel_manager.interrupt_kernel()
121 # interrupt already gets passed to subprocess by signal handler.
122 # Only if we prevent that should we need to explicitly call
123 # interrupt_kernel, until which time, this would result in a
124 # double-interrupt:
125 # self.kernel_manager.interrupt_kernel()
126 pass
127 else:
128 self.shell.write_err('\n')
129 error("Cannot interrupt kernels we didn't start.\n")
120 else:
130 else:
121 print 'Kernel process is either remote or unspecified.',
131 # raise the KeyboardInterrupt if we aren't waiting for execution,
122 print 'Cannot interrupt.'
132 # so that the interact loop advances, and prompt is redrawn, etc.
133 raise KeyboardInterrupt
134
123
135
124 def init_code(self):
136 def init_code(self):
125 # no-op in the frontend, code gets run in the backend
137 # no-op in the frontend, code gets run in the backend
126 pass
138 pass
127
139
128 def launch_new_instance():
140 def launch_new_instance():
129 """Create and run a full blown IPython instance"""
141 """Create and run a full blown IPython instance"""
130 app = ZMQTerminalIPythonApp.instance()
142 app = ZMQTerminalIPythonApp.instance()
131 app.initialize()
143 app.initialize()
132 app.start()
144 app.start()
133
145
134
146
135 if __name__ == '__main__':
147 if __name__ == '__main__':
136 launch_new_instance()
148 launch_new_instance()
137
149
@@ -1,645 +1,648 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 """A simple interactive kernel that talks to a frontend over 0MQ.
2 """A simple interactive kernel that talks to a frontend over 0MQ.
3
3
4 Things to do:
4 Things to do:
5
5
6 * Implement `set_parent` logic. Right before doing exec, the Kernel should
6 * Implement `set_parent` logic. Right before doing exec, the Kernel should
7 call set_parent on all the PUB objects with the message about to be executed.
7 call set_parent on all the PUB objects with the message about to be executed.
8 * Implement random port and security key logic.
8 * Implement random port and security key logic.
9 * Implement control messages.
9 * Implement control messages.
10 * Implement event loop and poll version.
10 * Implement event loop and poll version.
11 """
11 """
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 from __future__ import print_function
16 from __future__ import print_function
17
17
18 # Standard library imports.
18 # Standard library imports.
19 import __builtin__
19 import __builtin__
20 import atexit
20 import atexit
21 import sys
21 import sys
22 import time
22 import time
23 import traceback
23 import traceback
24 import logging
24 import logging
25 from signal import (
25 from signal import (
26 signal, default_int_handler, SIGINT, SIG_IGN
26 signal, default_int_handler, SIGINT, SIG_IGN
27 )
27 )
28 # System library imports.
28 # System library imports.
29 import zmq
29 import zmq
30
30
31 # Local imports.
31 # Local imports.
32 from IPython.core import pylabtools
32 from IPython.core import pylabtools
33 from IPython.config.configurable import Configurable
33 from IPython.config.configurable import Configurable
34 from IPython.config.application import boolean_flag, catch_config_error
34 from IPython.config.application import boolean_flag, catch_config_error
35 from IPython.core.application import ProfileDir
35 from IPython.core.application import ProfileDir
36 from IPython.core.error import StdinNotImplementedError
36 from IPython.core.error import StdinNotImplementedError
37 from IPython.core.shellapp import (
37 from IPython.core.shellapp import (
38 InteractiveShellApp, shell_flags, shell_aliases
38 InteractiveShellApp, shell_flags, shell_aliases
39 )
39 )
40 from IPython.utils import io
40 from IPython.utils import io
41 from IPython.utils import py3compat
41 from IPython.utils import py3compat
42 from IPython.utils.jsonutil import json_clean
42 from IPython.utils.jsonutil import json_clean
43 from IPython.utils.traitlets import (
43 from IPython.utils.traitlets import (
44 Any, Instance, Float, Dict, CaselessStrEnum
44 Any, Instance, Float, Dict, CaselessStrEnum
45 )
45 )
46
46
47 from entry_point import base_launch_kernel
47 from entry_point import base_launch_kernel
48 from kernelapp import KernelApp, kernel_flags, kernel_aliases
48 from kernelapp import KernelApp, kernel_flags, kernel_aliases
49 from session import Session, Message
49 from session import Session, Message
50 from zmqshell import ZMQInteractiveShell
50 from zmqshell import ZMQInteractiveShell
51
51
52
52
53 #-----------------------------------------------------------------------------
53 #-----------------------------------------------------------------------------
54 # Main kernel class
54 # Main kernel class
55 #-----------------------------------------------------------------------------
55 #-----------------------------------------------------------------------------
56
56
57 class Kernel(Configurable):
57 class Kernel(Configurable):
58
58
59 #---------------------------------------------------------------------------
59 #---------------------------------------------------------------------------
60 # Kernel interface
60 # Kernel interface
61 #---------------------------------------------------------------------------
61 #---------------------------------------------------------------------------
62
62
63 # attribute to override with a GUI
63 # attribute to override with a GUI
64 eventloop = Any(None)
64 eventloop = Any(None)
65
65
66 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
66 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
67 session = Instance(Session)
67 session = Instance(Session)
68 profile_dir = Instance('IPython.core.profiledir.ProfileDir')
68 profile_dir = Instance('IPython.core.profiledir.ProfileDir')
69 shell_socket = Instance('zmq.Socket')
69 shell_socket = Instance('zmq.Socket')
70 iopub_socket = Instance('zmq.Socket')
70 iopub_socket = Instance('zmq.Socket')
71 stdin_socket = Instance('zmq.Socket')
71 stdin_socket = Instance('zmq.Socket')
72 log = Instance(logging.Logger)
72 log = Instance(logging.Logger)
73
73
74 # Private interface
74 # Private interface
75
75
76 # Time to sleep after flushing the stdout/err buffers in each execute
76 # Time to sleep after flushing the stdout/err buffers in each execute
77 # cycle. While this introduces a hard limit on the minimal latency of the
77 # cycle. While this introduces a hard limit on the minimal latency of the
78 # execute cycle, it helps prevent output synchronization problems for
78 # execute cycle, it helps prevent output synchronization problems for
79 # clients.
79 # clients.
80 # Units are in seconds. The minimum zmq latency on local host is probably
80 # Units are in seconds. The minimum zmq latency on local host is probably
81 # ~150 microseconds, set this to 500us for now. We may need to increase it
81 # ~150 microseconds, set this to 500us for now. We may need to increase it
82 # a little if it's not enough after more interactive testing.
82 # a little if it's not enough after more interactive testing.
83 _execute_sleep = Float(0.0005, config=True)
83 _execute_sleep = Float(0.0005, config=True)
84
84
85 # Frequency of the kernel's event loop.
85 # Frequency of the kernel's event loop.
86 # Units are in seconds, kernel subclasses for GUI toolkits may need to
86 # Units are in seconds, kernel subclasses for GUI toolkits may need to
87 # adapt to milliseconds.
87 # adapt to milliseconds.
88 _poll_interval = Float(0.05, config=True)
88 _poll_interval = Float(0.05, config=True)
89
89
90 # If the shutdown was requested over the network, we leave here the
90 # If the shutdown was requested over the network, we leave here the
91 # necessary reply message so it can be sent by our registered atexit
91 # necessary reply message so it can be sent by our registered atexit
92 # handler. This ensures that the reply is only sent to clients truly at
92 # handler. This ensures that the reply is only sent to clients truly at
93 # the end of our shutdown process (which happens after the underlying
93 # the end of our shutdown process (which happens after the underlying
94 # IPython shell's own shutdown).
94 # IPython shell's own shutdown).
95 _shutdown_message = None
95 _shutdown_message = None
96
96
97 # This is a dict of port number that the kernel is listening on. It is set
97 # This is a dict of port number that the kernel is listening on. It is set
98 # by record_ports and used by connect_request.
98 # by record_ports and used by connect_request.
99 _recorded_ports = Dict()
99 _recorded_ports = Dict()
100
100
101
101
102
102
103 def __init__(self, **kwargs):
103 def __init__(self, **kwargs):
104 super(Kernel, self).__init__(**kwargs)
104 super(Kernel, self).__init__(**kwargs)
105
105
106 # Before we even start up the shell, register *first* our exit handlers
106 # Before we even start up the shell, register *first* our exit handlers
107 # so they come before the shell's
107 # so they come before the shell's
108 atexit.register(self._at_shutdown)
108 atexit.register(self._at_shutdown)
109
109
110 # Initialize the InteractiveShell subclass
110 # Initialize the InteractiveShell subclass
111 self.shell = ZMQInteractiveShell.instance(config=self.config,
111 self.shell = ZMQInteractiveShell.instance(config=self.config,
112 profile_dir = self.profile_dir,
112 profile_dir = self.profile_dir,
113 )
113 )
114 self.shell.displayhook.session = self.session
114 self.shell.displayhook.session = self.session
115 self.shell.displayhook.pub_socket = self.iopub_socket
115 self.shell.displayhook.pub_socket = self.iopub_socket
116 self.shell.display_pub.session = self.session
116 self.shell.display_pub.session = self.session
117 self.shell.display_pub.pub_socket = self.iopub_socket
117 self.shell.display_pub.pub_socket = self.iopub_socket
118
118
119 # TMP - hack while developing
119 # TMP - hack while developing
120 self.shell._reply_content = None
120 self.shell._reply_content = None
121
121
122 # Build dict of handlers for message types
122 # Build dict of handlers for message types
123 msg_types = [ 'execute_request', 'complete_request',
123 msg_types = [ 'execute_request', 'complete_request',
124 'object_info_request', 'history_request',
124 'object_info_request', 'history_request',
125 'connect_request', 'shutdown_request']
125 'connect_request', 'shutdown_request']
126 self.handlers = {}
126 self.handlers = {}
127 for msg_type in msg_types:
127 for msg_type in msg_types:
128 self.handlers[msg_type] = getattr(self, msg_type)
128 self.handlers[msg_type] = getattr(self, msg_type)
129
129
130 def do_one_iteration(self):
130 def do_one_iteration(self):
131 """Do one iteration of the kernel's evaluation loop.
131 """Do one iteration of the kernel's evaluation loop.
132 """
132 """
133 try:
133 try:
134 ident,msg = self.session.recv(self.shell_socket, zmq.NOBLOCK)
134 ident,msg = self.session.recv(self.shell_socket, zmq.NOBLOCK)
135 except Exception:
135 except Exception:
136 self.log.warn("Invalid Message:", exc_info=True)
136 self.log.warn("Invalid Message:", exc_info=True)
137 return
137 return
138 if msg is None:
138 if msg is None:
139 return
139 return
140
140
141 msg_type = msg['header']['msg_type']
141 msg_type = msg['header']['msg_type']
142
142
143 # This assert will raise in versions of zeromq 2.0.7 and lesser.
143 # This assert will raise in versions of zeromq 2.0.7 and lesser.
144 # We now require 2.0.8 or above, so we can uncomment for safety.
144 # We now require 2.0.8 or above, so we can uncomment for safety.
145 # print(ident,msg, file=sys.__stdout__)
145 # print(ident,msg, file=sys.__stdout__)
146 assert ident is not None, "Missing message part."
146 assert ident is not None, "Missing message part."
147
147
148 # Print some info about this message and leave a '--->' marker, so it's
148 # Print some info about this message and leave a '--->' marker, so it's
149 # easier to trace visually the message chain when debugging. Each
149 # easier to trace visually the message chain when debugging. Each
150 # handler prints its message at the end.
150 # handler prints its message at the end.
151 self.log.debug('\n*** MESSAGE TYPE:'+str(msg_type)+'***')
151 self.log.debug('\n*** MESSAGE TYPE:'+str(msg_type)+'***')
152 self.log.debug(' Content: '+str(msg['content'])+'\n --->\n ')
152 self.log.debug(' Content: '+str(msg['content'])+'\n --->\n ')
153
153
154 # Find and call actual handler for message
154 # Find and call actual handler for message
155 handler = self.handlers.get(msg_type, None)
155 handler = self.handlers.get(msg_type, None)
156 if handler is None:
156 if handler is None:
157 self.log.error("UNKNOWN MESSAGE TYPE:" +str(msg))
157 self.log.error("UNKNOWN MESSAGE TYPE:" +str(msg))
158 else:
158 else:
159 handler(ident, msg)
159 handler(ident, msg)
160
160
161 # Check whether we should exit, in case the incoming message set the
161 # Check whether we should exit, in case the incoming message set the
162 # exit flag on
162 # exit flag on
163 if self.shell.exit_now:
163 if self.shell.exit_now:
164 self.log.debug('\nExiting IPython kernel...')
164 self.log.debug('\nExiting IPython kernel...')
165 # We do a normal, clean exit, which allows any actions registered
165 # We do a normal, clean exit, which allows any actions registered
166 # via atexit (such as history saving) to take place.
166 # via atexit (such as history saving) to take place.
167 sys.exit(0)
167 sys.exit(0)
168
168
169
169
170 def start(self):
170 def start(self):
171 """ Start the kernel main loop.
171 """ Start the kernel main loop.
172 """
172 """
173 # a KeyboardInterrupt (SIGINT) can occur on any python statement, so
173 # a KeyboardInterrupt (SIGINT) can occur on any python statement, so
174 # let's ignore (SIG_IGN) them until we're in a place to handle them properly
174 # let's ignore (SIG_IGN) them until we're in a place to handle them properly
175 signal(SIGINT,SIG_IGN)
175 signal(SIGINT,SIG_IGN)
176 poller = zmq.Poller()
176 poller = zmq.Poller()
177 poller.register(self.shell_socket, zmq.POLLIN)
177 poller.register(self.shell_socket, zmq.POLLIN)
178 # loop while self.eventloop has not been overridden
178 # loop while self.eventloop has not been overridden
179 while self.eventloop is None:
179 while self.eventloop is None:
180 try:
180 try:
181 # scale by extra factor of 10, because there is no
181 # scale by extra factor of 10, because there is no
182 # reason for this to be anything less than ~ 0.1s
182 # reason for this to be anything less than ~ 0.1s
183 # since it is a real poller and will respond
183 # since it is a real poller and will respond
184 # to events immediately
184 # to events immediately
185
185
186 # double nested try/except, to properly catch KeyboardInterrupt
186 # double nested try/except, to properly catch KeyboardInterrupt
187 # due to pyzmq Issue #130
187 # due to pyzmq Issue #130
188 try:
188 try:
189 poller.poll(10*1000*self._poll_interval)
189 poller.poll(10*1000*self._poll_interval)
190 # restore raising of KeyboardInterrupt
190 # restore raising of KeyboardInterrupt
191 signal(SIGINT, default_int_handler)
191 signal(SIGINT, default_int_handler)
192 self.do_one_iteration()
192 self.do_one_iteration()
193 except:
193 except:
194 raise
194 raise
195 finally:
195 finally:
196 # prevent raising of KeyboardInterrupt
196 # prevent raising of KeyboardInterrupt
197 signal(SIGINT,SIG_IGN)
197 signal(SIGINT,SIG_IGN)
198 except KeyboardInterrupt:
198 except KeyboardInterrupt:
199 # Ctrl-C shouldn't crash the kernel
199 # Ctrl-C shouldn't crash the kernel
200 io.raw_print("KeyboardInterrupt caught in kernel")
200 io.raw_print("KeyboardInterrupt caught in kernel")
201 # stop ignoring sigint, now that we are out of our own loop,
202 # we don't want to prevent future code from handling it
203 signal(SIGINT, default_int_handler)
201 if self.eventloop is not None:
204 if self.eventloop is not None:
202 try:
205 try:
203 self.eventloop(self)
206 self.eventloop(self)
204 except KeyboardInterrupt:
207 except KeyboardInterrupt:
205 # Ctrl-C shouldn't crash the kernel
208 # Ctrl-C shouldn't crash the kernel
206 io.raw_print("KeyboardInterrupt caught in kernel")
209 io.raw_print("KeyboardInterrupt caught in kernel")
207
210
208
211
209 def record_ports(self, ports):
212 def record_ports(self, ports):
210 """Record the ports that this kernel is using.
213 """Record the ports that this kernel is using.
211
214
212 The creator of the Kernel instance must call this methods if they
215 The creator of the Kernel instance must call this methods if they
213 want the :meth:`connect_request` method to return the port numbers.
216 want the :meth:`connect_request` method to return the port numbers.
214 """
217 """
215 self._recorded_ports = ports
218 self._recorded_ports = ports
216
219
217 #---------------------------------------------------------------------------
220 #---------------------------------------------------------------------------
218 # Kernel request handlers
221 # Kernel request handlers
219 #---------------------------------------------------------------------------
222 #---------------------------------------------------------------------------
220
223
221 def _publish_pyin(self, code, parent):
224 def _publish_pyin(self, code, parent):
222 """Publish the code request on the pyin stream."""
225 """Publish the code request on the pyin stream."""
223
226
224 self.session.send(self.iopub_socket, u'pyin', {u'code':code},
227 self.session.send(self.iopub_socket, u'pyin', {u'code':code},
225 parent=parent)
228 parent=parent)
226
229
227 def execute_request(self, ident, parent):
230 def execute_request(self, ident, parent):
228
231
229 self.session.send(self.iopub_socket,
232 self.session.send(self.iopub_socket,
230 u'status',
233 u'status',
231 {u'execution_state':u'busy'},
234 {u'execution_state':u'busy'},
232 parent=parent )
235 parent=parent )
233
236
234 try:
237 try:
235 content = parent[u'content']
238 content = parent[u'content']
236 code = content[u'code']
239 code = content[u'code']
237 silent = content[u'silent']
240 silent = content[u'silent']
238 except:
241 except:
239 self.log.error("Got bad msg: ")
242 self.log.error("Got bad msg: ")
240 self.log.error(str(Message(parent)))
243 self.log.error(str(Message(parent)))
241 return
244 return
242
245
243 shell = self.shell # we'll need this a lot here
246 shell = self.shell # we'll need this a lot here
244
247
245 # Replace raw_input. Note that is not sufficient to replace
248 # Replace raw_input. Note that is not sufficient to replace
246 # raw_input in the user namespace.
249 # raw_input in the user namespace.
247 if content.get('allow_stdin', False):
250 if content.get('allow_stdin', False):
248 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
251 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
249 else:
252 else:
250 raw_input = lambda prompt='' : self._no_raw_input()
253 raw_input = lambda prompt='' : self._no_raw_input()
251
254
252 if py3compat.PY3:
255 if py3compat.PY3:
253 __builtin__.input = raw_input
256 __builtin__.input = raw_input
254 else:
257 else:
255 __builtin__.raw_input = raw_input
258 __builtin__.raw_input = raw_input
256
259
257 # Set the parent message of the display hook and out streams.
260 # Set the parent message of the display hook and out streams.
258 shell.displayhook.set_parent(parent)
261 shell.displayhook.set_parent(parent)
259 shell.display_pub.set_parent(parent)
262 shell.display_pub.set_parent(parent)
260 sys.stdout.set_parent(parent)
263 sys.stdout.set_parent(parent)
261 sys.stderr.set_parent(parent)
264 sys.stderr.set_parent(parent)
262
265
263 # Re-broadcast our input for the benefit of listening clients, and
266 # Re-broadcast our input for the benefit of listening clients, and
264 # start computing output
267 # start computing output
265 if not silent:
268 if not silent:
266 self._publish_pyin(code, parent)
269 self._publish_pyin(code, parent)
267
270
268 reply_content = {}
271 reply_content = {}
269 try:
272 try:
270 if silent:
273 if silent:
271 # run_code uses 'exec' mode, so no displayhook will fire, and it
274 # run_code uses 'exec' mode, so no displayhook will fire, and it
272 # doesn't call logging or history manipulations. Print
275 # doesn't call logging or history manipulations. Print
273 # statements in that code will obviously still execute.
276 # statements in that code will obviously still execute.
274 shell.run_code(code)
277 shell.run_code(code)
275 else:
278 else:
276 # FIXME: the shell calls the exception handler itself.
279 # FIXME: the shell calls the exception handler itself.
277 shell.run_cell(code, store_history=True)
280 shell.run_cell(code, store_history=True)
278 except:
281 except:
279 status = u'error'
282 status = u'error'
280 # FIXME: this code right now isn't being used yet by default,
283 # FIXME: this code right now isn't being used yet by default,
281 # because the run_cell() call above directly fires off exception
284 # because the run_cell() call above directly fires off exception
282 # reporting. This code, therefore, is only active in the scenario
285 # reporting. This code, therefore, is only active in the scenario
283 # where runlines itself has an unhandled exception. We need to
286 # where runlines itself has an unhandled exception. We need to
284 # uniformize this, for all exception construction to come from a
287 # uniformize this, for all exception construction to come from a
285 # single location in the codbase.
288 # single location in the codbase.
286 etype, evalue, tb = sys.exc_info()
289 etype, evalue, tb = sys.exc_info()
287 tb_list = traceback.format_exception(etype, evalue, tb)
290 tb_list = traceback.format_exception(etype, evalue, tb)
288 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
291 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
289 else:
292 else:
290 status = u'ok'
293 status = u'ok'
291
294
292 reply_content[u'status'] = status
295 reply_content[u'status'] = status
293
296
294 # Return the execution counter so clients can display prompts
297 # Return the execution counter so clients can display prompts
295 reply_content['execution_count'] = shell.execution_count -1
298 reply_content['execution_count'] = shell.execution_count -1
296
299
297 # FIXME - fish exception info out of shell, possibly left there by
300 # FIXME - fish exception info out of shell, possibly left there by
298 # runlines. We'll need to clean up this logic later.
301 # runlines. We'll need to clean up this logic later.
299 if shell._reply_content is not None:
302 if shell._reply_content is not None:
300 reply_content.update(shell._reply_content)
303 reply_content.update(shell._reply_content)
301 # reset after use
304 # reset after use
302 shell._reply_content = None
305 shell._reply_content = None
303
306
304 # At this point, we can tell whether the main code execution succeeded
307 # At this point, we can tell whether the main code execution succeeded
305 # or not. If it did, we proceed to evaluate user_variables/expressions
308 # or not. If it did, we proceed to evaluate user_variables/expressions
306 if reply_content['status'] == 'ok':
309 if reply_content['status'] == 'ok':
307 reply_content[u'user_variables'] = \
310 reply_content[u'user_variables'] = \
308 shell.user_variables(content[u'user_variables'])
311 shell.user_variables(content[u'user_variables'])
309 reply_content[u'user_expressions'] = \
312 reply_content[u'user_expressions'] = \
310 shell.user_expressions(content[u'user_expressions'])
313 shell.user_expressions(content[u'user_expressions'])
311 else:
314 else:
312 # If there was an error, don't even try to compute variables or
315 # If there was an error, don't even try to compute variables or
313 # expressions
316 # expressions
314 reply_content[u'user_variables'] = {}
317 reply_content[u'user_variables'] = {}
315 reply_content[u'user_expressions'] = {}
318 reply_content[u'user_expressions'] = {}
316
319
317 # Payloads should be retrieved regardless of outcome, so we can both
320 # Payloads should be retrieved regardless of outcome, so we can both
318 # recover partial output (that could have been generated early in a
321 # recover partial output (that could have been generated early in a
319 # block, before an error) and clear the payload system always.
322 # block, before an error) and clear the payload system always.
320 reply_content[u'payload'] = shell.payload_manager.read_payload()
323 reply_content[u'payload'] = shell.payload_manager.read_payload()
321 # Be agressive about clearing the payload because we don't want
324 # Be agressive about clearing the payload because we don't want
322 # it to sit in memory until the next execute_request comes in.
325 # it to sit in memory until the next execute_request comes in.
323 shell.payload_manager.clear_payload()
326 shell.payload_manager.clear_payload()
324
327
325 # Flush output before sending the reply.
328 # Flush output before sending the reply.
326 sys.stdout.flush()
329 sys.stdout.flush()
327 sys.stderr.flush()
330 sys.stderr.flush()
328 # FIXME: on rare occasions, the flush doesn't seem to make it to the
331 # FIXME: on rare occasions, the flush doesn't seem to make it to the
329 # clients... This seems to mitigate the problem, but we definitely need
332 # clients... This seems to mitigate the problem, but we definitely need
330 # to better understand what's going on.
333 # to better understand what's going on.
331 if self._execute_sleep:
334 if self._execute_sleep:
332 time.sleep(self._execute_sleep)
335 time.sleep(self._execute_sleep)
333
336
334 # Send the reply.
337 # Send the reply.
335 reply_content = json_clean(reply_content)
338 reply_content = json_clean(reply_content)
336 reply_msg = self.session.send(self.shell_socket, u'execute_reply',
339 reply_msg = self.session.send(self.shell_socket, u'execute_reply',
337 reply_content, parent, ident=ident)
340 reply_content, parent, ident=ident)
338 self.log.debug(str(reply_msg))
341 self.log.debug(str(reply_msg))
339
342
340 if reply_msg['content']['status'] == u'error':
343 if reply_msg['content']['status'] == u'error':
341 self._abort_queue()
344 self._abort_queue()
342
345
343 self.session.send(self.iopub_socket,
346 self.session.send(self.iopub_socket,
344 u'status',
347 u'status',
345 {u'execution_state':u'idle'},
348 {u'execution_state':u'idle'},
346 parent=parent )
349 parent=parent )
347
350
348 def complete_request(self, ident, parent):
351 def complete_request(self, ident, parent):
349 txt, matches = self._complete(parent)
352 txt, matches = self._complete(parent)
350 matches = {'matches' : matches,
353 matches = {'matches' : matches,
351 'matched_text' : txt,
354 'matched_text' : txt,
352 'status' : 'ok'}
355 'status' : 'ok'}
353 matches = json_clean(matches)
356 matches = json_clean(matches)
354 completion_msg = self.session.send(self.shell_socket, 'complete_reply',
357 completion_msg = self.session.send(self.shell_socket, 'complete_reply',
355 matches, parent, ident)
358 matches, parent, ident)
356 self.log.debug(str(completion_msg))
359 self.log.debug(str(completion_msg))
357
360
358 def object_info_request(self, ident, parent):
361 def object_info_request(self, ident, parent):
359 object_info = self.shell.object_inspect(parent['content']['oname'])
362 object_info = self.shell.object_inspect(parent['content']['oname'])
360 # Before we send this object over, we scrub it for JSON usage
363 # Before we send this object over, we scrub it for JSON usage
361 oinfo = json_clean(object_info)
364 oinfo = json_clean(object_info)
362 msg = self.session.send(self.shell_socket, 'object_info_reply',
365 msg = self.session.send(self.shell_socket, 'object_info_reply',
363 oinfo, parent, ident)
366 oinfo, parent, ident)
364 self.log.debug(msg)
367 self.log.debug(msg)
365
368
366 def history_request(self, ident, parent):
369 def history_request(self, ident, parent):
367 # We need to pull these out, as passing **kwargs doesn't work with
370 # We need to pull these out, as passing **kwargs doesn't work with
368 # unicode keys before Python 2.6.5.
371 # unicode keys before Python 2.6.5.
369 hist_access_type = parent['content']['hist_access_type']
372 hist_access_type = parent['content']['hist_access_type']
370 raw = parent['content']['raw']
373 raw = parent['content']['raw']
371 output = parent['content']['output']
374 output = parent['content']['output']
372 if hist_access_type == 'tail':
375 if hist_access_type == 'tail':
373 n = parent['content']['n']
376 n = parent['content']['n']
374 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
377 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
375 include_latest=True)
378 include_latest=True)
376
379
377 elif hist_access_type == 'range':
380 elif hist_access_type == 'range':
378 session = parent['content']['session']
381 session = parent['content']['session']
379 start = parent['content']['start']
382 start = parent['content']['start']
380 stop = parent['content']['stop']
383 stop = parent['content']['stop']
381 hist = self.shell.history_manager.get_range(session, start, stop,
384 hist = self.shell.history_manager.get_range(session, start, stop,
382 raw=raw, output=output)
385 raw=raw, output=output)
383
386
384 elif hist_access_type == 'search':
387 elif hist_access_type == 'search':
385 pattern = parent['content']['pattern']
388 pattern = parent['content']['pattern']
386 hist = self.shell.history_manager.search(pattern, raw=raw,
389 hist = self.shell.history_manager.search(pattern, raw=raw,
387 output=output)
390 output=output)
388
391
389 else:
392 else:
390 hist = []
393 hist = []
391 content = {'history' : list(hist)}
394 content = {'history' : list(hist)}
392 content = json_clean(content)
395 content = json_clean(content)
393 msg = self.session.send(self.shell_socket, 'history_reply',
396 msg = self.session.send(self.shell_socket, 'history_reply',
394 content, parent, ident)
397 content, parent, ident)
395 self.log.debug(str(msg))
398 self.log.debug(str(msg))
396
399
397 def connect_request(self, ident, parent):
400 def connect_request(self, ident, parent):
398 if self._recorded_ports is not None:
401 if self._recorded_ports is not None:
399 content = self._recorded_ports.copy()
402 content = self._recorded_ports.copy()
400 else:
403 else:
401 content = {}
404 content = {}
402 msg = self.session.send(self.shell_socket, 'connect_reply',
405 msg = self.session.send(self.shell_socket, 'connect_reply',
403 content, parent, ident)
406 content, parent, ident)
404 self.log.debug(msg)
407 self.log.debug(msg)
405
408
406 def shutdown_request(self, ident, parent):
409 def shutdown_request(self, ident, parent):
407 self.shell.exit_now = True
410 self.shell.exit_now = True
408 self._shutdown_message = self.session.msg(u'shutdown_reply',
411 self._shutdown_message = self.session.msg(u'shutdown_reply',
409 parent['content'], parent)
412 parent['content'], parent)
410 sys.exit(0)
413 sys.exit(0)
411
414
412 #---------------------------------------------------------------------------
415 #---------------------------------------------------------------------------
413 # Protected interface
416 # Protected interface
414 #---------------------------------------------------------------------------
417 #---------------------------------------------------------------------------
415
418
416 def _abort_queue(self):
419 def _abort_queue(self):
417 while True:
420 while True:
418 try:
421 try:
419 ident,msg = self.session.recv(self.shell_socket, zmq.NOBLOCK)
422 ident,msg = self.session.recv(self.shell_socket, zmq.NOBLOCK)
420 except Exception:
423 except Exception:
421 self.log.warn("Invalid Message:", exc_info=True)
424 self.log.warn("Invalid Message:", exc_info=True)
422 continue
425 continue
423 if msg is None:
426 if msg is None:
424 break
427 break
425 else:
428 else:
426 assert ident is not None, \
429 assert ident is not None, \
427 "Unexpected missing message part."
430 "Unexpected missing message part."
428
431
429 self.log.debug("Aborting:\n"+str(Message(msg)))
432 self.log.debug("Aborting:\n"+str(Message(msg)))
430 msg_type = msg['header']['msg_type']
433 msg_type = msg['header']['msg_type']
431 reply_type = msg_type.split('_')[0] + '_reply'
434 reply_type = msg_type.split('_')[0] + '_reply'
432 reply_msg = self.session.send(self.shell_socket, reply_type,
435 reply_msg = self.session.send(self.shell_socket, reply_type,
433 {'status' : 'aborted'}, msg, ident=ident)
436 {'status' : 'aborted'}, msg, ident=ident)
434 self.log.debug(reply_msg)
437 self.log.debug(reply_msg)
435 # We need to wait a bit for requests to come in. This can probably
438 # We need to wait a bit for requests to come in. This can probably
436 # be set shorter for true asynchronous clients.
439 # be set shorter for true asynchronous clients.
437 time.sleep(0.1)
440 time.sleep(0.1)
438
441
439 def _no_raw_input(self):
442 def _no_raw_input(self):
440 """Raise StdinNotImplentedError if active frontend doesn't support
443 """Raise StdinNotImplentedError if active frontend doesn't support
441 stdin."""
444 stdin."""
442 raise StdinNotImplementedError("raw_input was called, but this "
445 raise StdinNotImplementedError("raw_input was called, but this "
443 "frontend does not support stdin.")
446 "frontend does not support stdin.")
444
447
445 def _raw_input(self, prompt, ident, parent):
448 def _raw_input(self, prompt, ident, parent):
446 # Flush output before making the request.
449 # Flush output before making the request.
447 sys.stderr.flush()
450 sys.stderr.flush()
448 sys.stdout.flush()
451 sys.stdout.flush()
449
452
450 # Send the input request.
453 # Send the input request.
451 content = json_clean(dict(prompt=prompt))
454 content = json_clean(dict(prompt=prompt))
452 self.session.send(self.stdin_socket, u'input_request', content, parent,
455 self.session.send(self.stdin_socket, u'input_request', content, parent,
453 ident=ident)
456 ident=ident)
454
457
455 # Await a response.
458 # Await a response.
456 while True:
459 while True:
457 try:
460 try:
458 ident, reply = self.session.recv(self.stdin_socket, 0)
461 ident, reply = self.session.recv(self.stdin_socket, 0)
459 except Exception:
462 except Exception:
460 self.log.warn("Invalid Message:", exc_info=True)
463 self.log.warn("Invalid Message:", exc_info=True)
461 else:
464 else:
462 break
465 break
463 try:
466 try:
464 value = reply['content']['value']
467 value = reply['content']['value']
465 except:
468 except:
466 self.log.error("Got bad raw_input reply: ")
469 self.log.error("Got bad raw_input reply: ")
467 self.log.error(str(Message(parent)))
470 self.log.error(str(Message(parent)))
468 value = ''
471 value = ''
469 return value
472 return value
470
473
471 def _complete(self, msg):
474 def _complete(self, msg):
472 c = msg['content']
475 c = msg['content']
473 try:
476 try:
474 cpos = int(c['cursor_pos'])
477 cpos = int(c['cursor_pos'])
475 except:
478 except:
476 # If we don't get something that we can convert to an integer, at
479 # If we don't get something that we can convert to an integer, at
477 # least attempt the completion guessing the cursor is at the end of
480 # least attempt the completion guessing the cursor is at the end of
478 # the text, if there's any, and otherwise of the line
481 # the text, if there's any, and otherwise of the line
479 cpos = len(c['text'])
482 cpos = len(c['text'])
480 if cpos==0:
483 if cpos==0:
481 cpos = len(c['line'])
484 cpos = len(c['line'])
482 return self.shell.complete(c['text'], c['line'], cpos)
485 return self.shell.complete(c['text'], c['line'], cpos)
483
486
484 def _object_info(self, context):
487 def _object_info(self, context):
485 symbol, leftover = self._symbol_from_context(context)
488 symbol, leftover = self._symbol_from_context(context)
486 if symbol is not None and not leftover:
489 if symbol is not None and not leftover:
487 doc = getattr(symbol, '__doc__', '')
490 doc = getattr(symbol, '__doc__', '')
488 else:
491 else:
489 doc = ''
492 doc = ''
490 object_info = dict(docstring = doc)
493 object_info = dict(docstring = doc)
491 return object_info
494 return object_info
492
495
493 def _symbol_from_context(self, context):
496 def _symbol_from_context(self, context):
494 if not context:
497 if not context:
495 return None, context
498 return None, context
496
499
497 base_symbol_string = context[0]
500 base_symbol_string = context[0]
498 symbol = self.shell.user_ns.get(base_symbol_string, None)
501 symbol = self.shell.user_ns.get(base_symbol_string, None)
499 if symbol is None:
502 if symbol is None:
500 symbol = __builtin__.__dict__.get(base_symbol_string, None)
503 symbol = __builtin__.__dict__.get(base_symbol_string, None)
501 if symbol is None:
504 if symbol is None:
502 return None, context
505 return None, context
503
506
504 context = context[1:]
507 context = context[1:]
505 for i, name in enumerate(context):
508 for i, name in enumerate(context):
506 new_symbol = getattr(symbol, name, None)
509 new_symbol = getattr(symbol, name, None)
507 if new_symbol is None:
510 if new_symbol is None:
508 return symbol, context[i:]
511 return symbol, context[i:]
509 else:
512 else:
510 symbol = new_symbol
513 symbol = new_symbol
511
514
512 return symbol, []
515 return symbol, []
513
516
514 def _at_shutdown(self):
517 def _at_shutdown(self):
515 """Actions taken at shutdown by the kernel, called by python's atexit.
518 """Actions taken at shutdown by the kernel, called by python's atexit.
516 """
519 """
517 # io.rprint("Kernel at_shutdown") # dbg
520 # io.rprint("Kernel at_shutdown") # dbg
518 if self._shutdown_message is not None:
521 if self._shutdown_message is not None:
519 self.session.send(self.shell_socket, self._shutdown_message)
522 self.session.send(self.shell_socket, self._shutdown_message)
520 self.session.send(self.iopub_socket, self._shutdown_message)
523 self.session.send(self.iopub_socket, self._shutdown_message)
521 self.log.debug(str(self._shutdown_message))
524 self.log.debug(str(self._shutdown_message))
522 # A very short sleep to give zmq time to flush its message buffers
525 # A very short sleep to give zmq time to flush its message buffers
523 # before Python truly shuts down.
526 # before Python truly shuts down.
524 time.sleep(0.01)
527 time.sleep(0.01)
525
528
526 #-----------------------------------------------------------------------------
529 #-----------------------------------------------------------------------------
527 # Aliases and Flags for the IPKernelApp
530 # Aliases and Flags for the IPKernelApp
528 #-----------------------------------------------------------------------------
531 #-----------------------------------------------------------------------------
529
532
530 flags = dict(kernel_flags)
533 flags = dict(kernel_flags)
531 flags.update(shell_flags)
534 flags.update(shell_flags)
532
535
533 addflag = lambda *args: flags.update(boolean_flag(*args))
536 addflag = lambda *args: flags.update(boolean_flag(*args))
534
537
535 flags['pylab'] = (
538 flags['pylab'] = (
536 {'IPKernelApp' : {'pylab' : 'auto'}},
539 {'IPKernelApp' : {'pylab' : 'auto'}},
537 """Pre-load matplotlib and numpy for interactive use with
540 """Pre-load matplotlib and numpy for interactive use with
538 the default matplotlib backend."""
541 the default matplotlib backend."""
539 )
542 )
540
543
541 aliases = dict(kernel_aliases)
544 aliases = dict(kernel_aliases)
542 aliases.update(shell_aliases)
545 aliases.update(shell_aliases)
543
546
544 # it's possible we don't want short aliases for *all* of these:
547 # it's possible we don't want short aliases for *all* of these:
545 aliases.update(dict(
548 aliases.update(dict(
546 pylab='IPKernelApp.pylab',
549 pylab='IPKernelApp.pylab',
547 ))
550 ))
548
551
549 #-----------------------------------------------------------------------------
552 #-----------------------------------------------------------------------------
550 # The IPKernelApp class
553 # The IPKernelApp class
551 #-----------------------------------------------------------------------------
554 #-----------------------------------------------------------------------------
552
555
553 class IPKernelApp(KernelApp, InteractiveShellApp):
556 class IPKernelApp(KernelApp, InteractiveShellApp):
554 name = 'ipkernel'
557 name = 'ipkernel'
555
558
556 aliases = Dict(aliases)
559 aliases = Dict(aliases)
557 flags = Dict(flags)
560 flags = Dict(flags)
558 classes = [Kernel, ZMQInteractiveShell, ProfileDir, Session]
561 classes = [Kernel, ZMQInteractiveShell, ProfileDir, Session]
559 # configurables
562 # configurables
560 pylab = CaselessStrEnum(['tk', 'qt', 'wx', 'gtk', 'osx', 'inline', 'auto'],
563 pylab = CaselessStrEnum(['tk', 'qt', 'wx', 'gtk', 'osx', 'inline', 'auto'],
561 config=True,
564 config=True,
562 help="""Pre-load matplotlib and numpy for interactive use,
565 help="""Pre-load matplotlib and numpy for interactive use,
563 selecting a particular matplotlib backend and loop integration.
566 selecting a particular matplotlib backend and loop integration.
564 """
567 """
565 )
568 )
566
569
567 @catch_config_error
570 @catch_config_error
568 def initialize(self, argv=None):
571 def initialize(self, argv=None):
569 super(IPKernelApp, self).initialize(argv)
572 super(IPKernelApp, self).initialize(argv)
570 self.init_shell()
573 self.init_shell()
571 self.init_extensions()
574 self.init_extensions()
572 self.init_code()
575 self.init_code()
573
576
574 def init_kernel(self):
577 def init_kernel(self):
575
578
576 kernel = Kernel(config=self.config, session=self.session,
579 kernel = Kernel(config=self.config, session=self.session,
577 shell_socket=self.shell_socket,
580 shell_socket=self.shell_socket,
578 iopub_socket=self.iopub_socket,
581 iopub_socket=self.iopub_socket,
579 stdin_socket=self.stdin_socket,
582 stdin_socket=self.stdin_socket,
580 log=self.log,
583 log=self.log,
581 profile_dir=self.profile_dir,
584 profile_dir=self.profile_dir,
582 )
585 )
583 self.kernel = kernel
586 self.kernel = kernel
584 kernel.record_ports(self.ports)
587 kernel.record_ports(self.ports)
585 shell = kernel.shell
588 shell = kernel.shell
586 if self.pylab:
589 if self.pylab:
587 try:
590 try:
588 gui, backend = pylabtools.find_gui_and_backend(self.pylab)
591 gui, backend = pylabtools.find_gui_and_backend(self.pylab)
589 shell.enable_pylab(gui, import_all=self.pylab_import_all)
592 shell.enable_pylab(gui, import_all=self.pylab_import_all)
590 except Exception:
593 except Exception:
591 self.log.error("Pylab initialization failed", exc_info=True)
594 self.log.error("Pylab initialization failed", exc_info=True)
592 # print exception straight to stdout, because normally
595 # print exception straight to stdout, because normally
593 # _showtraceback associates the reply with an execution,
596 # _showtraceback associates the reply with an execution,
594 # which means frontends will never draw it, as this exception
597 # which means frontends will never draw it, as this exception
595 # is not associated with any execute request.
598 # is not associated with any execute request.
596
599
597 # replace pyerr-sending traceback with stdout
600 # replace pyerr-sending traceback with stdout
598 _showtraceback = shell._showtraceback
601 _showtraceback = shell._showtraceback
599 def print_tb(etype, evalue, stb):
602 def print_tb(etype, evalue, stb):
600 print ("Error initializing pylab, pylab mode will not "
603 print ("Error initializing pylab, pylab mode will not "
601 "be active", file=io.stderr)
604 "be active", file=io.stderr)
602 print (shell.InteractiveTB.stb2text(stb), file=io.stdout)
605 print (shell.InteractiveTB.stb2text(stb), file=io.stdout)
603 shell._showtraceback = print_tb
606 shell._showtraceback = print_tb
604
607
605 # send the traceback over stdout
608 # send the traceback over stdout
606 shell.showtraceback(tb_offset=0)
609 shell.showtraceback(tb_offset=0)
607
610
608 # restore proper _showtraceback method
611 # restore proper _showtraceback method
609 shell._showtraceback = _showtraceback
612 shell._showtraceback = _showtraceback
610
613
611
614
612 def init_shell(self):
615 def init_shell(self):
613 self.shell = self.kernel.shell
616 self.shell = self.kernel.shell
614 self.shell.configurables.append(self)
617 self.shell.configurables.append(self)
615
618
616
619
617 #-----------------------------------------------------------------------------
620 #-----------------------------------------------------------------------------
618 # Kernel main and launch functions
621 # Kernel main and launch functions
619 #-----------------------------------------------------------------------------
622 #-----------------------------------------------------------------------------
620
623
621 def launch_kernel(*args, **kwargs):
624 def launch_kernel(*args, **kwargs):
622 """Launches a localhost IPython kernel, binding to the specified ports.
625 """Launches a localhost IPython kernel, binding to the specified ports.
623
626
624 This function simply calls entry_point.base_launch_kernel with the right
627 This function simply calls entry_point.base_launch_kernel with the right
625 first command to start an ipkernel. See base_launch_kernel for arguments.
628 first command to start an ipkernel. See base_launch_kernel for arguments.
626
629
627 Returns
630 Returns
628 -------
631 -------
629 A tuple of form:
632 A tuple of form:
630 (kernel_process, shell_port, iopub_port, stdin_port, hb_port)
633 (kernel_process, shell_port, iopub_port, stdin_port, hb_port)
631 where kernel_process is a Popen object and the ports are integers.
634 where kernel_process is a Popen object and the ports are integers.
632 """
635 """
633 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
636 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
634 *args, **kwargs)
637 *args, **kwargs)
635
638
636
639
637 def main():
640 def main():
638 """Run an IPKernel as an application"""
641 """Run an IPKernel as an application"""
639 app = IPKernelApp.instance()
642 app = IPKernelApp.instance()
640 app.initialize()
643 app.initialize()
641 app.start()
644 app.start()
642
645
643
646
644 if __name__ == '__main__':
647 if __name__ == '__main__':
645 main()
648 main()
General Comments 0
You need to be logged in to leave comments. Login now