##// END OF EJS Templates
rename IPythonMixinConsoleApp to IPythonConsoleApp...
MinRK -
Show More
@@ -1,381 +1,381 b''
1 1 """ A minimal application base mixin for all ZMQ based IPython frontends.
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. This is a
5 5 refactoring of what used to be the IPython/frontend/qt/console/qtconsoleapp.py
6 6
7 7 Authors:
8 8
9 9 * Evan Patterson
10 10 * Min RK
11 11 * Erik Tollerud
12 12 * Fernando Perez
13 13 * Bussonnier Matthias
14 14 * Thomas Kluyver
15 15 * Paul Ivanov
16 16
17 17 """
18 18
19 19 #-----------------------------------------------------------------------------
20 20 # Imports
21 21 #-----------------------------------------------------------------------------
22 22
23 23 # stdlib imports
24 24 import atexit
25 25 import json
26 26 import os
27 27 import signal
28 28 import sys
29 29 import uuid
30 30
31 31
32 32 # Local imports
33 33 from IPython.config.application import boolean_flag
34 34 from IPython.config.configurable import Configurable
35 35 from IPython.core.profiledir import ProfileDir
36 36 from IPython.lib.kernel import tunnel_to_kernel, find_connection_file
37 37 from IPython.zmq.blockingkernelmanager import BlockingKernelManager
38 38 from IPython.utils.path import filefind
39 39 from IPython.utils.py3compat import str_to_bytes
40 40 from IPython.utils.traitlets import (
41 41 Dict, List, Unicode, CUnicode, Int, CBool, Any
42 42 )
43 43 from IPython.zmq.ipkernel import (
44 44 flags as ipkernel_flags,
45 45 aliases as ipkernel_aliases,
46 46 IPKernelApp
47 47 )
48 48 from IPython.zmq.session import Session, default_secure
49 49 from IPython.zmq.zmqshell import ZMQInteractiveShell
50 50
51 51 #-----------------------------------------------------------------------------
52 52 # Network Constants
53 53 #-----------------------------------------------------------------------------
54 54
55 55 from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS
56 56
57 57 #-----------------------------------------------------------------------------
58 58 # Globals
59 59 #-----------------------------------------------------------------------------
60 60
61 61
62 62 #-----------------------------------------------------------------------------
63 63 # Aliases and Flags
64 64 #-----------------------------------------------------------------------------
65 65
66 66 flags = dict(ipkernel_flags)
67 67
68 68 # the flags that are specific to the frontend
69 69 # these must be scrubbed before being passed to the kernel,
70 70 # or it will raise an error on unrecognized flags
71 71 app_flags = {
72 'existing' : ({'IPythonMixinConsoleApp' : {'existing' : 'kernel*.json'}},
72 'existing' : ({'IPythonConsoleApp' : {'existing' : 'kernel*.json'}},
73 73 "Connect to an existing kernel. If no argument specified, guess most recent"),
74 74 }
75 75 app_flags.update(boolean_flag(
76 'confirm-exit', 'IPythonMixinConsoleApp.confirm_exit',
76 'confirm-exit', 'IPythonConsoleApp.confirm_exit',
77 77 """Set to display confirmation dialog on exit. You can always use 'exit' or 'quit',
78 78 to force a direct exit without any confirmation.
79 79 """,
80 80 """Don't prompt the user when exiting. This will terminate the kernel
81 81 if it is owned by the frontend, and leave it alive if it is external.
82 82 """
83 83 ))
84 84 flags.update(app_flags)
85 85
86 86 aliases = dict(ipkernel_aliases)
87 87
88 88 # also scrub aliases from the frontend
89 89 app_aliases = dict(
90 hb = 'IPythonMixinConsoleApp.hb_port',
91 shell = 'IPythonMixinConsoleApp.shell_port',
92 iopub = 'IPythonMixinConsoleApp.iopub_port',
93 stdin = 'IPythonMixinConsoleApp.stdin_port',
94 ip = 'IPythonMixinConsoleApp.ip',
95 existing = 'IPythonMixinConsoleApp.existing',
96 f = 'IPythonMixinConsoleApp.connection_file',
90 hb = 'IPythonConsoleApp.hb_port',
91 shell = 'IPythonConsoleApp.shell_port',
92 iopub = 'IPythonConsoleApp.iopub_port',
93 stdin = 'IPythonConsoleApp.stdin_port',
94 ip = 'IPythonConsoleApp.ip',
95 existing = 'IPythonConsoleApp.existing',
96 f = 'IPythonConsoleApp.connection_file',
97 97
98 98
99 ssh = 'IPythonMixinConsoleApp.sshserver',
99 ssh = 'IPythonConsoleApp.sshserver',
100 100 )
101 101 aliases.update(app_aliases)
102 102
103 103 #-----------------------------------------------------------------------------
104 104 # Classes
105 105 #-----------------------------------------------------------------------------
106 106
107 107 #-----------------------------------------------------------------------------
108 # IPythonMixinConsole
108 # IPythonConsole
109 109 #-----------------------------------------------------------------------------
110 110
111 111
112 class IPythonMixinConsoleApp(Configurable):
112 class IPythonConsoleApp(Configurable):
113 113 name = 'ipython-console-mixin'
114 114 default_config_file_name='ipython_config.py'
115 115
116 116 description = """
117 117 The IPython Mixin Console.
118 118
119 119 This class contains the common portions of console client (QtConsole,
120 120 ZMQ-based terminal console, etc). It is not a full console, in that
121 121 launched terminal subprocesses will not be able to accept input.
122 122
123 123 The Console using this mixing supports various extra features beyond
124 124 the single-process Terminal IPython shell, such as connecting to
125 125 existing kernel, via:
126 126
127 127 ipython <appname> --existing
128 128
129 129 as well as tunnel via SSH
130 130
131 131 """
132 132
133 133 classes = [IPKernelApp, ZMQInteractiveShell, ProfileDir, Session]
134 134 flags = Dict(flags)
135 135 aliases = Dict(aliases)
136 136 kernel_manager_class = BlockingKernelManager
137 137
138 138 kernel_argv = List(Unicode)
139 139
140 140 pure = CBool(False, config=True,
141 141 help="Use a pure Python kernel instead of an IPython kernel.")
142 142 # create requested profiles by default, if they don't exist:
143 143 auto_create = CBool(True)
144 144 # connection info:
145 145 ip = Unicode(LOCALHOST, config=True,
146 146 help="""Set the kernel\'s IP address [default localhost].
147 147 If the IP address is something other than localhost, then
148 148 Consoles on other machines will be able to connect
149 149 to the Kernel, so be careful!"""
150 150 )
151 151
152 152 sshserver = Unicode('', config=True,
153 153 help="""The SSH server to use to connect to the kernel.""")
154 154 sshkey = Unicode('', config=True,
155 155 help="""Path to the ssh key to use for logging in to the ssh server.""")
156 156
157 157 hb_port = Int(0, config=True,
158 158 help="set the heartbeat port [default: random]")
159 159 shell_port = Int(0, config=True,
160 160 help="set the shell (XREP) port [default: random]")
161 161 iopub_port = Int(0, config=True,
162 162 help="set the iopub (PUB) port [default: random]")
163 163 stdin_port = Int(0, config=True,
164 164 help="set the stdin (XREQ) port [default: random]")
165 165 connection_file = Unicode('', config=True,
166 166 help="""JSON file in which to store connection info [default: kernel-<pid>.json]
167 167
168 168 This file will contain the IP, ports, and authentication key needed to connect
169 169 clients to this kernel. By default, this file will be created in the security-dir
170 170 of the current profile, but can be specified by absolute path.
171 171 """)
172 172 def _connection_file_default(self):
173 173 return 'kernel-%i.json' % os.getpid()
174 174
175 175 existing = CUnicode('', config=True,
176 176 help="""Connect to an already running kernel""")
177 177
178 178 confirm_exit = CBool(True, config=True,
179 179 help="""
180 180 Set to display confirmation dialog on exit. You can always use 'exit' or 'quit',
181 181 to force a direct exit without any confirmation.""",
182 182 )
183 183
184 184
185 185 def parse_command_line(self, argv=None):
186 186 #super(PythonBaseConsoleApp, self).parse_command_line(argv)
187 187 # make this stuff after this a function, in case the super stuff goes
188 188 # away. Also, Min notes that this functionality should be moved to a
189 189 # generic library of kernel stuff
190 190 self.swallow_args(app_aliases,app_flags,argv=argv)
191 191
192 192 def swallow_args(self, aliases,flags, argv=None):
193 193 if argv is None:
194 194 argv = sys.argv[1:]
195 195 self.kernel_argv = list(argv) # copy
196 196 # kernel should inherit default config file from frontend
197 197 self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name)
198 198 # Scrub frontend-specific flags
199 199 swallow_next = False
200 200 was_flag = False
201 201 for a in argv:
202 202 if swallow_next:
203 203 swallow_next = False
204 204 # last arg was an alias, remove the next one
205 205 # *unless* the last alias has a no-arg flag version, in which
206 206 # case, don't swallow the next arg if it's also a flag:
207 207 if not (was_flag and a.startswith('-')):
208 208 self.kernel_argv.remove(a)
209 209 continue
210 210 if a.startswith('-'):
211 211 split = a.lstrip('-').split('=')
212 212 alias = split[0]
213 213 if alias in aliases:
214 214 self.kernel_argv.remove(a)
215 215 if len(split) == 1:
216 216 # alias passed with arg via space
217 217 swallow_next = True
218 218 # could have been a flag that matches an alias, e.g. `existing`
219 219 # in which case, we might not swallow the next arg
220 220 was_flag = alias in flags
221 221 elif alias in flags:
222 222 # strip flag, but don't swallow next, as flags don't take args
223 223 self.kernel_argv.remove(a)
224 224
225 225 def init_connection_file(self):
226 226 """find the connection file, and load the info if found.
227 227
228 228 The current working directory and the current profile's security
229 229 directory will be searched for the file if it is not given by
230 230 absolute path.
231 231
232 232 When attempting to connect to an existing kernel and the `--existing`
233 233 argument does not match an existing file, it will be interpreted as a
234 234 fileglob, and the matching file in the current profile's security dir
235 235 with the latest access time will be used.
236 236
237 237 After this method is called, self.connection_file contains the *full path*
238 238 to the connection file, never just its name.
239 239 """
240 240 if self.existing:
241 241 try:
242 242 cf = find_connection_file(self.existing)
243 243 except Exception:
244 244 self.log.critical("Could not find existing kernel connection file %s", self.existing)
245 245 self.exit(1)
246 246 self.log.info("Connecting to existing kernel: %s" % cf)
247 247 self.connection_file = cf
248 248 else:
249 249 # not existing, check if we are going to write the file
250 250 # and ensure that self.connection_file is a full path, not just the shortname
251 251 try:
252 252 cf = find_connection_file(self.connection_file)
253 253 except Exception:
254 254 # file might not exist
255 255 if self.connection_file == os.path.basename(self.connection_file):
256 256 # just shortname, put it in security dir
257 257 cf = os.path.join(self.profile_dir.security_dir, self.connection_file)
258 258 else:
259 259 cf = self.connection_file
260 260 self.connection_file = cf
261 261
262 262 # should load_connection_file only be used for existing?
263 263 # as it is now, this allows reusing ports if an existing
264 264 # file is requested
265 265 try:
266 266 self.load_connection_file()
267 267 except Exception:
268 268 self.log.error("Failed to load connection file: %r", self.connection_file, exc_info=True)
269 269 self.exit(1)
270 270
271 271 def load_connection_file(self):
272 272 """load ip/port/hmac config from JSON connection file"""
273 273 # this is identical to KernelApp.load_connection_file
274 274 # perhaps it can be centralized somewhere?
275 275 try:
276 276 fname = filefind(self.connection_file, ['.', self.profile_dir.security_dir])
277 277 except IOError:
278 278 self.log.debug("Connection File not found: %s", self.connection_file)
279 279 return
280 280 self.log.debug(u"Loading connection file %s", fname)
281 281 with open(fname) as f:
282 282 s = f.read()
283 283 cfg = json.loads(s)
284 284 if self.ip == LOCALHOST and 'ip' in cfg:
285 285 # not overridden by config or cl_args
286 286 self.ip = cfg['ip']
287 287 for channel in ('hb', 'shell', 'iopub', 'stdin'):
288 288 name = channel + '_port'
289 289 if getattr(self, name) == 0 and name in cfg:
290 290 # not overridden by config or cl_args
291 291 setattr(self, name, cfg[name])
292 292 if 'key' in cfg:
293 293 self.config.Session.key = str_to_bytes(cfg['key'])
294 294
295 295 def init_ssh(self):
296 296 """set up ssh tunnels, if needed."""
297 297 if not self.sshserver and not self.sshkey:
298 298 return
299 299
300 300 if self.sshkey and not self.sshserver:
301 301 # specifying just the key implies that we are connecting directly
302 302 self.sshserver = self.ip
303 303 self.ip = LOCALHOST
304 304
305 305 # build connection dict for tunnels:
306 306 info = dict(ip=self.ip,
307 307 shell_port=self.shell_port,
308 308 iopub_port=self.iopub_port,
309 309 stdin_port=self.stdin_port,
310 310 hb_port=self.hb_port
311 311 )
312 312
313 313 self.log.info("Forwarding connections to %s via %s"%(self.ip, self.sshserver))
314 314
315 315 # tunnels return a new set of ports, which will be on localhost:
316 316 self.ip = LOCALHOST
317 317 try:
318 318 newports = tunnel_to_kernel(info, self.sshserver, self.sshkey)
319 319 except:
320 320 # even catch KeyboardInterrupt
321 321 self.log.error("Could not setup tunnels", exc_info=True)
322 322 self.exit(1)
323 323
324 324 self.shell_port, self.iopub_port, self.stdin_port, self.hb_port = newports
325 325
326 326 cf = self.connection_file
327 327 base,ext = os.path.splitext(cf)
328 328 base = os.path.basename(base)
329 329 self.connection_file = os.path.basename(base)+'-ssh'+ext
330 330 self.log.critical("To connect another client via this tunnel, use:")
331 331 self.log.critical("--existing %s" % self.connection_file)
332 332
333 333 def _new_connection_file(self):
334 334 cf = ''
335 335 while not cf:
336 336 # we don't need a 128b id to distinguish kernels, use more readable
337 337 # 48b node segment (12 hex chars). Users running more than 32k simultaneous
338 338 # kernels can subclass.
339 339 ident = str(uuid.uuid4()).split('-')[-1]
340 340 cf = os.path.join(self.profile_dir.security_dir, 'kernel-%s.json' % ident)
341 341 # only keep if it's actually new. Protect against unlikely collision
342 342 # in 48b random search space
343 343 cf = cf if not os.path.exists(cf) else ''
344 344 return cf
345 345
346 346 def init_kernel_manager(self):
347 347 # Don't let Qt or ZMQ swallow KeyboardInterupts.
348 348 signal.signal(signal.SIGINT, signal.SIG_DFL)
349 349
350 350 # Create a KernelManager and start a kernel.
351 351 self.kernel_manager = self.kernel_manager_class(
352 352 ip=self.ip,
353 353 shell_port=self.shell_port,
354 354 iopub_port=self.iopub_port,
355 355 stdin_port=self.stdin_port,
356 356 hb_port=self.hb_port,
357 357 connection_file=self.connection_file,
358 358 config=self.config,
359 359 )
360 360 # start the kernel
361 361 if not self.existing:
362 362 kwargs = dict(ipython=not self.pure)
363 363 kwargs['extra_arguments'] = self.kernel_argv
364 364 self.kernel_manager.start_kernel(**kwargs)
365 365 elif self.sshserver:
366 366 # ssh, write new connection file
367 367 self.kernel_manager.write_connection_file()
368 368 atexit.register(self.kernel_manager.cleanup_connection_file)
369 369 self.kernel_manager.start_channels()
370 370
371 371
372 372 def initialize(self, argv=None):
373 373 """
374 374 Classes which mix this class in should call:
375 IPythonMixinConsoleApp.initialize(self,argv)
375 IPythonConsoleApp.initialize(self,argv)
376 376 """
377 377 self.init_connection_file()
378 378 default_secure(self.config)
379 379 self.init_ssh()
380 380 self.init_kernel_manager()
381 381
@@ -1,350 +1,350 b''
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 * Bussonnier Matthias
13 13 * Thomas Kluyver
14 14 * Paul Ivanov
15 15
16 16 """
17 17
18 18 #-----------------------------------------------------------------------------
19 19 # Imports
20 20 #-----------------------------------------------------------------------------
21 21
22 22 # stdlib imports
23 23 import json
24 24 import os
25 25 import signal
26 26 import sys
27 27 import uuid
28 28
29 29 # System library imports
30 30 from IPython.external.qt import QtCore, QtGui
31 31
32 32 # Local imports
33 33 from IPython.config.application import boolean_flag, catch_config_error
34 34 from IPython.core.application import BaseIPythonApplication
35 35 from IPython.core.profiledir import ProfileDir
36 36 from IPython.lib.kernel import tunnel_to_kernel, find_connection_file
37 37 from IPython.frontend.qt.console.frontend_widget import FrontendWidget
38 38 from IPython.frontend.qt.console.ipython_widget import IPythonWidget
39 39 from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
40 40 from IPython.frontend.qt.console import styles
41 41 from IPython.frontend.qt.console.mainwindow import MainWindow
42 42 from IPython.frontend.qt.kernelmanager import QtKernelManager
43 43 from IPython.utils.path import filefind
44 44 from IPython.utils.py3compat import str_to_bytes
45 45 from IPython.utils.traitlets import (
46 46 Dict, List, Unicode, Integer, CaselessStrEnum, CBool, Any
47 47 )
48 48 from IPython.zmq.ipkernel import IPKernelApp
49 49 from IPython.zmq.session import Session, default_secure
50 50 from IPython.zmq.zmqshell import ZMQInteractiveShell
51 51
52 from IPython.frontend.kernelmixinapp import (
53 IPythonMixinConsoleApp, app_aliases, app_flags, flags, aliases
52 from IPython.frontend.consoleapp import (
53 IPythonConsoleApp, app_aliases, app_flags, flags, aliases
54 54 )
55 55
56 56 #-----------------------------------------------------------------------------
57 57 # Network Constants
58 58 #-----------------------------------------------------------------------------
59 59
60 60 from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS
61 61
62 62 #-----------------------------------------------------------------------------
63 63 # Globals
64 64 #-----------------------------------------------------------------------------
65 65
66 66 _examples = """
67 67 ipython qtconsole # start the qtconsole
68 68 ipython qtconsole --pylab=inline # start with pylab in inline plotting mode
69 69 """
70 70
71 71 #-----------------------------------------------------------------------------
72 72 # Aliases and Flags
73 73 #-----------------------------------------------------------------------------
74 74
75 75 # start with copy of flags
76 76 flags = dict(flags)
77 77 qt_flags = {
78 78 'pure' : ({'IPythonQtConsoleApp' : {'pure' : True}},
79 79 "Use a pure Python kernel instead of an IPython kernel."),
80 80 'plain' : ({'ConsoleWidget' : {'kind' : 'plain'}},
81 81 "Disable rich text support."),
82 82 }
83 83 qt_flags.update(boolean_flag(
84 84 'gui-completion', 'ConsoleWidget.gui_completion',
85 85 "use a GUI widget for tab completion",
86 86 "use plaintext output for completion"
87 87 ))
88 88 # and app_flags from the Console Mixin
89 89 qt_flags.update(app_flags)
90 90 # add frontend flags to the full set
91 91 flags.update(qt_flags)
92 92
93 93 # start with copy of front&backend aliases list
94 94 aliases = dict(aliases)
95 95 qt_aliases = dict(
96 96
97 97 style = 'IPythonWidget.syntax_style',
98 98 stylesheet = 'IPythonQtConsoleApp.stylesheet',
99 99 colors = 'ZMQInteractiveShell.colors',
100 100
101 101 editor = 'IPythonWidget.editor',
102 102 paging = 'ConsoleWidget.paging',
103 103 )
104 104 # and app_aliases from the Console Mixin
105 105 qt_aliases.update(app_aliases)
106 106 # add frontend aliases to the full set
107 107 aliases.update(qt_aliases)
108 108
109 109 # get flags&aliases into sets, and remove a couple that
110 110 # shouldn't be scrubbed from backend flags:
111 111 qt_aliases = set(qt_aliases.keys())
112 112 qt_aliases.remove('colors')
113 113 qt_flags = set(qt_flags.keys())
114 114
115 115 #-----------------------------------------------------------------------------
116 116 # Classes
117 117 #-----------------------------------------------------------------------------
118 118
119 119 #-----------------------------------------------------------------------------
120 120 # IPythonQtConsole
121 121 #-----------------------------------------------------------------------------
122 122
123 123
124 class IPythonQtConsoleApp(BaseIPythonApplication, IPythonMixinConsoleApp):
124 class IPythonQtConsoleApp(BaseIPythonApplication, IPythonConsoleApp):
125 125 name = 'ipython-qtconsole'
126 126
127 127 description = """
128 128 The IPython QtConsole.
129 129
130 130 This launches a Console-style application using Qt. It is not a full
131 131 console, in that launched terminal subprocesses will not be able to accept
132 132 input.
133 133
134 134 The QtConsole supports various extra features beyond the Terminal IPython
135 135 shell, such as inline plotting with matplotlib, via:
136 136
137 137 ipython qtconsole --pylab=inline
138 138
139 139 as well as saving your session as HTML, and printing the output.
140 140
141 141 """
142 142 examples = _examples
143 143
144 144 classes = [IPKernelApp, IPythonWidget, ZMQInteractiveShell, ProfileDir, Session]
145 145 flags = Dict(flags)
146 146 aliases = Dict(aliases)
147 147 kernel_manager_class = QtKernelManager
148 148
149 149 stylesheet = Unicode('', config=True,
150 150 help="path to a custom CSS stylesheet")
151 151
152 152 plain = CBool(False, config=True,
153 153 help="Use a plaintext widget instead of rich text (plain can't print/save).")
154 154
155 155 def _pure_changed(self, name, old, new):
156 156 kind = 'plain' if self.plain else 'rich'
157 157 self.config.ConsoleWidget.kind = kind
158 158 if self.pure:
159 159 self.widget_factory = FrontendWidget
160 160 elif self.plain:
161 161 self.widget_factory = IPythonWidget
162 162 else:
163 163 self.widget_factory = RichIPythonWidget
164 164
165 165 _plain_changed = _pure_changed
166 166
167 167 # the factory for creating a widget
168 168 widget_factory = Any(RichIPythonWidget)
169 169
170 170 def parse_command_line(self, argv=None):
171 171 super(IPythonQtConsoleApp, self).parse_command_line(argv)
172 172 self.swallow_args(qt_aliases,qt_flags,argv=argv)
173 173
174 174
175 175 def new_frontend_master(self):
176 176 """ Create and return new frontend attached to new kernel, launched on localhost.
177 177 """
178 178 ip = self.ip if self.ip in LOCAL_IPS else LOCALHOST
179 179 kernel_manager = QtKernelManager(
180 180 ip=ip,
181 181 connection_file=self._new_connection_file(),
182 182 config=self.config,
183 183 )
184 184 # start the kernel
185 185 kwargs = dict(ipython=not self.pure)
186 186 kwargs['extra_arguments'] = self.kernel_argv
187 187 kernel_manager.start_kernel(**kwargs)
188 188 kernel_manager.start_channels()
189 189 widget = self.widget_factory(config=self.config,
190 190 local_kernel=True)
191 191 widget.kernel_manager = kernel_manager
192 192 widget._existing = False
193 193 widget._may_close = True
194 194 widget._confirm_exit = self.confirm_exit
195 195 return widget
196 196
197 197 def new_frontend_slave(self, current_widget):
198 198 """Create and return a new frontend attached to an existing kernel.
199 199
200 200 Parameters
201 201 ----------
202 202 current_widget : IPythonWidget
203 203 The IPythonWidget whose kernel this frontend is to share
204 204 """
205 205 kernel_manager = QtKernelManager(
206 206 connection_file=current_widget.kernel_manager.connection_file,
207 207 config = self.config,
208 208 )
209 209 kernel_manager.load_connection_file()
210 210 kernel_manager.start_channels()
211 211 widget = self.widget_factory(config=self.config,
212 212 local_kernel=False)
213 213 widget._existing = True
214 214 widget._may_close = False
215 215 widget._confirm_exit = False
216 216 widget.kernel_manager = kernel_manager
217 217 return widget
218 218
219 219 def init_qt_elements(self):
220 220 # Create the widget.
221 221 self.app = QtGui.QApplication([])
222 222
223 223 base_path = os.path.abspath(os.path.dirname(__file__))
224 224 icon_path = os.path.join(base_path, 'resources', 'icon', 'IPythonConsole.svg')
225 225 self.app.icon = QtGui.QIcon(icon_path)
226 226 QtGui.QApplication.setWindowIcon(self.app.icon)
227 227
228 228 local_kernel = (not self.existing) or self.ip in LOCAL_IPS
229 229 self.widget = self.widget_factory(config=self.config,
230 230 local_kernel=local_kernel)
231 231 self.widget._existing = self.existing
232 232 self.widget._may_close = not self.existing
233 233 self.widget._confirm_exit = self.confirm_exit
234 234
235 235 self.widget.kernel_manager = self.kernel_manager
236 236 self.window = MainWindow(self.app,
237 237 confirm_exit=self.confirm_exit,
238 238 new_frontend_factory=self.new_frontend_master,
239 239 slave_frontend_factory=self.new_frontend_slave,
240 240 )
241 241 self.window.log = self.log
242 242 self.window.add_tab_with_frontend(self.widget)
243 243 self.window.init_menu_bar()
244 244
245 245 self.window.setWindowTitle('Python' if self.pure else 'IPython')
246 246
247 247 def init_colors(self):
248 248 """Configure the coloring of the widget"""
249 249 # Note: This will be dramatically simplified when colors
250 250 # are removed from the backend.
251 251
252 252 if self.pure:
253 253 # only IPythonWidget supports styling
254 254 return
255 255
256 256 # parse the colors arg down to current known labels
257 257 try:
258 258 colors = self.config.ZMQInteractiveShell.colors
259 259 except AttributeError:
260 260 colors = None
261 261 try:
262 262 style = self.config.IPythonWidget.syntax_style
263 263 except AttributeError:
264 264 style = None
265 265
266 266 # find the value for colors:
267 267 if colors:
268 268 colors=colors.lower()
269 269 if colors in ('lightbg', 'light'):
270 270 colors='lightbg'
271 271 elif colors in ('dark', 'linux'):
272 272 colors='linux'
273 273 else:
274 274 colors='nocolor'
275 275 elif style:
276 276 if style=='bw':
277 277 colors='nocolor'
278 278 elif styles.dark_style(style):
279 279 colors='linux'
280 280 else:
281 281 colors='lightbg'
282 282 else:
283 283 colors=None
284 284
285 285 # Configure the style.
286 286 widget = self.widget
287 287 if style:
288 288 widget.style_sheet = styles.sheet_from_template(style, colors)
289 289 widget.syntax_style = style
290 290 widget._syntax_style_changed()
291 291 widget._style_sheet_changed()
292 292 elif colors:
293 293 # use a default style
294 294 widget.set_default_style(colors=colors)
295 295 else:
296 296 # this is redundant for now, but allows the widget's
297 297 # defaults to change
298 298 widget.set_default_style()
299 299
300 300 if self.stylesheet:
301 301 # we got an expicit stylesheet
302 302 if os.path.isfile(self.stylesheet):
303 303 with open(self.stylesheet) as f:
304 304 sheet = f.read()
305 305 widget.style_sheet = sheet
306 306 widget._style_sheet_changed()
307 307 else:
308 308 raise IOError("Stylesheet %r not found."%self.stylesheet)
309 309
310 310 def init_signal(self):
311 311 """allow clean shutdown on sigint"""
312 312 signal.signal(signal.SIGINT, lambda sig, frame: self.exit(-2))
313 313 # need a timer, so that QApplication doesn't block until a real
314 314 # Qt event fires (can require mouse movement)
315 315 # timer trick from http://stackoverflow.com/q/4938723/938949
316 316 timer = QtCore.QTimer()
317 317 # Let the interpreter run each 200 ms:
318 318 timer.timeout.connect(lambda: None)
319 319 timer.start(200)
320 320 # hold onto ref, so the timer doesn't get cleaned up
321 321 self._sigint_timer = timer
322 322
323 323 @catch_config_error
324 324 def initialize(self, argv=None):
325 325 super(IPythonQtConsoleApp, self).initialize(argv)
326 IPythonMixinConsoleApp.initialize(self,argv)
326 IPythonConsoleApp.initialize(self,argv)
327 327 self.init_qt_elements()
328 328 self.init_colors()
329 329 self.init_signal()
330 330
331 331 def start(self):
332 332
333 333 # draw the window
334 334 self.window.show()
335 335
336 336 # Start the application main loop.
337 337 self.app.exec_()
338 338
339 339 #-----------------------------------------------------------------------------
340 340 # Main entry point
341 341 #-----------------------------------------------------------------------------
342 342
343 343 def main():
344 344 app = IPythonQtConsoleApp()
345 345 app.initialize()
346 346 app.start()
347 347
348 348
349 349 if __name__ == '__main__':
350 350 main()
@@ -1,149 +1,149 b''
1 1 """ A minimal application using the ZMQ-based terminal 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 * Min RK
9 9 * Paul Ivanov
10 10
11 11 """
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16 import signal
17 17 import sys
18 18 import time
19 19
20 20 from IPython.frontend.terminal.ipapp import TerminalIPythonApp, frontend_flags as term_flags
21 21
22 22 from IPython.utils.traitlets import (
23 23 Dict, List, Unicode, Int, CaselessStrEnum, CBool, Any
24 24 )
25 25 from IPython.utils.warn import warn,error
26 26
27 27 from IPython.zmq.ipkernel import IPKernelApp
28 28 from IPython.zmq.session import Session, default_secure
29 29 from IPython.zmq.zmqshell import ZMQInteractiveShell
30 from IPython.frontend.kernelmixinapp import (
31 IPythonMixinConsoleApp, app_aliases, app_flags, aliases, app_aliases, flags
30 from IPython.frontend.consoleapp import (
31 IPythonConsoleApp, app_aliases, app_flags, aliases, app_aliases, flags
32 32 )
33 33
34 34 from IPython.frontend.terminal.console.interactiveshell import ZMQTerminalInteractiveShell
35 35
36 36 #-----------------------------------------------------------------------------
37 37 # Globals
38 38 #-----------------------------------------------------------------------------
39 39
40 40 _examples = """
41 41 ipython console # start the ZMQ-based console
42 42 ipython console --existing # connect to an existing ipython session
43 43 """
44 44
45 45 #-----------------------------------------------------------------------------
46 46 # Flags and Aliases
47 47 #-----------------------------------------------------------------------------
48 48
49 49 # copy flags from mixin:
50 50 flags = dict(flags)
51 51 # start with mixin frontend flags:
52 52 frontend_flags = dict(app_flags)
53 53 # add TerminalIPApp flags:
54 54 frontend_flags.update(term_flags)
55 55 # pylab is not frontend-specific in two-process IPython
56 56 frontend_flags.pop('pylab')
57 57 # disable quick startup, as it won't propagate to the kernel anyway
58 58 frontend_flags.pop('quick')
59 59 # update full dict with frontend flags:
60 60 flags.update(frontend_flags)
61 61
62 62 # copy flags from mixin
63 63 aliases = dict(aliases)
64 64 # start with mixin frontend flags
65 65 frontend_aliases = dict(app_aliases)
66 66 # load updated frontend flags into full dict
67 67 aliases.update(frontend_aliases)
68 68
69 69 # get flags&aliases into sets, and remove a couple that
70 70 # shouldn't be scrubbed from backend flags:
71 71 frontend_aliases = set(frontend_aliases.keys())
72 72 frontend_flags = set(frontend_flags.keys())
73 73
74 74
75 75 #-----------------------------------------------------------------------------
76 76 # Classes
77 77 #-----------------------------------------------------------------------------
78 78
79 79
80 class ZMQTerminalIPythonApp(TerminalIPythonApp, IPythonMixinConsoleApp):
80 class ZMQTerminalIPythonApp(TerminalIPythonApp, IPythonConsoleApp):
81 81 name = "ipython-console"
82 82 """Start a terminal frontend to the IPython zmq kernel."""
83 83
84 84 description = """
85 85 The IPython terminal-based Console.
86 86
87 87 This launches a Console application inside a terminal.
88 88
89 89 The Console supports various extra features beyond the traditional
90 90 single-process Terminal IPython shell, such as connecting to an
91 91 existing ipython session, via:
92 92
93 93 ipython console --existing
94 94
95 95 where the previous session could have been created by another ipython
96 96 console, an ipython qtconsole, or by opening an ipython notebook.
97 97
98 98 """
99 99 examples = _examples
100 100
101 101 classes = List([IPKernelApp, ZMQTerminalInteractiveShell, Session])
102 102 flags = Dict(flags)
103 103 aliases = Dict(aliases)
104 104 subcommands = Dict()
105 105
106 106 def parse_command_line(self, argv=None):
107 107 super(ZMQTerminalIPythonApp, self).parse_command_line(argv)
108 108 self.swallow_args(frontend_aliases,frontend_flags,argv=argv)
109 109
110 110 def init_shell(self):
111 IPythonMixinConsoleApp.initialize(self)
111 IPythonConsoleApp.initialize(self)
112 112 # relay sigint to kernel
113 113 signal.signal(signal.SIGINT, self.handle_sigint)
114 114 self.shell = ZMQTerminalInteractiveShell.instance(config=self.config,
115 115 display_banner=False, profile_dir=self.profile_dir,
116 116 ipython_dir=self.ipython_dir, kernel_manager=self.kernel_manager)
117 117
118 118 def handle_sigint(self, *args):
119 119 if self.shell._executing:
120 120 if self.kernel_manager.has_kernel:
121 121 # interrupt already gets passed to subprocess by signal handler.
122 122 # Only if we prevent that should we need to explicitly call
123 123 # interrupt_kernel, until which time, this would result in a
124 124 # double-interrupt:
125 125 # self.kernel_manager.interrupt_kernel()
126 126 pass
127 127 else:
128 128 self.shell.write_err('\n')
129 129 error("Cannot interrupt kernels we didn't start.\n")
130 130 else:
131 131 # raise the KeyboardInterrupt if we aren't waiting for execution,
132 132 # so that the interact loop advances, and prompt is redrawn, etc.
133 133 raise KeyboardInterrupt
134 134
135 135
136 136 def init_code(self):
137 137 # no-op in the frontend, code gets run in the backend
138 138 pass
139 139
140 140 def launch_new_instance():
141 141 """Create and run a full blown IPython instance"""
142 142 app = ZMQTerminalIPythonApp.instance()
143 143 app.initialize()
144 144 app.start()
145 145
146 146
147 147 if __name__ == '__main__':
148 148 launch_new_instance()
149 149
General Comments 0
You need to be logged in to leave comments. Login now