##// END OF EJS Templates
fix typo in stripping kernel args in nb and qt...
MinRK -
Show More
@@ -1,304 +1,304
1 1 """A tornado based IPython notebook server.
2 2
3 3 Authors:
4 4
5 5 * Brian Granger
6 6 """
7 7
8 8 #-----------------------------------------------------------------------------
9 9 # Copyright (C) 2008-2011 The IPython Development Team
10 10 #
11 11 # Distributed under the terms of the BSD License. The full license is in
12 12 # the file COPYING, distributed as part of this software.
13 13 #-----------------------------------------------------------------------------
14 14
15 15 #-----------------------------------------------------------------------------
16 16 # Imports
17 17 #-----------------------------------------------------------------------------
18 18
19 19 import errno
20 20 import logging
21 21 import os
22 22 import signal
23 23 import socket
24 24 import sys
25 25
26 26 import zmq
27 27
28 28 # Install the pyzmq ioloop. This has to be done before anything else from
29 29 # tornado is imported.
30 30 from zmq.eventloop import ioloop
31 31 import tornado.ioloop
32 32 tornado.ioloop.IOLoop = ioloop.IOLoop
33 33
34 34 from tornado import httpserver
35 35 from tornado import web
36 36
37 37 from .kernelmanager import MappingKernelManager
38 38 from .handlers import (LoginHandler,
39 39 NBBrowserHandler, NewHandler, NamedNotebookHandler,
40 40 MainKernelHandler, KernelHandler, KernelActionHandler, IOPubHandler,
41 41 ShellHandler, NotebookRootHandler, NotebookHandler, RSTHandler
42 42 )
43 43 from .notebookmanager import NotebookManager
44 44
45 45 from IPython.core.application import BaseIPythonApplication
46 46 from IPython.core.profiledir import ProfileDir
47 47 from IPython.zmq.session import Session, default_secure
48 48 from IPython.zmq.zmqshell import ZMQInteractiveShell
49 49 from IPython.zmq.ipkernel import (
50 50 flags as ipkernel_flags,
51 51 aliases as ipkernel_aliases,
52 52 IPKernelApp
53 53 )
54 54 from IPython.utils.traitlets import Dict, Unicode, Int, List, Enum
55 55
56 56 #-----------------------------------------------------------------------------
57 57 # Module globals
58 58 #-----------------------------------------------------------------------------
59 59
60 60 _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
61 61 _kernel_action_regex = r"(?P<action>restart|interrupt)"
62 62 _notebook_id_regex = r"(?P<notebook_id>\w+-\w+-\w+-\w+-\w+)"
63 63
64 64 LOCALHOST = '127.0.0.1'
65 65
66 66 _examples = """
67 67 ipython notebook # start the notebook
68 68 ipython notebook --profile=sympy # use the sympy profile
69 69 ipython notebook --pylab=inline # pylab in inline plotting mode
70 70 ipython notebook --certfile=mycert.pem # use SSL/TLS certificate
71 71 ipython notebook --port=5555 --ip=* # Listen on port 5555, all interfaces
72 72 """
73 73
74 74 #-----------------------------------------------------------------------------
75 75 # The Tornado web application
76 76 #-----------------------------------------------------------------------------
77 77
78 78 class NotebookWebApplication(web.Application):
79 79
80 80 def __init__(self, ipython_app, kernel_manager, notebook_manager, log):
81 81 handlers = [
82 82 (r"/", NBBrowserHandler),
83 83 (r"/login", LoginHandler),
84 84 (r"/new", NewHandler),
85 85 (r"/%s" % _notebook_id_regex, NamedNotebookHandler),
86 86 (r"/kernels", MainKernelHandler),
87 87 (r"/kernels/%s" % _kernel_id_regex, KernelHandler),
88 88 (r"/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
89 89 (r"/kernels/%s/iopub" % _kernel_id_regex, IOPubHandler),
90 90 (r"/kernels/%s/shell" % _kernel_id_regex, ShellHandler),
91 91 (r"/notebooks", NotebookRootHandler),
92 92 (r"/notebooks/%s" % _notebook_id_regex, NotebookHandler),
93 93 (r"/rstservice/render", RSTHandler)
94 94 ]
95 95 settings = dict(
96 96 template_path=os.path.join(os.path.dirname(__file__), "templates"),
97 97 static_path=os.path.join(os.path.dirname(__file__), "static"),
98 98 cookie_secret=os.urandom(1024),
99 99 login_url="/login",
100 100 )
101 101 web.Application.__init__(self, handlers, **settings)
102 102
103 103 self.kernel_manager = kernel_manager
104 104 self.log = log
105 105 self.notebook_manager = notebook_manager
106 106 self.ipython_app = ipython_app
107 107
108 108
109 109 #-----------------------------------------------------------------------------
110 110 # Aliases and Flags
111 111 #-----------------------------------------------------------------------------
112 112
113 113 flags = dict(ipkernel_flags)
114 114
115 115 # the flags that are specific to the frontend
116 116 # these must be scrubbed before being passed to the kernel,
117 117 # or it will raise an error on unrecognized flags
118 118 notebook_flags = []
119 119
120 120 aliases = dict(ipkernel_aliases)
121 121
122 122 aliases.update({
123 123 'ip': 'IPythonNotebookApp.ip',
124 124 'port': 'IPythonNotebookApp.port',
125 125 'keyfile': 'IPythonNotebookApp.keyfile',
126 126 'certfile': 'IPythonNotebookApp.certfile',
127 127 'ws-hostname': 'IPythonNotebookApp.ws_hostname',
128 128 'notebook-dir': 'NotebookManager.notebook_dir',
129 129 })
130 130
131 131 # remove ipkernel flags that are singletons, and don't make sense in
132 132 # multi-kernel evironment:
133 133 aliases.pop('f', None)
134 134
135 135 notebook_aliases = [u'port', u'ip', u'keyfile', u'certfile', u'ws-hostname',
136 136 u'notebook-dir']
137 137
138 138 #-----------------------------------------------------------------------------
139 139 # IPythonNotebookApp
140 140 #-----------------------------------------------------------------------------
141 141
142 142 class IPythonNotebookApp(BaseIPythonApplication):
143 143
144 144 name = 'ipython-notebook'
145 145 default_config_file_name='ipython_notebook_config.py'
146 146
147 147 description = """
148 148 The IPython HTML Notebook.
149 149
150 150 This launches a Tornado based HTML Notebook Server that serves up an
151 151 HTML5/Javascript Notebook client.
152 152 """
153 153 examples = _examples
154 154
155 155 classes = [IPKernelApp, ZMQInteractiveShell, ProfileDir, Session,
156 156 MappingKernelManager, NotebookManager]
157 157 flags = Dict(flags)
158 158 aliases = Dict(aliases)
159 159
160 160 kernel_argv = List(Unicode)
161 161
162 162 log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
163 163 default_value=logging.INFO,
164 164 config=True,
165 165 help="Set the log level by value or name.")
166 166
167 167 # Network related information.
168 168
169 169 ip = Unicode(LOCALHOST, config=True,
170 170 help="The IP address the notebook server will listen on."
171 171 )
172 172
173 173 def _ip_changed(self, name, old, new):
174 174 if new == u'*': self.ip = u''
175 175
176 176 port = Int(8888, config=True,
177 177 help="The port the notebook server will listen on."
178 178 )
179 179
180 180 ws_hostname = Unicode(LOCALHOST, config=True,
181 181 help="""The FQDN or IP for WebSocket connections. The default will work
182 182 fine when the server is listening on localhost, but this needs to
183 183 be set if the ip option is used. It will be used as the hostname part
184 184 of the WebSocket url: ws://hostname/path."""
185 185 )
186 186
187 187 certfile = Unicode(u'', config=True,
188 188 help="""The full path to an SSL/TLS certificate file."""
189 189 )
190 190
191 191 keyfile = Unicode(u'', config=True,
192 192 help="""The full path to a private key file for usage with SSL/TLS."""
193 193 )
194 194
195 195 password = Unicode(u'', config=True,
196 196 help="""Password to use for web authentication"""
197 197 )
198 198
199 199 def get_ws_url(self):
200 200 """Return the WebSocket URL for this server."""
201 201 if self.certfile:
202 202 prefix = u'wss://'
203 203 else:
204 204 prefix = u'ws://'
205 205 return prefix + self.ws_hostname + u':' + unicode(self.port)
206 206
207 207 def parse_command_line(self, argv=None):
208 208 super(IPythonNotebookApp, self).parse_command_line(argv)
209 209 if argv is None:
210 210 argv = sys.argv[1:]
211 211
212 212 self.kernel_argv = list(argv) # copy
213 213 # Kernel should inherit default config file from frontend
214 214 self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name)
215 215 # Scrub frontend-specific flags
216 216 for a in argv:
217 217 if a.startswith('-') and a.lstrip('-') in notebook_flags:
218 218 self.kernel_argv.remove(a)
219 219 swallow_next = False
220 220 for a in argv:
221 221 if swallow_next:
222 222 self.kernel_argv.remove(a)
223 223 swallow_next = False
224 224 continue
225 225 if a.startswith('-'):
226 split = a.lstrip('-').split('=')[0]
226 split = a.lstrip('-').split('=')
227 227 alias = split[0]
228 228 if alias in notebook_aliases:
229 229 self.kernel_argv.remove(a)
230 230 if len(split) == 1:
231 231 # alias passed with arg via space
232 232 swallow_next = True
233 233
234 234 def init_configurables(self):
235 235 # Don't let Qt or ZMQ swallow KeyboardInterupts.
236 236 signal.signal(signal.SIGINT, signal.SIG_DFL)
237 237
238 238 # force Session default to be secure
239 239 default_secure(self.config)
240 240 # Create a KernelManager and start a kernel.
241 241 self.kernel_manager = MappingKernelManager(
242 242 config=self.config, log=self.log, kernel_argv=self.kernel_argv,
243 243 connection_dir = self.profile_dir.security_dir,
244 244 )
245 245 self.notebook_manager = NotebookManager(config=self.config, log=self.log)
246 246 self.notebook_manager.list_notebooks()
247 247
248 248 def init_logging(self):
249 249 super(IPythonNotebookApp, self).init_logging()
250 250 # This prevents double log messages because tornado use a root logger that
251 251 # self.log is a child of. The logging module dipatches log messages to a log
252 252 # and all of its ancenstors until propagate is set to False.
253 253 self.log.propagate = False
254 254
255 255 def initialize(self, argv=None):
256 256 super(IPythonNotebookApp, self).initialize(argv)
257 257 self.init_configurables()
258 258 self.web_app = NotebookWebApplication(
259 259 self, self.kernel_manager, self.notebook_manager, self.log
260 260 )
261 261 if self.certfile:
262 262 ssl_options = dict(certfile=self.certfile)
263 263 if self.keyfile:
264 264 ssl_options['keyfile'] = self.keyfile
265 265 else:
266 266 ssl_options = None
267 267 self.web_app.password = self.password
268 268 self.http_server = httpserver.HTTPServer(self.web_app, ssl_options=ssl_options)
269 269 if ssl_options is None and not self.ip:
270 270 self.log.critical('WARNING: the notebook server is listening on all IP addresses '
271 271 'but not using any encryption or authentication. This is highly '
272 272 'insecure and not recommended.')
273 273
274 274 # Try random ports centered around the default.
275 275 from random import randint
276 276 n = 50 # Max number of attempts, keep reasonably large.
277 277 for port in [self.port] + [self.port + randint(-2*n, 2*n) for i in range(n)]:
278 278 try:
279 279 self.http_server.listen(port, self.ip)
280 280 except socket.error, e:
281 281 if e.errno != errno.EADDRINUSE:
282 282 raise
283 283 self.log.info('The port %i is already in use, trying another random port.' % port)
284 284 else:
285 285 self.port = port
286 286 break
287 287
288 288 def start(self):
289 289 ip = self.ip if self.ip else '[all ip addresses on your system]'
290 290 proto = 'https' if self.certfile else 'http'
291 291 self.log.info("The IPython Notebook is running at: %s://%s:%i" % (proto,
292 292 ip,
293 293 self.port))
294 294 ioloop.IOLoop.instance().start()
295 295
296 296 #-----------------------------------------------------------------------------
297 297 # Main entry point
298 298 #-----------------------------------------------------------------------------
299 299
300 300 def launch_new_instance():
301 301 app = IPythonNotebookApp()
302 302 app.initialize()
303 303 app.start()
304 304
@@ -1,595 +1,595
1 1 """ A minimal application using the Qt console-style IPython frontend.
2 2
3 3 This is not a complete console app, as subprocess will not be able to receive
4 4 input, there is no real readline support, among other limitations.
5 5
6 6 Authors:
7 7
8 8 * Evan Patterson
9 9 * Min RK
10 10 * Erik Tollerud
11 11 * Fernando Perez
12 12
13 13 """
14 14
15 15 #-----------------------------------------------------------------------------
16 16 # Imports
17 17 #-----------------------------------------------------------------------------
18 18
19 19 # stdlib imports
20 20 import json
21 21 import os
22 22 import signal
23 23 import sys
24 24
25 25 # System library imports
26 26 from IPython.external.qt import QtGui
27 27 from pygments.styles import get_all_styles
28 28
29 29 # Local imports
30 30 from IPython.config.application import boolean_flag
31 31 from IPython.core.application import BaseIPythonApplication
32 32 from IPython.core.profiledir import ProfileDir
33 33 from IPython.lib.kernel import tunnel_to_kernel, find_connection_file
34 34 from IPython.frontend.qt.console.frontend_widget import FrontendWidget
35 35 from IPython.frontend.qt.console.ipython_widget import IPythonWidget
36 36 from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
37 37 from IPython.frontend.qt.console import styles
38 38 from IPython.frontend.qt.kernelmanager import QtKernelManager
39 39 from IPython.utils.path import filefind
40 40 from IPython.utils.py3compat import str_to_bytes
41 41 from IPython.utils.traitlets import (
42 42 Dict, List, Unicode, Int, CaselessStrEnum, CBool, Any
43 43 )
44 44 from IPython.zmq.ipkernel import (
45 45 flags as ipkernel_flags,
46 46 aliases as ipkernel_aliases,
47 47 IPKernelApp
48 48 )
49 49 from IPython.zmq.session import Session, default_secure
50 50 from IPython.zmq.zmqshell import ZMQInteractiveShell
51 51
52 52
53 53 #-----------------------------------------------------------------------------
54 54 # Network Constants
55 55 #-----------------------------------------------------------------------------
56 56
57 57 from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS
58 58
59 59 #-----------------------------------------------------------------------------
60 60 # Globals
61 61 #-----------------------------------------------------------------------------
62 62
63 63 _examples = """
64 64 ipython qtconsole # start the qtconsole
65 65 ipython qtconsole --pylab=inline # start with pylab in inline plotting mode
66 66 """
67 67
68 68 #-----------------------------------------------------------------------------
69 69 # Classes
70 70 #-----------------------------------------------------------------------------
71 71
72 72 class MainWindow(QtGui.QMainWindow):
73 73
74 74 #---------------------------------------------------------------------------
75 75 # 'object' interface
76 76 #---------------------------------------------------------------------------
77 77
78 78 def __init__(self, app, frontend, existing=False, may_close=True,
79 79 confirm_exit=True):
80 80 """ Create a MainWindow for the specified FrontendWidget.
81 81
82 82 The app is passed as an argument to allow for different
83 83 closing behavior depending on whether we are the Kernel's parent.
84 84
85 85 If existing is True, then this Console does not own the Kernel.
86 86
87 87 If may_close is True, then this Console is permitted to close the kernel
88 88 """
89 89 super(MainWindow, self).__init__()
90 90 self._app = app
91 91 self._frontend = frontend
92 92 self._existing = existing
93 93 if existing:
94 94 self._may_close = may_close
95 95 else:
96 96 self._may_close = True
97 97 self._frontend.exit_requested.connect(self.close)
98 98 self._confirm_exit = confirm_exit
99 99 self.setCentralWidget(frontend)
100 100
101 101 #---------------------------------------------------------------------------
102 102 # QWidget interface
103 103 #---------------------------------------------------------------------------
104 104
105 105 def closeEvent(self, event):
106 106 """ Close the window and the kernel (if necessary).
107 107
108 108 This will prompt the user if they are finished with the kernel, and if
109 109 so, closes the kernel cleanly. Alternatively, if the exit magic is used,
110 110 it closes without prompt.
111 111 """
112 112 keepkernel = None #Use the prompt by default
113 113 if hasattr(self._frontend,'_keep_kernel_on_exit'): #set by exit magic
114 114 keepkernel = self._frontend._keep_kernel_on_exit
115 115
116 116 kernel_manager = self._frontend.kernel_manager
117 117
118 118 if keepkernel is None and not self._confirm_exit:
119 119 # don't prompt, just terminate the kernel if we own it
120 120 # or leave it alone if we don't
121 121 keepkernel = not self._existing
122 122
123 123 if keepkernel is None: #show prompt
124 124 if kernel_manager and kernel_manager.channels_running:
125 125 title = self.window().windowTitle()
126 126 cancel = QtGui.QMessageBox.Cancel
127 127 okay = QtGui.QMessageBox.Ok
128 128 if self._may_close:
129 129 msg = "You are closing this Console window."
130 130 info = "Would you like to quit the Kernel and all attached Consoles as well?"
131 131 justthis = QtGui.QPushButton("&No, just this Console", self)
132 132 justthis.setShortcut('N')
133 133 closeall = QtGui.QPushButton("&Yes, quit everything", self)
134 134 closeall.setShortcut('Y')
135 135 box = QtGui.QMessageBox(QtGui.QMessageBox.Question,
136 136 title, msg)
137 137 box.setInformativeText(info)
138 138 box.addButton(cancel)
139 139 box.addButton(justthis, QtGui.QMessageBox.NoRole)
140 140 box.addButton(closeall, QtGui.QMessageBox.YesRole)
141 141 box.setDefaultButton(closeall)
142 142 box.setEscapeButton(cancel)
143 143 reply = box.exec_()
144 144 if reply == 1: # close All
145 145 kernel_manager.shutdown_kernel()
146 146 #kernel_manager.stop_channels()
147 147 event.accept()
148 148 elif reply == 0: # close Console
149 149 if not self._existing:
150 150 # Have kernel: don't quit, just close the window
151 151 self._app.setQuitOnLastWindowClosed(False)
152 152 self.deleteLater()
153 153 event.accept()
154 154 else:
155 155 event.ignore()
156 156 else:
157 157 reply = QtGui.QMessageBox.question(self, title,
158 158 "Are you sure you want to close this Console?"+
159 159 "\nThe Kernel and other Consoles will remain active.",
160 160 okay|cancel,
161 161 defaultButton=okay
162 162 )
163 163 if reply == okay:
164 164 event.accept()
165 165 else:
166 166 event.ignore()
167 167 elif keepkernel: #close console but leave kernel running (no prompt)
168 168 if kernel_manager and kernel_manager.channels_running:
169 169 if not self._existing:
170 170 # I have the kernel: don't quit, just close the window
171 171 self._app.setQuitOnLastWindowClosed(False)
172 172 event.accept()
173 173 else: #close console and kernel (no prompt)
174 174 if kernel_manager and kernel_manager.channels_running:
175 175 kernel_manager.shutdown_kernel()
176 176 event.accept()
177 177
178 178 #-----------------------------------------------------------------------------
179 179 # Aliases and Flags
180 180 #-----------------------------------------------------------------------------
181 181
182 182 flags = dict(ipkernel_flags)
183 183 qt_flags = {
184 184 'existing' : ({'IPythonQtConsoleApp' : {'existing' : 'kernel*.json'}},
185 185 "Connect to an existing kernel. If no argument specified, guess most recent"),
186 186 'pure' : ({'IPythonQtConsoleApp' : {'pure' : True}},
187 187 "Use a pure Python kernel instead of an IPython kernel."),
188 188 'plain' : ({'ConsoleWidget' : {'kind' : 'plain'}},
189 189 "Disable rich text support."),
190 190 }
191 191 qt_flags.update(boolean_flag(
192 192 'gui-completion', 'ConsoleWidget.gui_completion',
193 193 "use a GUI widget for tab completion",
194 194 "use plaintext output for completion"
195 195 ))
196 196 qt_flags.update(boolean_flag(
197 197 'confirm-exit', 'IPythonQtConsoleApp.confirm_exit',
198 198 """Set to display confirmation dialog on exit. You can always use 'exit' or 'quit',
199 199 to force a direct exit without any confirmation.
200 200 """,
201 201 """Don't prompt the user when exiting. This will terminate the kernel
202 202 if it is owned by the frontend, and leave it alive if it is external.
203 203 """
204 204 ))
205 205 flags.update(qt_flags)
206 206
207 207 aliases = dict(ipkernel_aliases)
208 208
209 209 qt_aliases = dict(
210 210 hb = 'IPythonQtConsoleApp.hb_port',
211 211 shell = 'IPythonQtConsoleApp.shell_port',
212 212 iopub = 'IPythonQtConsoleApp.iopub_port',
213 213 stdin = 'IPythonQtConsoleApp.stdin_port',
214 214 ip = 'IPythonQtConsoleApp.ip',
215 215 existing = 'IPythonQtConsoleApp.existing',
216 216 f = 'IPythonQtConsoleApp.connection_file',
217 217
218 218 style = 'IPythonWidget.syntax_style',
219 219 stylesheet = 'IPythonQtConsoleApp.stylesheet',
220 220 colors = 'ZMQInteractiveShell.colors',
221 221
222 222 editor = 'IPythonWidget.editor',
223 223 paging = 'ConsoleWidget.paging',
224 224 ssh = 'IPythonQtConsoleApp.sshserver',
225 225 )
226 226 aliases.update(qt_aliases)
227 227
228 228
229 229 #-----------------------------------------------------------------------------
230 230 # IPythonQtConsole
231 231 #-----------------------------------------------------------------------------
232 232
233 233
234 234 class IPythonQtConsoleApp(BaseIPythonApplication):
235 235 name = 'ipython-qtconsole'
236 236 default_config_file_name='ipython_config.py'
237 237
238 238 description = """
239 239 The IPython QtConsole.
240 240
241 241 This launches a Console-style application using Qt. It is not a full
242 242 console, in that launched terminal subprocesses will not be able to accept
243 243 input.
244 244
245 245 The QtConsole supports various extra features beyond the Terminal IPython
246 246 shell, such as inline plotting with matplotlib, via:
247 247
248 248 ipython qtconsole --pylab=inline
249 249
250 250 as well as saving your session as HTML, and printing the output.
251 251
252 252 """
253 253 examples = _examples
254 254
255 255 classes = [IPKernelApp, IPythonWidget, ZMQInteractiveShell, ProfileDir, Session]
256 256 flags = Dict(flags)
257 257 aliases = Dict(aliases)
258 258
259 259 kernel_argv = List(Unicode)
260 260
261 261 # create requested profiles by default, if they don't exist:
262 262 auto_create = CBool(True)
263 263 # connection info:
264 264 ip = Unicode(LOCALHOST, config=True,
265 265 help="""Set the kernel\'s IP address [default localhost].
266 266 If the IP address is something other than localhost, then
267 267 Consoles on other machines will be able to connect
268 268 to the Kernel, so be careful!"""
269 269 )
270 270
271 271 sshserver = Unicode('', config=True,
272 272 help="""The SSH server to use to connect to the kernel.""")
273 273 sshkey = Unicode('', config=True,
274 274 help="""Path to the ssh key to use for logging in to the ssh server.""")
275 275
276 276 hb_port = Int(0, config=True,
277 277 help="set the heartbeat port [default: random]")
278 278 shell_port = Int(0, config=True,
279 279 help="set the shell (XREP) port [default: random]")
280 280 iopub_port = Int(0, config=True,
281 281 help="set the iopub (PUB) port [default: random]")
282 282 stdin_port = Int(0, config=True,
283 283 help="set the stdin (XREQ) port [default: random]")
284 284 connection_file = Unicode('', config=True,
285 285 help="""JSON file in which to store connection info [default: kernel-<pid>.json]
286 286
287 287 This file will contain the IP, ports, and authentication key needed to connect
288 288 clients to this kernel. By default, this file will be created in the security-dir
289 289 of the current profile, but can be specified by absolute path.
290 290 """)
291 291 def _connection_file_default(self):
292 292 return 'kernel-%i.json' % os.getpid()
293 293
294 294 existing = Unicode('', config=True,
295 295 help="""Connect to an already running kernel""")
296 296
297 297 stylesheet = Unicode('', config=True,
298 298 help="path to a custom CSS stylesheet")
299 299
300 300 pure = CBool(False, config=True,
301 301 help="Use a pure Python kernel instead of an IPython kernel.")
302 302 plain = CBool(False, config=True,
303 303 help="Use a plaintext widget instead of rich text (plain can't print/save).")
304 304
305 305 def _pure_changed(self, name, old, new):
306 306 kind = 'plain' if self.plain else 'rich'
307 307 self.config.ConsoleWidget.kind = kind
308 308 if self.pure:
309 309 self.widget_factory = FrontendWidget
310 310 elif self.plain:
311 311 self.widget_factory = IPythonWidget
312 312 else:
313 313 self.widget_factory = RichIPythonWidget
314 314
315 315 _plain_changed = _pure_changed
316 316
317 317 confirm_exit = CBool(True, config=True,
318 318 help="""
319 319 Set to display confirmation dialog on exit. You can always use 'exit' or 'quit',
320 320 to force a direct exit without any confirmation.""",
321 321 )
322 322
323 323 # the factory for creating a widget
324 324 widget_factory = Any(RichIPythonWidget)
325 325
326 326 def parse_command_line(self, argv=None):
327 327 super(IPythonQtConsoleApp, self).parse_command_line(argv)
328 328 if argv is None:
329 329 argv = sys.argv[1:]
330 330
331 331 self.kernel_argv = list(argv) # copy
332 332 # kernel should inherit default config file from frontend
333 333 self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name)
334 334 # Scrub frontend-specific flags
335 335 for a in argv:
336 336 if a.startswith('-') and a.lstrip('-') in qt_flags:
337 337 self.kernel_argv.remove(a)
338 338 swallow_next = False
339 339 for a in argv:
340 340 if swallow_next:
341 341 self.kernel_argv.remove(a)
342 342 swallow_next = False
343 343 continue
344 344 if a.startswith('-'):
345 split = a.lstrip('-').split('=')[0]
345 split = a.lstrip('-').split('=')
346 346 alias = split[0]
347 347 if alias in qt_aliases:
348 348 self.kernel_argv.remove(a)
349 349 if len(split) == 1:
350 350 # alias passed with arg via space
351 351 swallow_next = True
352 352
353 353 def init_connection_file(self):
354 354 """find the connection file, and load the info if found.
355 355
356 356 The current working directory and the current profile's security
357 357 directory will be searched for the file if it is not given by
358 358 absolute path.
359 359
360 360 When attempting to connect to an existing kernel and the `--existing`
361 361 argument does not match an existing file, it will be interpreted as a
362 362 fileglob, and the matching file in the current profile's security dir
363 363 with the latest access time will be used.
364 364 """
365 365 if self.existing:
366 366 try:
367 367 cf = find_connection_file(self.existing)
368 368 except Exception:
369 369 self.log.critical("Could not find existing kernel connection file %s", self.existing)
370 370 self.exit(1)
371 371 self.log.info("Connecting to existing kernel: %s" % cf)
372 372 self.connection_file = cf
373 373 # should load_connection_file only be used for existing?
374 374 # as it is now, this allows reusing ports if an existing
375 375 # file is requested
376 376 try:
377 377 self.load_connection_file()
378 378 except Exception:
379 379 self.log.error("Failed to load connection file: %r", self.connection_file, exc_info=True)
380 380 self.exit(1)
381 381
382 382 def load_connection_file(self):
383 383 """load ip/port/hmac config from JSON connection file"""
384 384 # this is identical to KernelApp.load_connection_file
385 385 # perhaps it can be centralized somewhere?
386 386 try:
387 387 fname = filefind(self.connection_file, ['.', self.profile_dir.security_dir])
388 388 except IOError:
389 389 self.log.debug("Connection File not found: %s", self.connection_file)
390 390 return
391 391 self.log.debug(u"Loading connection file %s", fname)
392 392 with open(fname) as f:
393 393 s = f.read()
394 394 cfg = json.loads(s)
395 395 if self.ip == LOCALHOST and 'ip' in cfg:
396 396 # not overridden by config or cl_args
397 397 self.ip = cfg['ip']
398 398 for channel in ('hb', 'shell', 'iopub', 'stdin'):
399 399 name = channel + '_port'
400 400 if getattr(self, name) == 0 and name in cfg:
401 401 # not overridden by config or cl_args
402 402 setattr(self, name, cfg[name])
403 403 if 'key' in cfg:
404 404 self.config.Session.key = str_to_bytes(cfg['key'])
405 405
406 406 def init_ssh(self):
407 407 """set up ssh tunnels, if needed."""
408 408 if not self.sshserver and not self.sshkey:
409 409 return
410 410
411 411 if self.sshkey and not self.sshserver:
412 412 # specifying just the key implies that we are connecting directly
413 413 self.sshserver = self.ip
414 414 self.ip = LOCALHOST
415 415
416 416 # build connection dict for tunnels:
417 417 info = dict(ip=self.ip,
418 418 shell_port=self.shell_port,
419 419 iopub_port=self.iopub_port,
420 420 stdin_port=self.stdin_port,
421 421 hb_port=self.hb_port
422 422 )
423 423
424 424 self.log.info("Forwarding connections to %s via %s"%(self.ip, self.sshserver))
425 425
426 426 # tunnels return a new set of ports, which will be on localhost:
427 427 self.ip = LOCALHOST
428 428 try:
429 429 newports = tunnel_to_kernel(info, self.sshserver, self.sshkey)
430 430 except:
431 431 # even catch KeyboardInterrupt
432 432 self.log.error("Could not setup tunnels", exc_info=True)
433 433 self.exit(1)
434 434
435 435 self.shell_port, self.iopub_port, self.stdin_port, self.hb_port = newports
436 436
437 437 cf = self.connection_file
438 438 base,ext = os.path.splitext(cf)
439 439 base = os.path.basename(base)
440 440 self.connection_file = os.path.basename(base)+'-ssh'+ext
441 441 self.log.critical("To connect another client via this tunnel, use:")
442 442 self.log.critical("--existing %s" % self.connection_file)
443 443
444 444 def init_kernel_manager(self):
445 445 # Don't let Qt or ZMQ swallow KeyboardInterupts.
446 446 signal.signal(signal.SIGINT, signal.SIG_DFL)
447 447 sec = self.profile_dir.security_dir
448 448 try:
449 449 cf = filefind(self.connection_file, ['.', sec])
450 450 except IOError:
451 451 # file might not exist
452 452 if self.connection_file == os.path.basename(self.connection_file):
453 453 # just shortname, put it in security dir
454 454 cf = os.path.join(sec, self.connection_file)
455 455 else:
456 456 cf = self.connection_file
457 457
458 458 # Create a KernelManager and start a kernel.
459 459 self.kernel_manager = QtKernelManager(
460 460 ip=self.ip,
461 461 shell_port=self.shell_port,
462 462 iopub_port=self.iopub_port,
463 463 stdin_port=self.stdin_port,
464 464 hb_port=self.hb_port,
465 465 connection_file=cf,
466 466 config=self.config,
467 467 )
468 468 # start the kernel
469 469 if not self.existing:
470 470 kwargs = dict(ipython=not self.pure)
471 471 kwargs['extra_arguments'] = self.kernel_argv
472 472 self.kernel_manager.start_kernel(**kwargs)
473 473 elif self.sshserver:
474 474 # ssh, write new connection file
475 475 self.kernel_manager.write_connection_file()
476 476 self.kernel_manager.start_channels()
477 477
478 478
479 479 def init_qt_elements(self):
480 480 # Create the widget.
481 481 self.app = QtGui.QApplication([])
482 482 local_kernel = (not self.existing) or self.ip in LOCAL_IPS
483 483 self.widget = self.widget_factory(config=self.config,
484 484 local_kernel=local_kernel)
485 485 self.widget.kernel_manager = self.kernel_manager
486 486 self.window = MainWindow(self.app, self.widget, self.existing,
487 487 may_close=local_kernel,
488 488 confirm_exit=self.confirm_exit)
489 489 self.window.setWindowTitle('Python' if self.pure else 'IPython')
490 490
491 491 def init_colors(self):
492 492 """Configure the coloring of the widget"""
493 493 # Note: This will be dramatically simplified when colors
494 494 # are removed from the backend.
495 495
496 496 if self.pure:
497 497 # only IPythonWidget supports styling
498 498 return
499 499
500 500 # parse the colors arg down to current known labels
501 501 try:
502 502 colors = self.config.ZMQInteractiveShell.colors
503 503 except AttributeError:
504 504 colors = None
505 505 try:
506 506 style = self.config.IPythonWidget.colors
507 507 except AttributeError:
508 508 style = None
509 509
510 510 # find the value for colors:
511 511 if colors:
512 512 colors=colors.lower()
513 513 if colors in ('lightbg', 'light'):
514 514 colors='lightbg'
515 515 elif colors in ('dark', 'linux'):
516 516 colors='linux'
517 517 else:
518 518 colors='nocolor'
519 519 elif style:
520 520 if style=='bw':
521 521 colors='nocolor'
522 522 elif styles.dark_style(style):
523 523 colors='linux'
524 524 else:
525 525 colors='lightbg'
526 526 else:
527 527 colors=None
528 528
529 529 # Configure the style.
530 530 widget = self.widget
531 531 if style:
532 532 widget.style_sheet = styles.sheet_from_template(style, colors)
533 533 widget.syntax_style = style
534 534 widget._syntax_style_changed()
535 535 widget._style_sheet_changed()
536 536 elif colors:
537 537 # use a default style
538 538 widget.set_default_style(colors=colors)
539 539 else:
540 540 # this is redundant for now, but allows the widget's
541 541 # defaults to change
542 542 widget.set_default_style()
543 543
544 544 if self.stylesheet:
545 545 # we got an expicit stylesheet
546 546 if os.path.isfile(self.stylesheet):
547 547 with open(self.stylesheet) as f:
548 548 sheet = f.read()
549 549 widget.style_sheet = sheet
550 550 widget._style_sheet_changed()
551 551 else:
552 552 raise IOError("Stylesheet %r not found."%self.stylesheet)
553 553
554 554 def initialize(self, argv=None):
555 555 super(IPythonQtConsoleApp, self).initialize(argv)
556 556 self.init_connection_file()
557 557 default_secure(self.config)
558 558 self.init_ssh()
559 559 self.init_kernel_manager()
560 560 self.init_qt_elements()
561 561 self.init_colors()
562 562 self.init_window_shortcut()
563 563
564 564 def init_window_shortcut(self):
565 565 fullScreenAction = QtGui.QAction('Toggle Full Screen', self.window)
566 566 fullScreenAction.setShortcut('Ctrl+Meta+Space')
567 567 fullScreenAction.triggered.connect(self.toggleFullScreen)
568 568 self.window.addAction(fullScreenAction)
569 569
570 570 def toggleFullScreen(self):
571 571 if not self.window.isFullScreen():
572 572 self.window.showFullScreen()
573 573 else:
574 574 self.window.showNormal()
575 575
576 576 def start(self):
577 577
578 578 # draw the window
579 579 self.window.show()
580 580
581 581 # Start the application main loop.
582 582 self.app.exec_()
583 583
584 584 #-----------------------------------------------------------------------------
585 585 # Main entry point
586 586 #-----------------------------------------------------------------------------
587 587
588 588 def main():
589 589 app = IPythonQtConsoleApp()
590 590 app.initialize()
591 591 app.start()
592 592
593 593
594 594 if __name__ == '__main__':
595 595 main()
General Comments 0
You need to be logged in to leave comments. Login now