##// END OF EJS Templates
py3compat pass on Session.key...
MinRK -
Show More
@@ -1,592 +1,593 b''
1 """ A minimal application using the Qt console-style IPython frontend.
1 """ A minimal application using the Qt console-style 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 * Evan Patterson
8 * Evan Patterson
9 * Min RK
9 * Min RK
10 * Erik Tollerud
10 * Erik Tollerud
11 * Fernando Perez
11 * Fernando Perez
12
12
13 """
13 """
14
14
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 # Imports
16 # Imports
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 # stdlib imports
19 # stdlib imports
20 import glob
20 import glob
21 import os
21 import os
22 import signal
22 import signal
23 import sys
23 import sys
24 from getpass import getpass
24 from getpass import getpass
25
25
26 # System library imports
26 # System library imports
27 from IPython.external.qt import QtGui
27 from IPython.external.qt import QtGui
28 from pygments.styles import get_all_styles
28 from pygments.styles import get_all_styles
29 from zmq.utils import jsonapi as json
29 from zmq.utils import jsonapi as json
30
30
31 # external imports
31 # external imports
32 from IPython.external.ssh import tunnel
32 from IPython.external.ssh import tunnel
33
33
34 # Local imports
34 # Local imports
35 from IPython.config.application import boolean_flag
35 from IPython.config.application import boolean_flag
36 from IPython.core.application import BaseIPythonApplication
36 from IPython.core.application import BaseIPythonApplication
37 from IPython.core.profiledir import ProfileDir
37 from IPython.core.profiledir import ProfileDir
38 from IPython.frontend.qt.console.frontend_widget import FrontendWidget
38 from IPython.frontend.qt.console.frontend_widget import FrontendWidget
39 from IPython.frontend.qt.console.ipython_widget import IPythonWidget
39 from IPython.frontend.qt.console.ipython_widget import IPythonWidget
40 from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
40 from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
41 from IPython.frontend.qt.console import styles
41 from IPython.frontend.qt.console import styles
42 from IPython.frontend.qt.kernelmanager import QtKernelManager
42 from IPython.frontend.qt.kernelmanager import QtKernelManager
43 from IPython.parallel.util import select_random_ports
43 from IPython.parallel.util import select_random_ports
44 from IPython.utils.path import filefind
44 from IPython.utils.path import filefind
45 from IPython.utils.py3compat import str_to_bytes
45 from IPython.utils.traitlets import (
46 from IPython.utils.traitlets import (
46 Dict, List, Unicode, Int, CaselessStrEnum, CBool, Any
47 Dict, List, Unicode, Int, CaselessStrEnum, CBool, Any
47 )
48 )
48 from IPython.zmq.ipkernel import (
49 from IPython.zmq.ipkernel import (
49 flags as ipkernel_flags,
50 flags as ipkernel_flags,
50 aliases as ipkernel_aliases,
51 aliases as ipkernel_aliases,
51 IPKernelApp
52 IPKernelApp
52 )
53 )
53 from IPython.zmq.session import Session, default_secure
54 from IPython.zmq.session import Session, default_secure
54 from IPython.zmq.zmqshell import ZMQInteractiveShell
55 from IPython.zmq.zmqshell import ZMQInteractiveShell
55
56
56
57
57 #-----------------------------------------------------------------------------
58 #-----------------------------------------------------------------------------
58 # Network Constants
59 # Network Constants
59 #-----------------------------------------------------------------------------
60 #-----------------------------------------------------------------------------
60
61
61 from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS
62 from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS
62
63
63 #-----------------------------------------------------------------------------
64 #-----------------------------------------------------------------------------
64 # Globals
65 # Globals
65 #-----------------------------------------------------------------------------
66 #-----------------------------------------------------------------------------
66
67
67 _examples = """
68 _examples = """
68 ipython qtconsole # start the qtconsole
69 ipython qtconsole # start the qtconsole
69 ipython qtconsole --pylab=inline # start with pylab in inline plotting mode
70 ipython qtconsole --pylab=inline # start with pylab in inline plotting mode
70 """
71 """
71
72
72 #-----------------------------------------------------------------------------
73 #-----------------------------------------------------------------------------
73 # Classes
74 # Classes
74 #-----------------------------------------------------------------------------
75 #-----------------------------------------------------------------------------
75
76
76 class MainWindow(QtGui.QMainWindow):
77 class MainWindow(QtGui.QMainWindow):
77
78
78 #---------------------------------------------------------------------------
79 #---------------------------------------------------------------------------
79 # 'object' interface
80 # 'object' interface
80 #---------------------------------------------------------------------------
81 #---------------------------------------------------------------------------
81
82
82 def __init__(self, app, frontend, existing=False, may_close=True,
83 def __init__(self, app, frontend, existing=False, may_close=True,
83 confirm_exit=True):
84 confirm_exit=True):
84 """ Create a MainWindow for the specified FrontendWidget.
85 """ Create a MainWindow for the specified FrontendWidget.
85
86
86 The app is passed as an argument to allow for different
87 The app is passed as an argument to allow for different
87 closing behavior depending on whether we are the Kernel's parent.
88 closing behavior depending on whether we are the Kernel's parent.
88
89
89 If existing is True, then this Console does not own the Kernel.
90 If existing is True, then this Console does not own the Kernel.
90
91
91 If may_close is True, then this Console is permitted to close the kernel
92 If may_close is True, then this Console is permitted to close the kernel
92 """
93 """
93 super(MainWindow, self).__init__()
94 super(MainWindow, self).__init__()
94 self._app = app
95 self._app = app
95 self._frontend = frontend
96 self._frontend = frontend
96 self._existing = existing
97 self._existing = existing
97 if existing:
98 if existing:
98 self._may_close = may_close
99 self._may_close = may_close
99 else:
100 else:
100 self._may_close = True
101 self._may_close = True
101 self._frontend.exit_requested.connect(self.close)
102 self._frontend.exit_requested.connect(self.close)
102 self._confirm_exit = confirm_exit
103 self._confirm_exit = confirm_exit
103 self.setCentralWidget(frontend)
104 self.setCentralWidget(frontend)
104
105
105 #---------------------------------------------------------------------------
106 #---------------------------------------------------------------------------
106 # QWidget interface
107 # QWidget interface
107 #---------------------------------------------------------------------------
108 #---------------------------------------------------------------------------
108
109
109 def closeEvent(self, event):
110 def closeEvent(self, event):
110 """ Close the window and the kernel (if necessary).
111 """ Close the window and the kernel (if necessary).
111
112
112 This will prompt the user if they are finished with the kernel, and if
113 This will prompt the user if they are finished with the kernel, and if
113 so, closes the kernel cleanly. Alternatively, if the exit magic is used,
114 so, closes the kernel cleanly. Alternatively, if the exit magic is used,
114 it closes without prompt.
115 it closes without prompt.
115 """
116 """
116 keepkernel = None #Use the prompt by default
117 keepkernel = None #Use the prompt by default
117 if hasattr(self._frontend,'_keep_kernel_on_exit'): #set by exit magic
118 if hasattr(self._frontend,'_keep_kernel_on_exit'): #set by exit magic
118 keepkernel = self._frontend._keep_kernel_on_exit
119 keepkernel = self._frontend._keep_kernel_on_exit
119
120
120 kernel_manager = self._frontend.kernel_manager
121 kernel_manager = self._frontend.kernel_manager
121
122
122 if keepkernel is None and not self._confirm_exit:
123 if keepkernel is None and not self._confirm_exit:
123 # don't prompt, just terminate the kernel if we own it
124 # don't prompt, just terminate the kernel if we own it
124 # or leave it alone if we don't
125 # or leave it alone if we don't
125 keepkernel = not self._existing
126 keepkernel = not self._existing
126
127
127 if keepkernel is None: #show prompt
128 if keepkernel is None: #show prompt
128 if kernel_manager and kernel_manager.channels_running:
129 if kernel_manager and kernel_manager.channels_running:
129 title = self.window().windowTitle()
130 title = self.window().windowTitle()
130 cancel = QtGui.QMessageBox.Cancel
131 cancel = QtGui.QMessageBox.Cancel
131 okay = QtGui.QMessageBox.Ok
132 okay = QtGui.QMessageBox.Ok
132 if self._may_close:
133 if self._may_close:
133 msg = "You are closing this Console window."
134 msg = "You are closing this Console window."
134 info = "Would you like to quit the Kernel and all attached Consoles as well?"
135 info = "Would you like to quit the Kernel and all attached Consoles as well?"
135 justthis = QtGui.QPushButton("&No, just this Console", self)
136 justthis = QtGui.QPushButton("&No, just this Console", self)
136 justthis.setShortcut('N')
137 justthis.setShortcut('N')
137 closeall = QtGui.QPushButton("&Yes, quit everything", self)
138 closeall = QtGui.QPushButton("&Yes, quit everything", self)
138 closeall.setShortcut('Y')
139 closeall.setShortcut('Y')
139 box = QtGui.QMessageBox(QtGui.QMessageBox.Question,
140 box = QtGui.QMessageBox(QtGui.QMessageBox.Question,
140 title, msg)
141 title, msg)
141 box.setInformativeText(info)
142 box.setInformativeText(info)
142 box.addButton(cancel)
143 box.addButton(cancel)
143 box.addButton(justthis, QtGui.QMessageBox.NoRole)
144 box.addButton(justthis, QtGui.QMessageBox.NoRole)
144 box.addButton(closeall, QtGui.QMessageBox.YesRole)
145 box.addButton(closeall, QtGui.QMessageBox.YesRole)
145 box.setDefaultButton(closeall)
146 box.setDefaultButton(closeall)
146 box.setEscapeButton(cancel)
147 box.setEscapeButton(cancel)
147 reply = box.exec_()
148 reply = box.exec_()
148 if reply == 1: # close All
149 if reply == 1: # close All
149 kernel_manager.shutdown_kernel()
150 kernel_manager.shutdown_kernel()
150 #kernel_manager.stop_channels()
151 #kernel_manager.stop_channels()
151 event.accept()
152 event.accept()
152 elif reply == 0: # close Console
153 elif reply == 0: # close Console
153 if not self._existing:
154 if not self._existing:
154 # Have kernel: don't quit, just close the window
155 # Have kernel: don't quit, just close the window
155 self._app.setQuitOnLastWindowClosed(False)
156 self._app.setQuitOnLastWindowClosed(False)
156 self.deleteLater()
157 self.deleteLater()
157 event.accept()
158 event.accept()
158 else:
159 else:
159 event.ignore()
160 event.ignore()
160 else:
161 else:
161 reply = QtGui.QMessageBox.question(self, title,
162 reply = QtGui.QMessageBox.question(self, title,
162 "Are you sure you want to close this Console?"+
163 "Are you sure you want to close this Console?"+
163 "\nThe Kernel and other Consoles will remain active.",
164 "\nThe Kernel and other Consoles will remain active.",
164 okay|cancel,
165 okay|cancel,
165 defaultButton=okay
166 defaultButton=okay
166 )
167 )
167 if reply == okay:
168 if reply == okay:
168 event.accept()
169 event.accept()
169 else:
170 else:
170 event.ignore()
171 event.ignore()
171 elif keepkernel: #close console but leave kernel running (no prompt)
172 elif keepkernel: #close console but leave kernel running (no prompt)
172 if kernel_manager and kernel_manager.channels_running:
173 if kernel_manager and kernel_manager.channels_running:
173 if not self._existing:
174 if not self._existing:
174 # I have the kernel: don't quit, just close the window
175 # I have the kernel: don't quit, just close the window
175 self._app.setQuitOnLastWindowClosed(False)
176 self._app.setQuitOnLastWindowClosed(False)
176 event.accept()
177 event.accept()
177 else: #close console and kernel (no prompt)
178 else: #close console and kernel (no prompt)
178 if kernel_manager and kernel_manager.channels_running:
179 if kernel_manager and kernel_manager.channels_running:
179 kernel_manager.shutdown_kernel()
180 kernel_manager.shutdown_kernel()
180 event.accept()
181 event.accept()
181
182
182 #-----------------------------------------------------------------------------
183 #-----------------------------------------------------------------------------
183 # Aliases and Flags
184 # Aliases and Flags
184 #-----------------------------------------------------------------------------
185 #-----------------------------------------------------------------------------
185
186
186 flags = dict(ipkernel_flags)
187 flags = dict(ipkernel_flags)
187 qt_flags = {
188 qt_flags = {
188 'existing' : ({'IPythonQtConsoleApp' : {'existing' : 'kernel*.json'}},
189 'existing' : ({'IPythonQtConsoleApp' : {'existing' : 'kernel*.json'}},
189 "Connect to an existing kernel. If no argument specified, guess most recent"),
190 "Connect to an existing kernel. If no argument specified, guess most recent"),
190 'pure' : ({'IPythonQtConsoleApp' : {'pure' : True}},
191 'pure' : ({'IPythonQtConsoleApp' : {'pure' : True}},
191 "Use a pure Python kernel instead of an IPython kernel."),
192 "Use a pure Python kernel instead of an IPython kernel."),
192 'plain' : ({'ConsoleWidget' : {'kind' : 'plain'}},
193 'plain' : ({'ConsoleWidget' : {'kind' : 'plain'}},
193 "Disable rich text support."),
194 "Disable rich text support."),
194 }
195 }
195 qt_flags.update(boolean_flag(
196 qt_flags.update(boolean_flag(
196 'gui-completion', 'ConsoleWidget.gui_completion',
197 'gui-completion', 'ConsoleWidget.gui_completion',
197 "use a GUI widget for tab completion",
198 "use a GUI widget for tab completion",
198 "use plaintext output for completion"
199 "use plaintext output for completion"
199 ))
200 ))
200 qt_flags.update(boolean_flag(
201 qt_flags.update(boolean_flag(
201 'confirm-exit', 'IPythonQtConsoleApp.confirm_exit',
202 'confirm-exit', 'IPythonQtConsoleApp.confirm_exit',
202 """Set to display confirmation dialog on exit. You can always use 'exit' or 'quit',
203 """Set to display confirmation dialog on exit. You can always use 'exit' or 'quit',
203 to force a direct exit without any confirmation.
204 to force a direct exit without any confirmation.
204 """,
205 """,
205 """Don't prompt the user when exiting. This will terminate the kernel
206 """Don't prompt the user when exiting. This will terminate the kernel
206 if it is owned by the frontend, and leave it alive if it is external.
207 if it is owned by the frontend, and leave it alive if it is external.
207 """
208 """
208 ))
209 ))
209 flags.update(qt_flags)
210 flags.update(qt_flags)
210
211
211 aliases = dict(ipkernel_aliases)
212 aliases = dict(ipkernel_aliases)
212
213
213 qt_aliases = dict(
214 qt_aliases = dict(
214 hb = 'IPythonQtConsoleApp.hb_port',
215 hb = 'IPythonQtConsoleApp.hb_port',
215 shell = 'IPythonQtConsoleApp.shell_port',
216 shell = 'IPythonQtConsoleApp.shell_port',
216 iopub = 'IPythonQtConsoleApp.iopub_port',
217 iopub = 'IPythonQtConsoleApp.iopub_port',
217 stdin = 'IPythonQtConsoleApp.stdin_port',
218 stdin = 'IPythonQtConsoleApp.stdin_port',
218 ip = 'IPythonQtConsoleApp.ip',
219 ip = 'IPythonQtConsoleApp.ip',
219 existing = 'IPythonQtConsoleApp.existing',
220 existing = 'IPythonQtConsoleApp.existing',
220 f = 'IPythonQtConsoleApp.connection_file',
221 f = 'IPythonQtConsoleApp.connection_file',
221
222
222 style = 'IPythonWidget.syntax_style',
223 style = 'IPythonWidget.syntax_style',
223 stylesheet = 'IPythonQtConsoleApp.stylesheet',
224 stylesheet = 'IPythonQtConsoleApp.stylesheet',
224 colors = 'ZMQInteractiveShell.colors',
225 colors = 'ZMQInteractiveShell.colors',
225
226
226 editor = 'IPythonWidget.editor',
227 editor = 'IPythonWidget.editor',
227 paging = 'ConsoleWidget.paging',
228 paging = 'ConsoleWidget.paging',
228 ssh = 'IPythonQtConsoleApp.sshserver',
229 ssh = 'IPythonQtConsoleApp.sshserver',
229 )
230 )
230 aliases.update(qt_aliases)
231 aliases.update(qt_aliases)
231
232
232
233
233 #-----------------------------------------------------------------------------
234 #-----------------------------------------------------------------------------
234 # IPythonQtConsole
235 # IPythonQtConsole
235 #-----------------------------------------------------------------------------
236 #-----------------------------------------------------------------------------
236
237
237
238
238 class IPythonQtConsoleApp(BaseIPythonApplication):
239 class IPythonQtConsoleApp(BaseIPythonApplication):
239 name = 'ipython-qtconsole'
240 name = 'ipython-qtconsole'
240 default_config_file_name='ipython_config.py'
241 default_config_file_name='ipython_config.py'
241
242
242 description = """
243 description = """
243 The IPython QtConsole.
244 The IPython QtConsole.
244
245
245 This launches a Console-style application using Qt. It is not a full
246 This launches a Console-style application using Qt. It is not a full
246 console, in that launched terminal subprocesses will not be able to accept
247 console, in that launched terminal subprocesses will not be able to accept
247 input.
248 input.
248
249
249 The QtConsole supports various extra features beyond the Terminal IPython
250 The QtConsole supports various extra features beyond the Terminal IPython
250 shell, such as inline plotting with matplotlib, via:
251 shell, such as inline plotting with matplotlib, via:
251
252
252 ipython qtconsole --pylab=inline
253 ipython qtconsole --pylab=inline
253
254
254 as well as saving your session as HTML, and printing the output.
255 as well as saving your session as HTML, and printing the output.
255
256
256 """
257 """
257 examples = _examples
258 examples = _examples
258
259
259 classes = [IPKernelApp, IPythonWidget, ZMQInteractiveShell, ProfileDir, Session]
260 classes = [IPKernelApp, IPythonWidget, ZMQInteractiveShell, ProfileDir, Session]
260 flags = Dict(flags)
261 flags = Dict(flags)
261 aliases = Dict(aliases)
262 aliases = Dict(aliases)
262
263
263 kernel_argv = List(Unicode)
264 kernel_argv = List(Unicode)
264
265
265 # create requested profiles by default, if they don't exist:
266 # create requested profiles by default, if they don't exist:
266 auto_create = CBool(True)
267 auto_create = CBool(True)
267 # connection info:
268 # connection info:
268 ip = Unicode(LOCALHOST, config=True,
269 ip = Unicode(LOCALHOST, config=True,
269 help="""Set the kernel\'s IP address [default localhost].
270 help="""Set the kernel\'s IP address [default localhost].
270 If the IP address is something other than localhost, then
271 If the IP address is something other than localhost, then
271 Consoles on other machines will be able to connect
272 Consoles on other machines will be able to connect
272 to the Kernel, so be careful!"""
273 to the Kernel, so be careful!"""
273 )
274 )
274
275
275 sshserver = Unicode('', config=True,
276 sshserver = Unicode('', config=True,
276 help="""The SSH server to use to connect to the kernel.""")
277 help="""The SSH server to use to connect to the kernel.""")
277 sshkey = Unicode('', config=True,
278 sshkey = Unicode('', config=True,
278 help="""Path to the ssh key to use for logging in to the ssh server.""")
279 help="""Path to the ssh key to use for logging in to the ssh server.""")
279
280
280 hb_port = Int(0, config=True,
281 hb_port = Int(0, config=True,
281 help="set the heartbeat port [default: random]")
282 help="set the heartbeat port [default: random]")
282 shell_port = Int(0, config=True,
283 shell_port = Int(0, config=True,
283 help="set the shell (XREP) port [default: random]")
284 help="set the shell (XREP) port [default: random]")
284 iopub_port = Int(0, config=True,
285 iopub_port = Int(0, config=True,
285 help="set the iopub (PUB) port [default: random]")
286 help="set the iopub (PUB) port [default: random]")
286 stdin_port = Int(0, config=True,
287 stdin_port = Int(0, config=True,
287 help="set the stdin (XREQ) port [default: random]")
288 help="set the stdin (XREQ) port [default: random]")
288 connection_file = Unicode('', config=True,
289 connection_file = Unicode('', config=True,
289 help="""JSON file in which to store connection info [default: kernel-<pid>.json]
290 help="""JSON file in which to store connection info [default: kernel-<pid>.json]
290
291
291 This file will contain the IP, ports, and authentication key needed to connect
292 This file will contain the IP, ports, and authentication key needed to connect
292 clients to this kernel. By default, this file will be created in the security-dir
293 clients to this kernel. By default, this file will be created in the security-dir
293 of the current profile, but can be specified by absolute path.
294 of the current profile, but can be specified by absolute path.
294 """)
295 """)
295 def _connection_file_default(self):
296 def _connection_file_default(self):
296 return 'kernel-%i.json' % os.getpid()
297 return 'kernel-%i.json' % os.getpid()
297
298
298 existing = Unicode('', config=True,
299 existing = Unicode('', config=True,
299 help="""Connect to an already running kernel""")
300 help="""Connect to an already running kernel""")
300
301
301 stylesheet = Unicode('', config=True,
302 stylesheet = Unicode('', config=True,
302 help="path to a custom CSS stylesheet")
303 help="path to a custom CSS stylesheet")
303
304
304 pure = CBool(False, config=True,
305 pure = CBool(False, config=True,
305 help="Use a pure Python kernel instead of an IPython kernel.")
306 help="Use a pure Python kernel instead of an IPython kernel.")
306 plain = CBool(False, config=True,
307 plain = CBool(False, config=True,
307 help="Use a plaintext widget instead of rich text (plain can't print/save).")
308 help="Use a plaintext widget instead of rich text (plain can't print/save).")
308
309
309 def _pure_changed(self, name, old, new):
310 def _pure_changed(self, name, old, new):
310 kind = 'plain' if self.plain else 'rich'
311 kind = 'plain' if self.plain else 'rich'
311 self.config.ConsoleWidget.kind = kind
312 self.config.ConsoleWidget.kind = kind
312 if self.pure:
313 if self.pure:
313 self.widget_factory = FrontendWidget
314 self.widget_factory = FrontendWidget
314 elif self.plain:
315 elif self.plain:
315 self.widget_factory = IPythonWidget
316 self.widget_factory = IPythonWidget
316 else:
317 else:
317 self.widget_factory = RichIPythonWidget
318 self.widget_factory = RichIPythonWidget
318
319
319 _plain_changed = _pure_changed
320 _plain_changed = _pure_changed
320
321
321 confirm_exit = CBool(True, config=True,
322 confirm_exit = CBool(True, config=True,
322 help="""
323 help="""
323 Set to display confirmation dialog on exit. You can always use 'exit' or 'quit',
324 Set to display confirmation dialog on exit. You can always use 'exit' or 'quit',
324 to force a direct exit without any confirmation.""",
325 to force a direct exit without any confirmation.""",
325 )
326 )
326
327
327 # the factory for creating a widget
328 # the factory for creating a widget
328 widget_factory = Any(RichIPythonWidget)
329 widget_factory = Any(RichIPythonWidget)
329
330
330 def parse_command_line(self, argv=None):
331 def parse_command_line(self, argv=None):
331 super(IPythonQtConsoleApp, self).parse_command_line(argv)
332 super(IPythonQtConsoleApp, self).parse_command_line(argv)
332 if argv is None:
333 if argv is None:
333 argv = sys.argv[1:]
334 argv = sys.argv[1:]
334
335
335 self.kernel_argv = list(argv) # copy
336 self.kernel_argv = list(argv) # copy
336 # kernel should inherit default config file from frontend
337 # kernel should inherit default config file from frontend
337 self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name)
338 self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name)
338 # Scrub frontend-specific flags
339 # Scrub frontend-specific flags
339 for a in argv:
340 for a in argv:
340 if a.startswith('-') and a.lstrip('-') in qt_flags:
341 if a.startswith('-') and a.lstrip('-') in qt_flags:
341 self.kernel_argv.remove(a)
342 self.kernel_argv.remove(a)
342 swallow_next = False
343 swallow_next = False
343 for a in argv:
344 for a in argv:
344 if swallow_next:
345 if swallow_next:
345 self.kernel_argv.remove(a)
346 self.kernel_argv.remove(a)
346 swallow_next = False
347 swallow_next = False
347 continue
348 continue
348 if a.startswith('-'):
349 if a.startswith('-'):
349 split = a.lstrip('-').split('=')[0]
350 split = a.lstrip('-').split('=')[0]
350 alias = split[0]
351 alias = split[0]
351 if alias in qt_aliases:
352 if alias in qt_aliases:
352 self.kernel_argv.remove(a)
353 self.kernel_argv.remove(a)
353 if len(split) == 1:
354 if len(split) == 1:
354 # alias passed with arg via space
355 # alias passed with arg via space
355 swallow_next = True
356 swallow_next = True
356
357
357 def init_connection_file(self):
358 def init_connection_file(self):
358 sec = self.profile_dir.security_dir
359 sec = self.profile_dir.security_dir
359 if self.existing:
360 if self.existing:
360 try:
361 try:
361 # first, try explicit name
362 # first, try explicit name
362 cf = filefind(self.existing, ['.', sec])
363 cf = filefind(self.existing, ['.', sec])
363 except IOError:
364 except IOError:
364 # not found by full name
365 # not found by full name
365 if '*' in self.existing:
366 if '*' in self.existing:
366 # given as a glob already
367 # given as a glob already
367 pat = self.existing
368 pat = self.existing
368 else:
369 else:
369 # accept any substring match
370 # accept any substring match
370 pat = '*%s*'
371 pat = '*%s*'
371 matches = glob.glob( os.path.join(sec, pat) )
372 matches = glob.glob( os.path.join(sec, pat) )
372 if not matches:
373 if not matches:
373 self.log.critical("Could not find existing kernel connection file %s", self.existing)
374 self.log.critical("Could not find existing kernel connection file %s", self.existing)
374 self.exit(1)
375 self.exit(1)
375 else:
376 else:
376 # get most recent match:
377 # get most recent match:
377 cf = sorted(matches, key=lambda f: os.stat(f).st_atime)[-1]
378 cf = sorted(matches, key=lambda f: os.stat(f).st_atime)[-1]
378 self.log.info("Connecting to existing kernel: %s" % cf)
379 self.log.info("Connecting to existing kernel: %s" % cf)
379 self.connection_file = cf
380 self.connection_file = cf
380 # should load_connection_file only be used for existing?
381 # should load_connection_file only be used for existing?
381 # as it is now, this allows reusing ports if an existing
382 # as it is now, this allows reusing ports if an existing
382 # file is requested
383 # file is requested
383 self.load_connection_file()
384 self.load_connection_file()
384
385
385 def load_connection_file(self):
386 def load_connection_file(self):
386 """load ip/port/hmac config from JSON connection file"""
387 """load ip/port/hmac config from JSON connection file"""
387 # this is identical to KernelApp.load_connection_file
388 # this is identical to KernelApp.load_connection_file
388 # perhaps it can be centralized somewhere?
389 # perhaps it can be centralized somewhere?
389 try:
390 try:
390 fname = filefind(self.connection_file, ['.', self.profile_dir.security_dir])
391 fname = filefind(self.connection_file, ['.', self.profile_dir.security_dir])
391 except IOError:
392 except IOError:
392 self.log.debug("Connection File not found: %s", self.connection_file)
393 self.log.debug("Connection File not found: %s", self.connection_file)
393 return
394 return
394 self.log.debug(u"Loading connection file %s", fname)
395 self.log.debug(u"Loading connection file %s", fname)
395 with open(fname) as f:
396 with open(fname) as f:
396 s = f.read()
397 s = f.read()
397 cfg = json.loads(s)
398 cfg = json.loads(s)
398 if self.ip == LOCALHOST and 'ip' in cfg:
399 if self.ip == LOCALHOST and 'ip' in cfg:
399 # not overridden by config or cl_args
400 # not overridden by config or cl_args
400 self.ip = cfg['ip']
401 self.ip = cfg['ip']
401 for channel in ('hb', 'shell', 'iopub', 'stdin'):
402 for channel in ('hb', 'shell', 'iopub', 'stdin'):
402 name = channel + '_port'
403 name = channel + '_port'
403 if getattr(self, name) == 0 and name in cfg:
404 if getattr(self, name) == 0 and name in cfg:
404 # not overridden by config or cl_args
405 # not overridden by config or cl_args
405 setattr(self, name, cfg[name])
406 setattr(self, name, cfg[name])
406 if 'key' in cfg:
407 if 'key' in cfg:
407 self.config.Session.key = cfg['key']
408 self.config.Session.key = str_to_bytes(cfg['key'])
408
409
409 def init_ssh(self):
410 def init_ssh(self):
410 """set up ssh tunnels, if needed."""
411 """set up ssh tunnels, if needed."""
411 if not self.sshserver and not self.sshkey:
412 if not self.sshserver and not self.sshkey:
412 return
413 return
413
414
414 if self.sshkey and not self.sshserver:
415 if self.sshkey and not self.sshserver:
415 self.sshserver = self.ip
416 self.sshserver = self.ip
416 self.ip=LOCALHOST
417 self.ip=LOCALHOST
417
418
418 lports = select_random_ports(4)
419 lports = select_random_ports(4)
419 rports = self.shell_port, self.iopub_port, self.stdin_port, self.hb_port
420 rports = self.shell_port, self.iopub_port, self.stdin_port, self.hb_port
420 self.shell_port, self.iopub_port, self.stdin_port, self.hb_port = lports
421 self.shell_port, self.iopub_port, self.stdin_port, self.hb_port = lports
421
422
422 remote_ip = self.ip
423 remote_ip = self.ip
423 self.ip = LOCALHOST
424 self.ip = LOCALHOST
424 self.log.info("Forwarding connections to %s via %s"%(remote_ip, self.sshserver))
425 self.log.info("Forwarding connections to %s via %s"%(remote_ip, self.sshserver))
425
426
426 if tunnel.try_passwordless_ssh(self.sshserver, self.sshkey):
427 if tunnel.try_passwordless_ssh(self.sshserver, self.sshkey):
427 password=False
428 password=False
428 else:
429 else:
429 password = getpass("SSH Password for %s: "%self.sshserver)
430 password = getpass("SSH Password for %s: "%self.sshserver)
430
431
431 for lp,rp in zip(lports, rports):
432 for lp,rp in zip(lports, rports):
432 tunnel.ssh_tunnel(lp, rp, self.sshserver, remote_ip, self.sshkey, password)
433 tunnel.ssh_tunnel(lp, rp, self.sshserver, remote_ip, self.sshkey, password)
433
434
434 cf = self.connection_file
435 cf = self.connection_file
435 base,ext = os.path.splitext(cf)
436 base,ext = os.path.splitext(cf)
436 base = os.path.basename(base)
437 base = os.path.basename(base)
437 self.connection_file = os.path.basename(base)+'-ssh'+ext
438 self.connection_file = os.path.basename(base)+'-ssh'+ext
438 self.log.critical("To connect another client via this tunnel, use:")
439 self.log.critical("To connect another client via this tunnel, use:")
439 self.log.critical("--existing %s" % self.connection_file)
440 self.log.critical("--existing %s" % self.connection_file)
440
441
441 def init_kernel_manager(self):
442 def init_kernel_manager(self):
442 # Don't let Qt or ZMQ swallow KeyboardInterupts.
443 # Don't let Qt or ZMQ swallow KeyboardInterupts.
443 signal.signal(signal.SIGINT, signal.SIG_DFL)
444 signal.signal(signal.SIGINT, signal.SIG_DFL)
444 sec = self.profile_dir.security_dir
445 sec = self.profile_dir.security_dir
445 try:
446 try:
446 cf = filefind(self.connection_file, ['.', sec])
447 cf = filefind(self.connection_file, ['.', sec])
447 except IOError:
448 except IOError:
448 # file might not exist, use
449 # file might not exist, use
449 if self.connection_file == os.path.basename(self.connection_file):
450 if self.connection_file == os.path.basename(self.connection_file):
450 # just shortname, put it in security dir
451 # just shortname, put it in security dir
451 cf = os.path.join(sec, self.connection_file)
452 cf = os.path.join(sec, self.connection_file)
452 else:
453 else:
453 cf = self.connection_file
454 cf = self.connection_file
454
455
455 # Create a KernelManager and start a kernel.
456 # Create a KernelManager and start a kernel.
456 self.kernel_manager = QtKernelManager(
457 self.kernel_manager = QtKernelManager(
457 ip=self.ip,
458 ip=self.ip,
458 shell_port=self.shell_port,
459 shell_port=self.shell_port,
459 iopub_port=self.iopub_port,
460 iopub_port=self.iopub_port,
460 stdin_port=self.stdin_port,
461 stdin_port=self.stdin_port,
461 hb_port=self.hb_port,
462 hb_port=self.hb_port,
462 connection_file=cf,
463 connection_file=cf,
463 config=self.config,
464 config=self.config,
464 )
465 )
465 # start the kernel
466 # start the kernel
466 if not self.existing:
467 if not self.existing:
467 kwargs = dict(ipython=not self.pure)
468 kwargs = dict(ipython=not self.pure)
468 kwargs['extra_arguments'] = self.kernel_argv
469 kwargs['extra_arguments'] = self.kernel_argv
469 self.kernel_manager.start_kernel(**kwargs)
470 self.kernel_manager.start_kernel(**kwargs)
470 elif self.sshserver:
471 elif self.sshserver:
471 # ssh, write new connection file
472 # ssh, write new connection file
472 self.kernel_manager.write_connection_file()
473 self.kernel_manager.write_connection_file()
473 self.kernel_manager.start_channels()
474 self.kernel_manager.start_channels()
474
475
475
476
476 def init_qt_elements(self):
477 def init_qt_elements(self):
477 # Create the widget.
478 # Create the widget.
478 self.app = QtGui.QApplication([])
479 self.app = QtGui.QApplication([])
479 local_kernel = (not self.existing) or self.ip in LOCAL_IPS
480 local_kernel = (not self.existing) or self.ip in LOCAL_IPS
480 self.widget = self.widget_factory(config=self.config,
481 self.widget = self.widget_factory(config=self.config,
481 local_kernel=local_kernel)
482 local_kernel=local_kernel)
482 self.widget.kernel_manager = self.kernel_manager
483 self.widget.kernel_manager = self.kernel_manager
483 self.window = MainWindow(self.app, self.widget, self.existing,
484 self.window = MainWindow(self.app, self.widget, self.existing,
484 may_close=local_kernel,
485 may_close=local_kernel,
485 confirm_exit=self.confirm_exit)
486 confirm_exit=self.confirm_exit)
486 self.window.setWindowTitle('Python' if self.pure else 'IPython')
487 self.window.setWindowTitle('Python' if self.pure else 'IPython')
487
488
488 def init_colors(self):
489 def init_colors(self):
489 """Configure the coloring of the widget"""
490 """Configure the coloring of the widget"""
490 # Note: This will be dramatically simplified when colors
491 # Note: This will be dramatically simplified when colors
491 # are removed from the backend.
492 # are removed from the backend.
492
493
493 if self.pure:
494 if self.pure:
494 # only IPythonWidget supports styling
495 # only IPythonWidget supports styling
495 return
496 return
496
497
497 # parse the colors arg down to current known labels
498 # parse the colors arg down to current known labels
498 try:
499 try:
499 colors = self.config.ZMQInteractiveShell.colors
500 colors = self.config.ZMQInteractiveShell.colors
500 except AttributeError:
501 except AttributeError:
501 colors = None
502 colors = None
502 try:
503 try:
503 style = self.config.IPythonWidget.colors
504 style = self.config.IPythonWidget.colors
504 except AttributeError:
505 except AttributeError:
505 style = None
506 style = None
506
507
507 # find the value for colors:
508 # find the value for colors:
508 if colors:
509 if colors:
509 colors=colors.lower()
510 colors=colors.lower()
510 if colors in ('lightbg', 'light'):
511 if colors in ('lightbg', 'light'):
511 colors='lightbg'
512 colors='lightbg'
512 elif colors in ('dark', 'linux'):
513 elif colors in ('dark', 'linux'):
513 colors='linux'
514 colors='linux'
514 else:
515 else:
515 colors='nocolor'
516 colors='nocolor'
516 elif style:
517 elif style:
517 if style=='bw':
518 if style=='bw':
518 colors='nocolor'
519 colors='nocolor'
519 elif styles.dark_style(style):
520 elif styles.dark_style(style):
520 colors='linux'
521 colors='linux'
521 else:
522 else:
522 colors='lightbg'
523 colors='lightbg'
523 else:
524 else:
524 colors=None
525 colors=None
525
526
526 # Configure the style.
527 # Configure the style.
527 widget = self.widget
528 widget = self.widget
528 if style:
529 if style:
529 widget.style_sheet = styles.sheet_from_template(style, colors)
530 widget.style_sheet = styles.sheet_from_template(style, colors)
530 widget.syntax_style = style
531 widget.syntax_style = style
531 widget._syntax_style_changed()
532 widget._syntax_style_changed()
532 widget._style_sheet_changed()
533 widget._style_sheet_changed()
533 elif colors:
534 elif colors:
534 # use a default style
535 # use a default style
535 widget.set_default_style(colors=colors)
536 widget.set_default_style(colors=colors)
536 else:
537 else:
537 # this is redundant for now, but allows the widget's
538 # this is redundant for now, but allows the widget's
538 # defaults to change
539 # defaults to change
539 widget.set_default_style()
540 widget.set_default_style()
540
541
541 if self.stylesheet:
542 if self.stylesheet:
542 # we got an expicit stylesheet
543 # we got an expicit stylesheet
543 if os.path.isfile(self.stylesheet):
544 if os.path.isfile(self.stylesheet):
544 with open(self.stylesheet) as f:
545 with open(self.stylesheet) as f:
545 sheet = f.read()
546 sheet = f.read()
546 widget.style_sheet = sheet
547 widget.style_sheet = sheet
547 widget._style_sheet_changed()
548 widget._style_sheet_changed()
548 else:
549 else:
549 raise IOError("Stylesheet %r not found."%self.stylesheet)
550 raise IOError("Stylesheet %r not found."%self.stylesheet)
550
551
551 def initialize(self, argv=None):
552 def initialize(self, argv=None):
552 super(IPythonQtConsoleApp, self).initialize(argv)
553 super(IPythonQtConsoleApp, self).initialize(argv)
553 self.init_connection_file()
554 self.init_connection_file()
554 default_secure(self.config)
555 default_secure(self.config)
555 self.init_ssh()
556 self.init_ssh()
556 self.init_kernel_manager()
557 self.init_kernel_manager()
557 self.init_qt_elements()
558 self.init_qt_elements()
558 self.init_colors()
559 self.init_colors()
559 self.init_window_shortcut()
560 self.init_window_shortcut()
560
561
561 def init_window_shortcut(self):
562 def init_window_shortcut(self):
562 fullScreenAction = QtGui.QAction('Toggle Full Screen', self.window)
563 fullScreenAction = QtGui.QAction('Toggle Full Screen', self.window)
563 fullScreenAction.setShortcut('Ctrl+Meta+Space')
564 fullScreenAction.setShortcut('Ctrl+Meta+Space')
564 fullScreenAction.triggered.connect(self.toggleFullScreen)
565 fullScreenAction.triggered.connect(self.toggleFullScreen)
565 self.window.addAction(fullScreenAction)
566 self.window.addAction(fullScreenAction)
566
567
567 def toggleFullScreen(self):
568 def toggleFullScreen(self):
568 if not self.window.isFullScreen():
569 if not self.window.isFullScreen():
569 self.window.showFullScreen()
570 self.window.showFullScreen()
570 else:
571 else:
571 self.window.showNormal()
572 self.window.showNormal()
572
573
573 def start(self):
574 def start(self):
574
575
575 # draw the window
576 # draw the window
576 self.window.show()
577 self.window.show()
577
578
578 # Start the application main loop.
579 # Start the application main loop.
579 self.app.exec_()
580 self.app.exec_()
580
581
581 #-----------------------------------------------------------------------------
582 #-----------------------------------------------------------------------------
582 # Main entry point
583 # Main entry point
583 #-----------------------------------------------------------------------------
584 #-----------------------------------------------------------------------------
584
585
585 def main():
586 def main():
586 app = IPythonQtConsoleApp()
587 app = IPythonQtConsoleApp()
587 app.initialize()
588 app.initialize()
588 app.start()
589 app.start()
589
590
590
591
591 if __name__ == '__main__':
592 if __name__ == '__main__':
592 main()
593 main()
@@ -1,207 +1,208 b''
1 """ Defines helper functions for creating kernel entry points and process
1 """ Defines helper functions for creating kernel entry points and process
2 launchers.
2 launchers.
3 """
3 """
4
4
5 # Standard library imports.
5 # Standard library imports.
6 import atexit
6 import atexit
7 import os
7 import os
8 import socket
8 import socket
9 from subprocess import Popen, PIPE
9 from subprocess import Popen, PIPE
10 import sys
10 import sys
11 import tempfile
11 import tempfile
12
12
13 # System library imports
13 # System library imports
14 from zmq.utils import jsonapi as json
14 from zmq.utils import jsonapi as json
15
15
16 # IPython imports
16 # IPython imports
17 from IPython.utils.localinterfaces import LOCALHOST
17 from IPython.utils.localinterfaces import LOCALHOST
18 from IPython.utils.py3compat import bytes_to_str
18
19
19 # Local imports.
20 # Local imports.
20 from parentpoller import ParentPollerWindows
21 from parentpoller import ParentPollerWindows
21
22
22 def write_connection_file(fname=None, shell_port=0, iopub_port=0, stdin_port=0, hb_port=0,
23 def write_connection_file(fname=None, shell_port=0, iopub_port=0, stdin_port=0, hb_port=0,
23 ip=LOCALHOST, key=''):
24 ip=LOCALHOST, key=b''):
24 """Generates a JSON config file, including the selection of random ports.
25 """Generates a JSON config file, including the selection of random ports.
25
26
26 Parameters
27 Parameters
27 ----------
28 ----------
28
29
29 fname : unicode
30 fname : unicode
30 The path to the file to write
31 The path to the file to write
31
32
32 shell_port : int, optional
33 shell_port : int, optional
33 The port to use for XREP channel.
34 The port to use for XREP channel.
34
35
35 iopub_port : int, optional
36 iopub_port : int, optional
36 The port to use for the SUB channel.
37 The port to use for the SUB channel.
37
38
38 stdin_port : int, optional
39 stdin_port : int, optional
39 The port to use for the REQ (raw input) channel.
40 The port to use for the REQ (raw input) channel.
40
41
41 hb_port : int, optional
42 hb_port : int, optional
42 The port to use for the hearbeat REP channel.
43 The port to use for the hearbeat REP channel.
43
44
44 ip : str, optional
45 ip : str, optional
45 The ip address the kernel will bind to.
46 The ip address the kernel will bind to.
46
47
47 key : str, optional
48 key : str, optional
48 The Session key used for HMAC authentication.
49 The Session key used for HMAC authentication.
49
50
50 """
51 """
51 # default to temporary connector file
52 # default to temporary connector file
52 if not fname:
53 if not fname:
53 fname = tempfile.mktemp('.json')
54 fname = tempfile.mktemp('.json')
54
55
55 # Find open ports as necessary.
56 # Find open ports as necessary.
56 ports = []
57 ports = []
57 ports_needed = int(shell_port <= 0) + int(iopub_port <= 0) + \
58 ports_needed = int(shell_port <= 0) + int(iopub_port <= 0) + \
58 int(stdin_port <= 0) + int(hb_port <= 0)
59 int(stdin_port <= 0) + int(hb_port <= 0)
59 for i in xrange(ports_needed):
60 for i in xrange(ports_needed):
60 sock = socket.socket()
61 sock = socket.socket()
61 sock.bind(('', 0))
62 sock.bind(('', 0))
62 ports.append(sock)
63 ports.append(sock)
63 for i, sock in enumerate(ports):
64 for i, sock in enumerate(ports):
64 port = sock.getsockname()[1]
65 port = sock.getsockname()[1]
65 sock.close()
66 sock.close()
66 ports[i] = port
67 ports[i] = port
67 if shell_port <= 0:
68 if shell_port <= 0:
68 shell_port = ports.pop(0)
69 shell_port = ports.pop(0)
69 if iopub_port <= 0:
70 if iopub_port <= 0:
70 iopub_port = ports.pop(0)
71 iopub_port = ports.pop(0)
71 if stdin_port <= 0:
72 if stdin_port <= 0:
72 stdin_port = ports.pop(0)
73 stdin_port = ports.pop(0)
73 if hb_port <= 0:
74 if hb_port <= 0:
74 hb_port = ports.pop(0)
75 hb_port = ports.pop(0)
75
76
76 cfg = dict( shell_port=shell_port,
77 cfg = dict( shell_port=shell_port,
77 iopub_port=iopub_port,
78 iopub_port=iopub_port,
78 stdin_port=stdin_port,
79 stdin_port=stdin_port,
79 hb_port=hb_port,
80 hb_port=hb_port,
80 )
81 )
81 cfg['ip'] = ip
82 cfg['ip'] = ip
82 cfg['key'] = key
83 cfg['key'] = bytes_to_str(key)
83
84
84 with open(fname, 'wb') as f:
85 with open(fname, 'wb') as f:
85 f.write(json.dumps(cfg, indent=2))
86 f.write(json.dumps(cfg, indent=2))
86
87
87 return fname, cfg
88 return fname, cfg
88
89
89
90
90 def base_launch_kernel(code, fname, stdin=None, stdout=None, stderr=None,
91 def base_launch_kernel(code, fname, stdin=None, stdout=None, stderr=None,
91 executable=None, independent=False, extra_arguments=[]):
92 executable=None, independent=False, extra_arguments=[]):
92 """ Launches a localhost kernel, binding to the specified ports.
93 """ Launches a localhost kernel, binding to the specified ports.
93
94
94 Parameters
95 Parameters
95 ----------
96 ----------
96 code : str,
97 code : str,
97 A string of Python code that imports and executes a kernel entry point.
98 A string of Python code that imports and executes a kernel entry point.
98
99
99 stdin, stdout, stderr : optional (default None)
100 stdin, stdout, stderr : optional (default None)
100 Standards streams, as defined in subprocess.Popen.
101 Standards streams, as defined in subprocess.Popen.
101
102
102 fname : unicode, optional
103 fname : unicode, optional
103 The JSON connector file, containing ip/port/hmac key information.
104 The JSON connector file, containing ip/port/hmac key information.
104
105
105 key : str, optional
106 key : str, optional
106 The Session key used for HMAC authentication.
107 The Session key used for HMAC authentication.
107
108
108 executable : str, optional (default sys.executable)
109 executable : str, optional (default sys.executable)
109 The Python executable to use for the kernel process.
110 The Python executable to use for the kernel process.
110
111
111 independent : bool, optional (default False)
112 independent : bool, optional (default False)
112 If set, the kernel process is guaranteed to survive if this process
113 If set, the kernel process is guaranteed to survive if this process
113 dies. If not set, an effort is made to ensure that the kernel is killed
114 dies. If not set, an effort is made to ensure that the kernel is killed
114 when this process dies. Note that in this case it is still good practice
115 when this process dies. Note that in this case it is still good practice
115 to kill kernels manually before exiting.
116 to kill kernels manually before exiting.
116
117
117 extra_arguments = list, optional
118 extra_arguments = list, optional
118 A list of extra arguments to pass when executing the launch code.
119 A list of extra arguments to pass when executing the launch code.
119
120
120 Returns
121 Returns
121 -------
122 -------
122 A tuple of form:
123 A tuple of form:
123 (kernel_process, shell_port, iopub_port, stdin_port, hb_port)
124 (kernel_process, shell_port, iopub_port, stdin_port, hb_port)
124 where kernel_process is a Popen object and the ports are integers.
125 where kernel_process is a Popen object and the ports are integers.
125 """
126 """
126
127
127 # Build the kernel launch command.
128 # Build the kernel launch command.
128 if executable is None:
129 if executable is None:
129 executable = sys.executable
130 executable = sys.executable
130 arguments = [ executable, '-c', code, '-f', fname ]
131 arguments = [ executable, '-c', code, '-f', fname ]
131 arguments.extend(extra_arguments)
132 arguments.extend(extra_arguments)
132
133
133 # Popen will fail (sometimes with a deadlock) if stdin, stdout, and stderr
134 # Popen will fail (sometimes with a deadlock) if stdin, stdout, and stderr
134 # are invalid. Unfortunately, there is in general no way to detect whether
135 # are invalid. Unfortunately, there is in general no way to detect whether
135 # they are valid. The following two blocks redirect them to (temporary)
136 # they are valid. The following two blocks redirect them to (temporary)
136 # pipes in certain important cases.
137 # pipes in certain important cases.
137
138
138 # If this process has been backgrounded, our stdin is invalid. Since there
139 # If this process has been backgrounded, our stdin is invalid. Since there
139 # is no compelling reason for the kernel to inherit our stdin anyway, we'll
140 # is no compelling reason for the kernel to inherit our stdin anyway, we'll
140 # place this one safe and always redirect.
141 # place this one safe and always redirect.
141 redirect_in = True
142 redirect_in = True
142 _stdin = PIPE if stdin is None else stdin
143 _stdin = PIPE if stdin is None else stdin
143
144
144 # If this process in running on pythonw, we know that stdin, stdout, and
145 # If this process in running on pythonw, we know that stdin, stdout, and
145 # stderr are all invalid.
146 # stderr are all invalid.
146 redirect_out = sys.executable.endswith('pythonw.exe')
147 redirect_out = sys.executable.endswith('pythonw.exe')
147 if redirect_out:
148 if redirect_out:
148 _stdout = PIPE if stdout is None else stdout
149 _stdout = PIPE if stdout is None else stdout
149 _stderr = PIPE if stderr is None else stderr
150 _stderr = PIPE if stderr is None else stderr
150 else:
151 else:
151 _stdout, _stderr = stdout, stderr
152 _stdout, _stderr = stdout, stderr
152
153
153 # Spawn a kernel.
154 # Spawn a kernel.
154 if sys.platform == 'win32':
155 if sys.platform == 'win32':
155 # Create a Win32 event for interrupting the kernel.
156 # Create a Win32 event for interrupting the kernel.
156 interrupt_event = ParentPollerWindows.create_interrupt_event()
157 interrupt_event = ParentPollerWindows.create_interrupt_event()
157 arguments += [ '--interrupt=%i'%interrupt_event ]
158 arguments += [ '--interrupt=%i'%interrupt_event ]
158
159
159 # If the kernel is running on pythonw and stdout/stderr are not been
160 # If the kernel is running on pythonw and stdout/stderr are not been
160 # re-directed, it will crash when more than 4KB of data is written to
161 # re-directed, it will crash when more than 4KB of data is written to
161 # stdout or stderr. This is a bug that has been with Python for a very
162 # stdout or stderr. This is a bug that has been with Python for a very
162 # long time; see http://bugs.python.org/issue706263.
163 # long time; see http://bugs.python.org/issue706263.
163 # A cleaner solution to this problem would be to pass os.devnull to
164 # A cleaner solution to this problem would be to pass os.devnull to
164 # Popen directly. Unfortunately, that does not work.
165 # Popen directly. Unfortunately, that does not work.
165 if executable.endswith('pythonw.exe'):
166 if executable.endswith('pythonw.exe'):
166 if stdout is None:
167 if stdout is None:
167 arguments.append('--no-stdout')
168 arguments.append('--no-stdout')
168 if stderr is None:
169 if stderr is None:
169 arguments.append('--no-stderr')
170 arguments.append('--no-stderr')
170
171
171 # Launch the kernel process.
172 # Launch the kernel process.
172 if independent:
173 if independent:
173 proc = Popen(arguments,
174 proc = Popen(arguments,
174 creationflags=512, # CREATE_NEW_PROCESS_GROUP
175 creationflags=512, # CREATE_NEW_PROCESS_GROUP
175 stdin=_stdin, stdout=_stdout, stderr=_stderr)
176 stdin=_stdin, stdout=_stdout, stderr=_stderr)
176 else:
177 else:
177 from _subprocess import DuplicateHandle, GetCurrentProcess, \
178 from _subprocess import DuplicateHandle, GetCurrentProcess, \
178 DUPLICATE_SAME_ACCESS
179 DUPLICATE_SAME_ACCESS
179 pid = GetCurrentProcess()
180 pid = GetCurrentProcess()
180 handle = DuplicateHandle(pid, pid, pid, 0,
181 handle = DuplicateHandle(pid, pid, pid, 0,
181 True, # Inheritable by new processes.
182 True, # Inheritable by new processes.
182 DUPLICATE_SAME_ACCESS)
183 DUPLICATE_SAME_ACCESS)
183 proc = Popen(arguments + ['--parent=%i'%int(handle)],
184 proc = Popen(arguments + ['--parent=%i'%int(handle)],
184 stdin=_stdin, stdout=_stdout, stderr=_stderr)
185 stdin=_stdin, stdout=_stdout, stderr=_stderr)
185
186
186 # Attach the interrupt event to the Popen objet so it can be used later.
187 # Attach the interrupt event to the Popen objet so it can be used later.
187 proc.win32_interrupt_event = interrupt_event
188 proc.win32_interrupt_event = interrupt_event
188
189
189 else:
190 else:
190 if independent:
191 if independent:
191 proc = Popen(arguments, preexec_fn=lambda: os.setsid(),
192 proc = Popen(arguments, preexec_fn=lambda: os.setsid(),
192 stdin=_stdin, stdout=_stdout, stderr=_stderr)
193 stdin=_stdin, stdout=_stdout, stderr=_stderr)
193 else:
194 else:
194 proc = Popen(arguments + ['--parent=1'],
195 proc = Popen(arguments + ['--parent=1'],
195 stdin=_stdin, stdout=_stdout, stderr=_stderr)
196 stdin=_stdin, stdout=_stdout, stderr=_stderr)
196
197
197 # Clean up pipes created to work around Popen bug.
198 # Clean up pipes created to work around Popen bug.
198 if redirect_in:
199 if redirect_in:
199 if stdin is None:
200 if stdin is None:
200 proc.stdin.close()
201 proc.stdin.close()
201 if redirect_out:
202 if redirect_out:
202 if stdout is None:
203 if stdout is None:
203 proc.stdout.close()
204 proc.stdout.close()
204 if stderr is None:
205 if stderr is None:
205 proc.stderr.close()
206 proc.stderr.close()
206
207
207 return proc
208 return proc
@@ -1,291 +1,292 b''
1 """An Application for launching a kernel
1 """An Application for launching a kernel
2
2
3 Authors
3 Authors
4 -------
4 -------
5 * MinRK
5 * MinRK
6 """
6 """
7 #-----------------------------------------------------------------------------
7 #-----------------------------------------------------------------------------
8 # Copyright (C) 2011 The IPython Development Team
8 # Copyright (C) 2011 The IPython Development Team
9 #
9 #
10 # Distributed under the terms of the BSD License. The full license is in
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING.txt, distributed as part of this software.
11 # the file COPYING.txt, distributed as part of this software.
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 # Standard library imports.
18 # Standard library imports.
19 import os
19 import os
20 import sys
20 import sys
21
21
22 # System library imports.
22 # System library imports.
23 import zmq
23 import zmq
24 from zmq.utils import jsonapi as json
24 from zmq.utils import jsonapi as json
25
25
26 # IPython imports.
26 # IPython imports.
27 from IPython.core.ultratb import FormattedTB
27 from IPython.core.ultratb import FormattedTB
28 from IPython.core.application import (
28 from IPython.core.application import (
29 BaseIPythonApplication, base_flags, base_aliases
29 BaseIPythonApplication, base_flags, base_aliases
30 )
30 )
31 from IPython.utils import io
31 from IPython.utils import io
32 from IPython.utils.localinterfaces import LOCALHOST
32 from IPython.utils.localinterfaces import LOCALHOST
33 from IPython.utils.path import filefind
33 from IPython.utils.path import filefind
34 from IPython.utils.py3compat import str_to_bytes
34 from IPython.utils.traitlets import (Any, Instance, Dict, Unicode, Int, Bool,
35 from IPython.utils.traitlets import (Any, Instance, Dict, Unicode, Int, Bool,
35 DottedObjectName)
36 DottedObjectName)
36 from IPython.utils.importstring import import_item
37 from IPython.utils.importstring import import_item
37 # local imports
38 # local imports
38 from IPython.zmq.entry_point import write_connection_file
39 from IPython.zmq.entry_point import write_connection_file
39 from IPython.zmq.heartbeat import Heartbeat
40 from IPython.zmq.heartbeat import Heartbeat
40 from IPython.zmq.parentpoller import ParentPollerUnix, ParentPollerWindows
41 from IPython.zmq.parentpoller import ParentPollerUnix, ParentPollerWindows
41 from IPython.zmq.session import (
42 from IPython.zmq.session import (
42 Session, session_flags, session_aliases, default_secure,
43 Session, session_flags, session_aliases, default_secure,
43 )
44 )
44
45
45
46
46 #-----------------------------------------------------------------------------
47 #-----------------------------------------------------------------------------
47 # Flags and Aliases
48 # Flags and Aliases
48 #-----------------------------------------------------------------------------
49 #-----------------------------------------------------------------------------
49
50
50 kernel_aliases = dict(base_aliases)
51 kernel_aliases = dict(base_aliases)
51 kernel_aliases.update({
52 kernel_aliases.update({
52 'ip' : 'KernelApp.ip',
53 'ip' : 'KernelApp.ip',
53 'hb' : 'KernelApp.hb_port',
54 'hb' : 'KernelApp.hb_port',
54 'shell' : 'KernelApp.shell_port',
55 'shell' : 'KernelApp.shell_port',
55 'iopub' : 'KernelApp.iopub_port',
56 'iopub' : 'KernelApp.iopub_port',
56 'stdin' : 'KernelApp.stdin_port',
57 'stdin' : 'KernelApp.stdin_port',
57 'f' : 'KernelApp.connection_file',
58 'f' : 'KernelApp.connection_file',
58 'parent': 'KernelApp.parent',
59 'parent': 'KernelApp.parent',
59 })
60 })
60 if sys.platform.startswith('win'):
61 if sys.platform.startswith('win'):
61 kernel_aliases['interrupt'] = 'KernelApp.interrupt'
62 kernel_aliases['interrupt'] = 'KernelApp.interrupt'
62
63
63 kernel_flags = dict(base_flags)
64 kernel_flags = dict(base_flags)
64 kernel_flags.update({
65 kernel_flags.update({
65 'no-stdout' : (
66 'no-stdout' : (
66 {'KernelApp' : {'no_stdout' : True}},
67 {'KernelApp' : {'no_stdout' : True}},
67 "redirect stdout to the null device"),
68 "redirect stdout to the null device"),
68 'no-stderr' : (
69 'no-stderr' : (
69 {'KernelApp' : {'no_stderr' : True}},
70 {'KernelApp' : {'no_stderr' : True}},
70 "redirect stderr to the null device"),
71 "redirect stderr to the null device"),
71 })
72 })
72
73
73 # inherit flags&aliases for Sessions
74 # inherit flags&aliases for Sessions
74 kernel_aliases.update(session_aliases)
75 kernel_aliases.update(session_aliases)
75 kernel_flags.update(session_flags)
76 kernel_flags.update(session_flags)
76
77
77
78
78
79
79 #-----------------------------------------------------------------------------
80 #-----------------------------------------------------------------------------
80 # Application class for starting a Kernel
81 # Application class for starting a Kernel
81 #-----------------------------------------------------------------------------
82 #-----------------------------------------------------------------------------
82
83
83 class KernelApp(BaseIPythonApplication):
84 class KernelApp(BaseIPythonApplication):
84 name='pykernel'
85 name='pykernel'
85 aliases = Dict(kernel_aliases)
86 aliases = Dict(kernel_aliases)
86 flags = Dict(kernel_flags)
87 flags = Dict(kernel_flags)
87 classes = [Session]
88 classes = [Session]
88 # the kernel class, as an importstring
89 # the kernel class, as an importstring
89 kernel_class = DottedObjectName('IPython.zmq.pykernel.Kernel')
90 kernel_class = DottedObjectName('IPython.zmq.pykernel.Kernel')
90 kernel = Any()
91 kernel = Any()
91 poller = Any() # don't restrict this even though current pollers are all Threads
92 poller = Any() # don't restrict this even though current pollers are all Threads
92 heartbeat = Instance(Heartbeat)
93 heartbeat = Instance(Heartbeat)
93 session = Instance('IPython.zmq.session.Session')
94 session = Instance('IPython.zmq.session.Session')
94 ports = Dict()
95 ports = Dict()
95
96
96 # inherit config file name from parent:
97 # inherit config file name from parent:
97 parent_appname = Unicode(config=True)
98 parent_appname = Unicode(config=True)
98 def _parent_appname_changed(self, name, old, new):
99 def _parent_appname_changed(self, name, old, new):
99 if self.config_file_specified:
100 if self.config_file_specified:
100 # it was manually specified, ignore
101 # it was manually specified, ignore
101 return
102 return
102 self.config_file_name = new.replace('-','_') + u'_config.py'
103 self.config_file_name = new.replace('-','_') + u'_config.py'
103 # don't let this count as specifying the config file
104 # don't let this count as specifying the config file
104 self.config_file_specified = False
105 self.config_file_specified = False
105
106
106 # connection info:
107 # connection info:
107 ip = Unicode(LOCALHOST, config=True,
108 ip = Unicode(LOCALHOST, config=True,
108 help="Set the IP or interface on which the kernel will listen.")
109 help="Set the IP or interface on which the kernel will listen.")
109 hb_port = Int(0, config=True, help="set the heartbeat port [default: random]")
110 hb_port = Int(0, config=True, help="set the heartbeat port [default: random]")
110 shell_port = Int(0, config=True, help="set the shell (XREP) port [default: random]")
111 shell_port = Int(0, config=True, help="set the shell (XREP) port [default: random]")
111 iopub_port = Int(0, config=True, help="set the iopub (PUB) port [default: random]")
112 iopub_port = Int(0, config=True, help="set the iopub (PUB) port [default: random]")
112 stdin_port = Int(0, config=True, help="set the stdin (XREQ) port [default: random]")
113 stdin_port = Int(0, config=True, help="set the stdin (XREQ) port [default: random]")
113 connection_file = Unicode('', config=True,
114 connection_file = Unicode('', config=True,
114 help="""JSON file in which to store connection info [default: kernel-<pid>.json]
115 help="""JSON file in which to store connection info [default: kernel-<pid>.json]
115
116
116 This file will contain the IP, ports, and authentication key needed to connect
117 This file will contain the IP, ports, and authentication key needed to connect
117 clients to this kernel. By default, this file will be created in the security-dir
118 clients to this kernel. By default, this file will be created in the security-dir
118 of the current profile, but can be specified by absolute path.
119 of the current profile, but can be specified by absolute path.
119 """)
120 """)
120
121
121 # streams, etc.
122 # streams, etc.
122 no_stdout = Bool(False, config=True, help="redirect stdout to the null device")
123 no_stdout = Bool(False, config=True, help="redirect stdout to the null device")
123 no_stderr = Bool(False, config=True, help="redirect stderr to the null device")
124 no_stderr = Bool(False, config=True, help="redirect stderr to the null device")
124 outstream_class = DottedObjectName('IPython.zmq.iostream.OutStream',
125 outstream_class = DottedObjectName('IPython.zmq.iostream.OutStream',
125 config=True, help="The importstring for the OutStream factory")
126 config=True, help="The importstring for the OutStream factory")
126 displayhook_class = DottedObjectName('IPython.zmq.displayhook.ZMQDisplayHook',
127 displayhook_class = DottedObjectName('IPython.zmq.displayhook.ZMQDisplayHook',
127 config=True, help="The importstring for the DisplayHook factory")
128 config=True, help="The importstring for the DisplayHook factory")
128
129
129 # polling
130 # polling
130 parent = Int(0, config=True,
131 parent = Int(0, config=True,
131 help="""kill this process if its parent dies. On Windows, the argument
132 help="""kill this process if its parent dies. On Windows, the argument
132 specifies the HANDLE of the parent process, otherwise it is simply boolean.
133 specifies the HANDLE of the parent process, otherwise it is simply boolean.
133 """)
134 """)
134 interrupt = Int(0, config=True,
135 interrupt = Int(0, config=True,
135 help="""ONLY USED ON WINDOWS
136 help="""ONLY USED ON WINDOWS
136 Interrupt this process when the parent is signalled.
137 Interrupt this process when the parent is signalled.
137 """)
138 """)
138
139
139 def init_crash_handler(self):
140 def init_crash_handler(self):
140 # Install minimal exception handling
141 # Install minimal exception handling
141 sys.excepthook = FormattedTB(mode='Verbose', color_scheme='NoColor',
142 sys.excepthook = FormattedTB(mode='Verbose', color_scheme='NoColor',
142 ostream=sys.__stdout__)
143 ostream=sys.__stdout__)
143
144
144 def init_poller(self):
145 def init_poller(self):
145 if sys.platform == 'win32':
146 if sys.platform == 'win32':
146 if self.interrupt or self.parent:
147 if self.interrupt or self.parent:
147 self.poller = ParentPollerWindows(self.interrupt, self.parent)
148 self.poller = ParentPollerWindows(self.interrupt, self.parent)
148 elif self.parent:
149 elif self.parent:
149 self.poller = ParentPollerUnix()
150 self.poller = ParentPollerUnix()
150
151
151 def _bind_socket(self, s, port):
152 def _bind_socket(self, s, port):
152 iface = 'tcp://%s' % self.ip
153 iface = 'tcp://%s' % self.ip
153 if port <= 0:
154 if port <= 0:
154 port = s.bind_to_random_port(iface)
155 port = s.bind_to_random_port(iface)
155 else:
156 else:
156 s.bind(iface + ':%i'%port)
157 s.bind(iface + ':%i'%port)
157 return port
158 return port
158
159
159 def load_connection_file(self):
160 def load_connection_file(self):
160 """load ip/port/hmac config from JSON connection file"""
161 """load ip/port/hmac config from JSON connection file"""
161 try:
162 try:
162 fname = filefind(self.connection_file, ['.', self.profile_dir.security_dir])
163 fname = filefind(self.connection_file, ['.', self.profile_dir.security_dir])
163 except IOError:
164 except IOError:
164 self.log.debug("Connection file not found: %s", self.connection_file)
165 self.log.debug("Connection file not found: %s", self.connection_file)
165 return
166 return
166 self.log.debug(u"Loading connection file %s", fname)
167 self.log.debug(u"Loading connection file %s", fname)
167 with open(fname) as f:
168 with open(fname) as f:
168 s = f.read()
169 s = f.read()
169 cfg = json.loads(s)
170 cfg = json.loads(s)
170 if self.ip == LOCALHOST and 'ip' in cfg:
171 if self.ip == LOCALHOST and 'ip' in cfg:
171 # not overridden by config or cl_args
172 # not overridden by config or cl_args
172 self.ip = cfg['ip']
173 self.ip = cfg['ip']
173 for channel in ('hb', 'shell', 'iopub', 'stdin'):
174 for channel in ('hb', 'shell', 'iopub', 'stdin'):
174 name = channel + '_port'
175 name = channel + '_port'
175 if getattr(self, name) == 0 and name in cfg:
176 if getattr(self, name) == 0 and name in cfg:
176 # not overridden by config or cl_args
177 # not overridden by config or cl_args
177 setattr(self, name, cfg[name])
178 setattr(self, name, cfg[name])
178 if 'key' in cfg:
179 if 'key' in cfg:
179 self.config.Session.key = cfg['key']
180 self.config.Session.key = str_to_bytes(cfg['key'])
180
181
181 def write_connection_file(self):
182 def write_connection_file(self):
182 """write connection info to JSON file"""
183 """write connection info to JSON file"""
183 if os.path.basename(self.connection_file) == self.connection_file:
184 if os.path.basename(self.connection_file) == self.connection_file:
184 cf = os.path.join(self.profile_dir.security_dir, self.connection_file)
185 cf = os.path.join(self.profile_dir.security_dir, self.connection_file)
185 else:
186 else:
186 cf = self.connection_file
187 cf = self.connection_file
187 write_connection_file(cf, ip=self.ip, key=self.session.key,
188 write_connection_file(cf, ip=self.ip, key=self.session.key,
188 shell_port=self.shell_port, stdin_port=self.stdin_port, hb_port=self.hb_port,
189 shell_port=self.shell_port, stdin_port=self.stdin_port, hb_port=self.hb_port,
189 iopub_port=self.iopub_port)
190 iopub_port=self.iopub_port)
190
191
191 def init_connection_file(self):
192 def init_connection_file(self):
192 if not self.connection_file:
193 if not self.connection_file:
193 self.connection_file = "kernel-%s.json"%os.getpid()
194 self.connection_file = "kernel-%s.json"%os.getpid()
194
195
195 self.load_connection_file()
196 self.load_connection_file()
196
197
197 def init_sockets(self):
198 def init_sockets(self):
198 # Create a context, a session, and the kernel sockets.
199 # Create a context, a session, and the kernel sockets.
199 self.log.info("Starting the kernel at pid: %i", os.getpid())
200 self.log.info("Starting the kernel at pid: %i", os.getpid())
200 context = zmq.Context.instance()
201 context = zmq.Context.instance()
201 # Uncomment this to try closing the context.
202 # Uncomment this to try closing the context.
202 # atexit.register(context.term)
203 # atexit.register(context.term)
203
204
204 self.shell_socket = context.socket(zmq.ROUTER)
205 self.shell_socket = context.socket(zmq.ROUTER)
205 self.shell_port = self._bind_socket(self.shell_socket, self.shell_port)
206 self.shell_port = self._bind_socket(self.shell_socket, self.shell_port)
206 self.log.debug("shell ROUTER Channel on port: %i"%self.shell_port)
207 self.log.debug("shell ROUTER Channel on port: %i"%self.shell_port)
207
208
208 self.iopub_socket = context.socket(zmq.PUB)
209 self.iopub_socket = context.socket(zmq.PUB)
209 self.iopub_port = self._bind_socket(self.iopub_socket, self.iopub_port)
210 self.iopub_port = self._bind_socket(self.iopub_socket, self.iopub_port)
210 self.log.debug("iopub PUB Channel on port: %i"%self.iopub_port)
211 self.log.debug("iopub PUB Channel on port: %i"%self.iopub_port)
211
212
212 self.stdin_socket = context.socket(zmq.ROUTER)
213 self.stdin_socket = context.socket(zmq.ROUTER)
213 self.stdin_port = self._bind_socket(self.stdin_socket, self.stdin_port)
214 self.stdin_port = self._bind_socket(self.stdin_socket, self.stdin_port)
214 self.log.debug("stdin ROUTER Channel on port: %i"%self.stdin_port)
215 self.log.debug("stdin ROUTER Channel on port: %i"%self.stdin_port)
215
216
216 self.heartbeat = Heartbeat(context, (self.ip, self.hb_port))
217 self.heartbeat = Heartbeat(context, (self.ip, self.hb_port))
217 self.hb_port = self.heartbeat.port
218 self.hb_port = self.heartbeat.port
218 self.log.debug("Heartbeat REP Channel on port: %i"%self.hb_port)
219 self.log.debug("Heartbeat REP Channel on port: %i"%self.hb_port)
219
220
220 # Helper to make it easier to connect to an existing kernel.
221 # Helper to make it easier to connect to an existing kernel.
221 # set log-level to critical, to make sure it is output
222 # set log-level to critical, to make sure it is output
222 self.log.critical("To connect another client to this kernel, use:")
223 self.log.critical("To connect another client to this kernel, use:")
223 if os.path.dirname(self.connection_file) == self.profile_dir.security_dir:
224 if os.path.dirname(self.connection_file) == self.profile_dir.security_dir:
224 # use shortname
225 # use shortname
225 tail = os.path.basename(self.connection_file)
226 tail = os.path.basename(self.connection_file)
226 if self.profile != 'default':
227 if self.profile != 'default':
227 tail += " --profile %s" % self.profile_name
228 tail += " --profile %s" % self.profile_name
228 else:
229 else:
229 tail = self.connection_file
230 tail = self.connection_file
230 self.log.critical("--existing %s", tail)
231 self.log.critical("--existing %s", tail)
231
232
232
233
233 self.ports = dict(shell=self.shell_port, iopub=self.iopub_port,
234 self.ports = dict(shell=self.shell_port, iopub=self.iopub_port,
234 stdin=self.stdin_port, hb=self.hb_port)
235 stdin=self.stdin_port, hb=self.hb_port)
235
236
236 def init_session(self):
237 def init_session(self):
237 """create our session object"""
238 """create our session object"""
238 default_secure(self.config)
239 default_secure(self.config)
239 self.session = Session(config=self.config, username=u'kernel')
240 self.session = Session(config=self.config, username=u'kernel')
240
241
241 def init_blackhole(self):
242 def init_blackhole(self):
242 """redirects stdout/stderr to devnull if necessary"""
243 """redirects stdout/stderr to devnull if necessary"""
243 if self.no_stdout or self.no_stderr:
244 if self.no_stdout or self.no_stderr:
244 blackhole = file(os.devnull, 'w')
245 blackhole = file(os.devnull, 'w')
245 if self.no_stdout:
246 if self.no_stdout:
246 sys.stdout = sys.__stdout__ = blackhole
247 sys.stdout = sys.__stdout__ = blackhole
247 if self.no_stderr:
248 if self.no_stderr:
248 sys.stderr = sys.__stderr__ = blackhole
249 sys.stderr = sys.__stderr__ = blackhole
249
250
250 def init_io(self):
251 def init_io(self):
251 """Redirect input streams and set a display hook."""
252 """Redirect input streams and set a display hook."""
252 if self.outstream_class:
253 if self.outstream_class:
253 outstream_factory = import_item(str(self.outstream_class))
254 outstream_factory = import_item(str(self.outstream_class))
254 sys.stdout = outstream_factory(self.session, self.iopub_socket, u'stdout')
255 sys.stdout = outstream_factory(self.session, self.iopub_socket, u'stdout')
255 sys.stderr = outstream_factory(self.session, self.iopub_socket, u'stderr')
256 sys.stderr = outstream_factory(self.session, self.iopub_socket, u'stderr')
256 if self.displayhook_class:
257 if self.displayhook_class:
257 displayhook_factory = import_item(str(self.displayhook_class))
258 displayhook_factory = import_item(str(self.displayhook_class))
258 sys.displayhook = displayhook_factory(self.session, self.iopub_socket)
259 sys.displayhook = displayhook_factory(self.session, self.iopub_socket)
259
260
260 def init_kernel(self):
261 def init_kernel(self):
261 """Create the Kernel object itself"""
262 """Create the Kernel object itself"""
262 kernel_factory = import_item(str(self.kernel_class))
263 kernel_factory = import_item(str(self.kernel_class))
263 self.kernel = kernel_factory(config=self.config, session=self.session,
264 self.kernel = kernel_factory(config=self.config, session=self.session,
264 shell_socket=self.shell_socket,
265 shell_socket=self.shell_socket,
265 iopub_socket=self.iopub_socket,
266 iopub_socket=self.iopub_socket,
266 stdin_socket=self.stdin_socket,
267 stdin_socket=self.stdin_socket,
267 log=self.log
268 log=self.log
268 )
269 )
269 self.kernel.record_ports(self.ports)
270 self.kernel.record_ports(self.ports)
270
271
271 def initialize(self, argv=None):
272 def initialize(self, argv=None):
272 super(KernelApp, self).initialize(argv)
273 super(KernelApp, self).initialize(argv)
273 self.init_blackhole()
274 self.init_blackhole()
274 self.init_connection_file()
275 self.init_connection_file()
275 self.init_session()
276 self.init_session()
276 self.init_poller()
277 self.init_poller()
277 self.init_sockets()
278 self.init_sockets()
278 # writing connection file must be *after* init_sockets
279 # writing connection file must be *after* init_sockets
279 self.write_connection_file()
280 self.write_connection_file()
280 self.init_io()
281 self.init_io()
281 self.init_kernel()
282 self.init_kernel()
282
283
283 def start(self):
284 def start(self):
284 self.heartbeat.start()
285 self.heartbeat.start()
285 if self.poller is not None:
286 if self.poller is not None:
286 self.poller.start()
287 self.poller.start()
287 try:
288 try:
288 self.kernel.start()
289 self.kernel.start()
289 except KeyboardInterrupt:
290 except KeyboardInterrupt:
290 pass
291 pass
291
292
General Comments 0
You need to be logged in to leave comments. Login now