##// END OF EJS Templates
update flags&aliases for two-process apps...
MinRK -
Show More
@@ -1,383 +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 72 'existing' : ({'IPythonMixinConsoleApp' : {'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 76 'confirm-exit', 'IPythonMixinConsoleApp.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 90 hb = 'IPythonMixinConsoleApp.hb_port',
91 91 shell = 'IPythonMixinConsoleApp.shell_port',
92 92 iopub = 'IPythonMixinConsoleApp.iopub_port',
93 93 stdin = 'IPythonMixinConsoleApp.stdin_port',
94 94 ip = 'IPythonMixinConsoleApp.ip',
95 95 existing = 'IPythonMixinConsoleApp.existing',
96 96 f = 'IPythonMixinConsoleApp.connection_file',
97 97
98 98
99 99 ssh = 'IPythonMixinConsoleApp.sshserver',
100 100 )
101 101 aliases.update(app_aliases)
102 102
103 103 #-----------------------------------------------------------------------------
104 104 # Classes
105 105 #-----------------------------------------------------------------------------
106 106
107 107 #-----------------------------------------------------------------------------
108 108 # IPythonMixinConsole
109 109 #-----------------------------------------------------------------------------
110 110
111 111
112 112 class IPythonMixinConsoleApp(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 # copy again, in case some aliases have the same name as a flag
202 # argv = list(self.kernel_argv)
203 201 for a in argv:
204 202 if swallow_next:
205 203 swallow_next = False
206 204 # last arg was an alias, remove the next one
207 205 # *unless* the last alias has a no-arg flag version, in which
208 206 # case, don't swallow the next arg if it's also a flag:
209 207 if not (was_flag and a.startswith('-')):
210 208 self.kernel_argv.remove(a)
211 209 continue
212 210 if a.startswith('-'):
213 211 split = a.lstrip('-').split('=')
214 212 alias = split[0]
215 213 if alias in aliases:
216 214 self.kernel_argv.remove(a)
217 215 if len(split) == 1:
218 216 # alias passed with arg via space
219 217 swallow_next = True
220 218 # could have been a flag that matches an alias, e.g. `existing`
221 219 # in which case, we might not swallow the next arg
222 220 was_flag = alias in flags
223 221 elif alias in flags:
224 222 # strip flag, but don't swallow next, as flags don't take args
225 223 self.kernel_argv.remove(a)
226 224
227 225 def init_connection_file(self):
228 226 """find the connection file, and load the info if found.
229 227
230 228 The current working directory and the current profile's security
231 229 directory will be searched for the file if it is not given by
232 230 absolute path.
233 231
234 232 When attempting to connect to an existing kernel and the `--existing`
235 233 argument does not match an existing file, it will be interpreted as a
236 234 fileglob, and the matching file in the current profile's security dir
237 235 with the latest access time will be used.
238 236
239 237 After this method is called, self.connection_file contains the *full path*
240 238 to the connection file, never just its name.
241 239 """
242 240 if self.existing:
243 241 try:
244 242 cf = find_connection_file(self.existing)
245 243 except Exception:
246 244 self.log.critical("Could not find existing kernel connection file %s", self.existing)
247 245 self.exit(1)
248 246 self.log.info("Connecting to existing kernel: %s" % cf)
249 247 self.connection_file = cf
250 248 else:
251 249 # not existing, check if we are going to write the file
252 250 # and ensure that self.connection_file is a full path, not just the shortname
253 251 try:
254 252 cf = find_connection_file(self.connection_file)
255 253 except Exception:
256 254 # file might not exist
257 255 if self.connection_file == os.path.basename(self.connection_file):
258 256 # just shortname, put it in security dir
259 257 cf = os.path.join(self.profile_dir.security_dir, self.connection_file)
260 258 else:
261 259 cf = self.connection_file
262 260 self.connection_file = cf
263 261
264 262 # should load_connection_file only be used for existing?
265 263 # as it is now, this allows reusing ports if an existing
266 264 # file is requested
267 265 try:
268 266 self.load_connection_file()
269 267 except Exception:
270 268 self.log.error("Failed to load connection file: %r", self.connection_file, exc_info=True)
271 269 self.exit(1)
272 270
273 271 def load_connection_file(self):
274 272 """load ip/port/hmac config from JSON connection file"""
275 273 # this is identical to KernelApp.load_connection_file
276 274 # perhaps it can be centralized somewhere?
277 275 try:
278 276 fname = filefind(self.connection_file, ['.', self.profile_dir.security_dir])
279 277 except IOError:
280 278 self.log.debug("Connection File not found: %s", self.connection_file)
281 279 return
282 280 self.log.debug(u"Loading connection file %s", fname)
283 281 with open(fname) as f:
284 282 s = f.read()
285 283 cfg = json.loads(s)
286 284 if self.ip == LOCALHOST and 'ip' in cfg:
287 285 # not overridden by config or cl_args
288 286 self.ip = cfg['ip']
289 287 for channel in ('hb', 'shell', 'iopub', 'stdin'):
290 288 name = channel + '_port'
291 289 if getattr(self, name) == 0 and name in cfg:
292 290 # not overridden by config or cl_args
293 291 setattr(self, name, cfg[name])
294 292 if 'key' in cfg:
295 293 self.config.Session.key = str_to_bytes(cfg['key'])
296 294
297 295 def init_ssh(self):
298 296 """set up ssh tunnels, if needed."""
299 297 if not self.sshserver and not self.sshkey:
300 298 return
301 299
302 300 if self.sshkey and not self.sshserver:
303 301 # specifying just the key implies that we are connecting directly
304 302 self.sshserver = self.ip
305 303 self.ip = LOCALHOST
306 304
307 305 # build connection dict for tunnels:
308 306 info = dict(ip=self.ip,
309 307 shell_port=self.shell_port,
310 308 iopub_port=self.iopub_port,
311 309 stdin_port=self.stdin_port,
312 310 hb_port=self.hb_port
313 311 )
314 312
315 313 self.log.info("Forwarding connections to %s via %s"%(self.ip, self.sshserver))
316 314
317 315 # tunnels return a new set of ports, which will be on localhost:
318 316 self.ip = LOCALHOST
319 317 try:
320 318 newports = tunnel_to_kernel(info, self.sshserver, self.sshkey)
321 319 except:
322 320 # even catch KeyboardInterrupt
323 321 self.log.error("Could not setup tunnels", exc_info=True)
324 322 self.exit(1)
325 323
326 324 self.shell_port, self.iopub_port, self.stdin_port, self.hb_port = newports
327 325
328 326 cf = self.connection_file
329 327 base,ext = os.path.splitext(cf)
330 328 base = os.path.basename(base)
331 329 self.connection_file = os.path.basename(base)+'-ssh'+ext
332 330 self.log.critical("To connect another client via this tunnel, use:")
333 331 self.log.critical("--existing %s" % self.connection_file)
334 332
335 333 def _new_connection_file(self):
336 334 cf = ''
337 335 while not cf:
338 336 # we don't need a 128b id to distinguish kernels, use more readable
339 337 # 48b node segment (12 hex chars). Users running more than 32k simultaneous
340 338 # kernels can subclass.
341 339 ident = str(uuid.uuid4()).split('-')[-1]
342 340 cf = os.path.join(self.profile_dir.security_dir, 'kernel-%s.json' % ident)
343 341 # only keep if it's actually new. Protect against unlikely collision
344 342 # in 48b random search space
345 343 cf = cf if not os.path.exists(cf) else ''
346 344 return cf
347 345
348 346 def init_kernel_manager(self):
349 347 # Don't let Qt or ZMQ swallow KeyboardInterupts.
350 348 signal.signal(signal.SIGINT, signal.SIG_DFL)
351 349
352 350 # Create a KernelManager and start a kernel.
353 351 self.kernel_manager = self.kernel_manager_class(
354 352 ip=self.ip,
355 353 shell_port=self.shell_port,
356 354 iopub_port=self.iopub_port,
357 355 stdin_port=self.stdin_port,
358 356 hb_port=self.hb_port,
359 357 connection_file=self.connection_file,
360 358 config=self.config,
361 359 )
362 360 # start the kernel
363 361 if not self.existing:
364 362 kwargs = dict(ipython=not self.pure)
365 363 kwargs['extra_arguments'] = self.kernel_argv
366 364 self.kernel_manager.start_kernel(**kwargs)
367 365 elif self.sshserver:
368 366 # ssh, write new connection file
369 367 self.kernel_manager.write_connection_file()
370 368 atexit.register(self.kernel_manager.cleanup_connection_file)
371 369 self.kernel_manager.start_channels()
372 370
373 371
374 372 def initialize(self, argv=None):
375 373 """
376 374 Classes which mix this class in should call:
377 375 IPythonMixinConsoleApp.initialize(self,argv)
378 376 """
379 377 self.init_connection_file()
380 378 default_secure(self.config)
381 379 self.init_ssh()
382 380 self.init_kernel_manager()
383 381
@@ -1,341 +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 52 from IPython.frontend.kernelmixinapp import (
53 IPythonMixinConsoleApp, app_aliases, app_flags
53 IPythonMixinConsoleApp, 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 # XXX: the app_flags should really be flags from the mixin
76 flags = dict(app_flags)
75 # start with copy of flags
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 # and app_flags from the Console Mixin
89 qt_flags.update(app_flags)
90 # add frontend flags to the full set
88 91 flags.update(qt_flags)
89 92
90 aliases = dict(app_aliases)
91
93 # start with copy of front&backend aliases list
94 aliases = dict(aliases)
92 95 qt_aliases = dict(
93 96
94 97 style = 'IPythonWidget.syntax_style',
95 98 stylesheet = 'IPythonQtConsoleApp.stylesheet',
96 99 colors = 'ZMQInteractiveShell.colors',
97 100
98 101 editor = 'IPythonWidget.editor',
99 102 paging = 'ConsoleWidget.paging',
100 103 )
104 # and app_aliases from the Console Mixin
105 qt_aliases.update(app_aliases)
106 # add frontend aliases to the full set
101 107 aliases.update(qt_aliases)
102 108
109 # get flags&aliases into sets, and remove a couple that
110 # shouldn't be scrubbed from backend flags:
111 qt_aliases = set(qt_aliases.keys())
112 qt_aliases.remove('colors')
113 qt_flags = set(qt_flags.keys())
114
103 115 #-----------------------------------------------------------------------------
104 116 # Classes
105 117 #-----------------------------------------------------------------------------
106 118
107 119 #-----------------------------------------------------------------------------
108 120 # IPythonQtConsole
109 121 #-----------------------------------------------------------------------------
110 122
111 123
112 124 class IPythonQtConsoleApp(BaseIPythonApplication, IPythonMixinConsoleApp):
113 125 name = 'ipython-qtconsole'
114 126
115 127 description = """
116 128 The IPython QtConsole.
117 129
118 130 This launches a Console-style application using Qt. It is not a full
119 131 console, in that launched terminal subprocesses will not be able to accept
120 132 input.
121 133
122 134 The QtConsole supports various extra features beyond the Terminal IPython
123 135 shell, such as inline plotting with matplotlib, via:
124 136
125 137 ipython qtconsole --pylab=inline
126 138
127 139 as well as saving your session as HTML, and printing the output.
128 140
129 141 """
130 142 examples = _examples
131 143
132 144 classes = [IPKernelApp, IPythonWidget, ZMQInteractiveShell, ProfileDir, Session]
133 145 flags = Dict(flags)
134 146 aliases = Dict(aliases)
135 147 kernel_manager_class = QtKernelManager
136 148
137 149 stylesheet = Unicode('', config=True,
138 150 help="path to a custom CSS stylesheet")
139 151
140 152 plain = CBool(False, config=True,
141 153 help="Use a plaintext widget instead of rich text (plain can't print/save).")
142 154
143 155 def _pure_changed(self, name, old, new):
144 156 kind = 'plain' if self.plain else 'rich'
145 157 self.config.ConsoleWidget.kind = kind
146 158 if self.pure:
147 159 self.widget_factory = FrontendWidget
148 160 elif self.plain:
149 161 self.widget_factory = IPythonWidget
150 162 else:
151 163 self.widget_factory = RichIPythonWidget
152 164
153 165 _plain_changed = _pure_changed
154 166
155 167 # the factory for creating a widget
156 168 widget_factory = Any(RichIPythonWidget)
157 169
158 170 def parse_command_line(self, argv=None):
159 171 super(IPythonQtConsoleApp, self).parse_command_line(argv)
160 IPythonMixinConsoleApp.parse_command_line(self,argv)
161 172 self.swallow_args(qt_aliases,qt_flags,argv=argv)
162 173
163 174
164
165
166 175 def new_frontend_master(self):
167 176 """ Create and return new frontend attached to new kernel, launched on localhost.
168 177 """
169 178 ip = self.ip if self.ip in LOCAL_IPS else LOCALHOST
170 179 kernel_manager = QtKernelManager(
171 180 ip=ip,
172 181 connection_file=self._new_connection_file(),
173 182 config=self.config,
174 183 )
175 184 # start the kernel
176 185 kwargs = dict(ipython=not self.pure)
177 186 kwargs['extra_arguments'] = self.kernel_argv
178 187 kernel_manager.start_kernel(**kwargs)
179 188 kernel_manager.start_channels()
180 189 widget = self.widget_factory(config=self.config,
181 190 local_kernel=True)
182 191 widget.kernel_manager = kernel_manager
183 192 widget._existing = False
184 193 widget._may_close = True
185 194 widget._confirm_exit = self.confirm_exit
186 195 return widget
187 196
188 197 def new_frontend_slave(self, current_widget):
189 198 """Create and return a new frontend attached to an existing kernel.
190 199
191 200 Parameters
192 201 ----------
193 202 current_widget : IPythonWidget
194 203 The IPythonWidget whose kernel this frontend is to share
195 204 """
196 205 kernel_manager = QtKernelManager(
197 206 connection_file=current_widget.kernel_manager.connection_file,
198 207 config = self.config,
199 208 )
200 209 kernel_manager.load_connection_file()
201 210 kernel_manager.start_channels()
202 211 widget = self.widget_factory(config=self.config,
203 212 local_kernel=False)
204 213 widget._existing = True
205 214 widget._may_close = False
206 215 widget._confirm_exit = False
207 216 widget.kernel_manager = kernel_manager
208 217 return widget
209 218
210 219 def init_qt_elements(self):
211 220 # Create the widget.
212 221 self.app = QtGui.QApplication([])
213 222
214 223 base_path = os.path.abspath(os.path.dirname(__file__))
215 224 icon_path = os.path.join(base_path, 'resources', 'icon', 'IPythonConsole.svg')
216 225 self.app.icon = QtGui.QIcon(icon_path)
217 226 QtGui.QApplication.setWindowIcon(self.app.icon)
218 227
219 228 local_kernel = (not self.existing) or self.ip in LOCAL_IPS
220 229 self.widget = self.widget_factory(config=self.config,
221 230 local_kernel=local_kernel)
222 231 self.widget._existing = self.existing
223 232 self.widget._may_close = not self.existing
224 233 self.widget._confirm_exit = self.confirm_exit
225 234
226 235 self.widget.kernel_manager = self.kernel_manager
227 236 self.window = MainWindow(self.app,
228 237 confirm_exit=self.confirm_exit,
229 238 new_frontend_factory=self.new_frontend_master,
230 239 slave_frontend_factory=self.new_frontend_slave,
231 240 )
232 241 self.window.log = self.log
233 242 self.window.add_tab_with_frontend(self.widget)
234 243 self.window.init_menu_bar()
235 244
236 245 self.window.setWindowTitle('Python' if self.pure else 'IPython')
237 246
238 247 def init_colors(self):
239 248 """Configure the coloring of the widget"""
240 249 # Note: This will be dramatically simplified when colors
241 250 # are removed from the backend.
242 251
243 252 if self.pure:
244 253 # only IPythonWidget supports styling
245 254 return
246 255
247 256 # parse the colors arg down to current known labels
248 257 try:
249 258 colors = self.config.ZMQInteractiveShell.colors
250 259 except AttributeError:
251 260 colors = None
252 261 try:
253 262 style = self.config.IPythonWidget.syntax_style
254 263 except AttributeError:
255 264 style = None
256 265
257 266 # find the value for colors:
258 267 if colors:
259 268 colors=colors.lower()
260 269 if colors in ('lightbg', 'light'):
261 270 colors='lightbg'
262 271 elif colors in ('dark', 'linux'):
263 272 colors='linux'
264 273 else:
265 274 colors='nocolor'
266 275 elif style:
267 276 if style=='bw':
268 277 colors='nocolor'
269 278 elif styles.dark_style(style):
270 279 colors='linux'
271 280 else:
272 281 colors='lightbg'
273 282 else:
274 283 colors=None
275 284
276 285 # Configure the style.
277 286 widget = self.widget
278 287 if style:
279 288 widget.style_sheet = styles.sheet_from_template(style, colors)
280 289 widget.syntax_style = style
281 290 widget._syntax_style_changed()
282 291 widget._style_sheet_changed()
283 292 elif colors:
284 293 # use a default style
285 294 widget.set_default_style(colors=colors)
286 295 else:
287 296 # this is redundant for now, but allows the widget's
288 297 # defaults to change
289 298 widget.set_default_style()
290 299
291 300 if self.stylesheet:
292 301 # we got an expicit stylesheet
293 302 if os.path.isfile(self.stylesheet):
294 303 with open(self.stylesheet) as f:
295 304 sheet = f.read()
296 305 widget.style_sheet = sheet
297 306 widget._style_sheet_changed()
298 307 else:
299 308 raise IOError("Stylesheet %r not found."%self.stylesheet)
300 309
301 310 def init_signal(self):
302 311 """allow clean shutdown on sigint"""
303 312 signal.signal(signal.SIGINT, lambda sig, frame: self.exit(-2))
304 313 # need a timer, so that QApplication doesn't block until a real
305 314 # Qt event fires (can require mouse movement)
306 315 # timer trick from http://stackoverflow.com/q/4938723/938949
307 316 timer = QtCore.QTimer()
308 317 # Let the interpreter run each 200 ms:
309 318 timer.timeout.connect(lambda: None)
310 319 timer.start(200)
311 320 # hold onto ref, so the timer doesn't get cleaned up
312 321 self._sigint_timer = timer
313 322
314 323 @catch_config_error
315 324 def initialize(self, argv=None):
316 325 super(IPythonQtConsoleApp, self).initialize(argv)
317 326 IPythonMixinConsoleApp.initialize(self,argv)
318 327 self.init_qt_elements()
319 328 self.init_colors()
320 329 self.init_signal()
321 330
322 331 def start(self):
323 332
324 333 # draw the window
325 334 self.window.show()
326 335
327 336 # Start the application main loop.
328 337 self.app.exec_()
329 338
330 339 #-----------------------------------------------------------------------------
331 340 # Main entry point
332 341 #-----------------------------------------------------------------------------
333 342
334 343 def main():
335 344 app = IPythonQtConsoleApp()
336 345 app.initialize()
337 346 app.start()
338 347
339 348
340 349 if __name__ == '__main__':
341 350 main()
@@ -1,405 +1,407 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 """
4 4 The :class:`~IPython.core.application.Application` object for the command
5 5 line :command:`ipython` program.
6 6
7 7 Authors
8 8 -------
9 9
10 10 * Brian Granger
11 11 * Fernando Perez
12 12 * Min Ragan-Kelley
13 13 """
14 14
15 15 #-----------------------------------------------------------------------------
16 16 # Copyright (C) 2008-2011 The IPython Development Team
17 17 #
18 18 # Distributed under the terms of the BSD License. The full license is in
19 19 # the file COPYING, distributed as part of this software.
20 20 #-----------------------------------------------------------------------------
21 21
22 22 #-----------------------------------------------------------------------------
23 23 # Imports
24 24 #-----------------------------------------------------------------------------
25 25
26 26 from __future__ import absolute_import
27 27
28 28 import logging
29 29 import os
30 30 import sys
31 31
32 32 from IPython.config.loader import (
33 33 Config, PyFileConfigLoader, ConfigFileNotFound
34 34 )
35 35 from IPython.config.application import boolean_flag, catch_config_error
36 36 from IPython.core import release
37 37 from IPython.core import usage
38 38 from IPython.core.completer import IPCompleter
39 39 from IPython.core.crashhandler import CrashHandler
40 40 from IPython.core.formatters import PlainTextFormatter
41 41 from IPython.core.prompts import PromptManager
42 42 from IPython.core.application import (
43 43 ProfileDir, BaseIPythonApplication, base_flags, base_aliases
44 44 )
45 45 from IPython.core.shellapp import (
46 46 InteractiveShellApp, shell_flags, shell_aliases
47 47 )
48 48 from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell
49 49 from IPython.lib import inputhook
50 50 from IPython.utils import warn
51 51 from IPython.utils.path import get_ipython_dir, check_for_old_config
52 52 from IPython.utils.traitlets import (
53 53 Bool, List, Dict, CaselessStrEnum
54 54 )
55 55
56 56 #-----------------------------------------------------------------------------
57 57 # Globals, utilities and helpers
58 58 #-----------------------------------------------------------------------------
59 59
60 60 #: The default config file name for this application.
61 61 default_config_file_name = u'ipython_config.py'
62 62
63 63 _examples = """
64 64 ipython --pylab # start in pylab mode
65 65 ipython --pylab=qt # start in pylab mode with the qt4 backend
66 66 ipython --log-level=DEBUG # set logging to DEBUG
67 67 ipython --profile=foo # start with profile foo
68 68
69 69 ipython qtconsole # start the qtconsole GUI application
70 70 ipython qtconsole -h # show the help string for the qtconsole subcmd
71 71
72 72 ipython console # start the terminal-based console application
73 73 ipython console -h # show the help string for the console subcmd
74 74
75 75 ipython profile create foo # create profile foo w/ default config files
76 76 ipython profile -h # show the help string for the profile subcmd
77 77 """
78 78
79 79 #-----------------------------------------------------------------------------
80 80 # Crash handler for this application
81 81 #-----------------------------------------------------------------------------
82 82
83 83 class IPAppCrashHandler(CrashHandler):
84 84 """sys.excepthook for IPython itself, leaves a detailed report on disk."""
85 85
86 86 def __init__(self, app):
87 87 contact_name = release.authors['Fernando'][0]
88 88 contact_email = release.author_email
89 89 bug_tracker = 'https://github.com/ipython/ipython/issues'
90 90 super(IPAppCrashHandler,self).__init__(
91 91 app, contact_name, contact_email, bug_tracker
92 92 )
93 93
94 94 def make_report(self,traceback):
95 95 """Return a string containing a crash report."""
96 96
97 97 sec_sep = self.section_sep
98 98 # Start with parent report
99 99 report = [super(IPAppCrashHandler, self).make_report(traceback)]
100 100 # Add interactive-specific info we may have
101 101 rpt_add = report.append
102 102 try:
103 103 rpt_add(sec_sep+"History of session input:")
104 104 for line in self.app.shell.user_ns['_ih']:
105 105 rpt_add(line)
106 106 rpt_add('\n*** Last line of input (may not be in above history):\n')
107 107 rpt_add(self.app.shell._last_input_line+'\n')
108 108 except:
109 109 pass
110 110
111 111 return ''.join(report)
112 112
113 113 #-----------------------------------------------------------------------------
114 114 # Aliases and Flags
115 115 #-----------------------------------------------------------------------------
116 116 flags = dict(base_flags)
117 117 flags.update(shell_flags)
118 addflag = lambda *args: flags.update(boolean_flag(*args))
118 frontend_flags = {}
119 addflag = lambda *args: frontend_flags.update(boolean_flag(*args))
119 120 addflag('autoedit-syntax', 'TerminalInteractiveShell.autoedit_syntax',
120 121 'Turn on auto editing of files with syntax errors.',
121 122 'Turn off auto editing of files with syntax errors.'
122 123 )
123 124 addflag('banner', 'TerminalIPythonApp.display_banner',
124 125 "Display a banner upon starting IPython.",
125 126 "Don't display a banner upon starting IPython."
126 127 )
127 128 addflag('confirm-exit', 'TerminalInteractiveShell.confirm_exit',
128 129 """Set to confirm when you try to exit IPython with an EOF (Control-D
129 130 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
130 131 you can force a direct exit without any confirmation.""",
131 132 "Don't prompt the user when exiting."
132 133 )
133 134 addflag('term-title', 'TerminalInteractiveShell.term_title',
134 135 "Enable auto setting the terminal title.",
135 136 "Disable auto setting the terminal title."
136 137 )
137 138 classic_config = Config()
138 139 classic_config.InteractiveShell.cache_size = 0
139 140 classic_config.PlainTextFormatter.pprint = False
140 141 classic_config.PromptManager.in_template = '>>> '
141 142 classic_config.PromptManager.in2_template = '... '
142 143 classic_config.PromptManager.out_template = ''
143 144 classic_config.InteractiveShell.separate_in = ''
144 145 classic_config.InteractiveShell.separate_out = ''
145 146 classic_config.InteractiveShell.separate_out2 = ''
146 147 classic_config.InteractiveShell.colors = 'NoColor'
147 148 classic_config.InteractiveShell.xmode = 'Plain'
148 149
149 flags['classic']=(
150 frontend_flags['classic']=(
150 151 classic_config,
151 152 "Gives IPython a similar feel to the classic Python prompt."
152 153 )
153 154 # # log doesn't make so much sense this way anymore
154 155 # paa('--log','-l',
155 156 # action='store_true', dest='InteractiveShell.logstart',
156 157 # help="Start logging to the default log file (./ipython_log.py).")
157 158 #
158 159 # # quick is harder to implement
159 flags['quick']=(
160 frontend_flags['quick']=(
160 161 {'TerminalIPythonApp' : {'quick' : True}},
161 162 "Enable quick startup with no config files."
162 163 )
163 164
164 flags['i'] = (
165 frontend_flags['i'] = (
165 166 {'TerminalIPythonApp' : {'force_interact' : True}},
166 167 """If running code from the command line, become interactive afterwards.
167 168 Note: can also be given simply as '-i.'"""
168 169 )
169 flags['pylab'] = (
170 frontend_flags['pylab'] = (
170 171 {'TerminalIPythonApp' : {'pylab' : 'auto'}},
171 172 """Pre-load matplotlib and numpy for interactive use with
172 173 the default matplotlib backend."""
173 174 )
175 flags.update(frontend_flags)
174 176
175 177 aliases = dict(base_aliases)
176 178 aliases.update(shell_aliases)
177 179
178 180 # it's possible we don't want short aliases for *all* of these:
179 181 aliases.update(dict(
180 182 gui='TerminalIPythonApp.gui',
181 183 pylab='TerminalIPythonApp.pylab',
182 184 ))
183 185
184 186 #-----------------------------------------------------------------------------
185 187 # Main classes and functions
186 188 #-----------------------------------------------------------------------------
187 189
188 190 class TerminalIPythonApp(BaseIPythonApplication, InteractiveShellApp):
189 191 name = u'ipython'
190 192 description = usage.cl_usage
191 193 default_config_file_name = default_config_file_name
192 194 crash_handler_class = IPAppCrashHandler
193 195 examples = _examples
194 196
195 197 flags = Dict(flags)
196 198 aliases = Dict(aliases)
197 199 classes = List()
198 200 def _classes_default(self):
199 201 """This has to be in a method, for TerminalIPythonApp to be available."""
200 202 return [
201 203 InteractiveShellApp, # ShellApp comes before TerminalApp, because
202 204 self.__class__, # it will also affect subclasses (e.g. QtConsole)
203 205 TerminalInteractiveShell,
204 206 PromptManager,
205 207 ProfileDir,
206 208 PlainTextFormatter,
207 209 IPCompleter,
208 210 ]
209 211
210 212 subcommands = Dict(dict(
211 213 qtconsole=('IPython.frontend.qt.console.qtconsoleapp.IPythonQtConsoleApp',
212 214 """Launch the IPython Qt Console."""
213 215 ),
214 216 notebook=('IPython.frontend.html.notebook.notebookapp.NotebookApp',
215 217 """Launch the IPython HTML Notebook Server."""
216 218 ),
217 219 profile = ("IPython.core.profileapp.ProfileApp",
218 220 "Create and manage IPython profiles."
219 221 ),
220 222 kernel = ("IPython.zmq.ipkernel.IPKernelApp",
221 223 "Start a kernel without an attached frontend."
222 224 ),
223 225 console=('IPython.frontend.zmqterminal.app.ZMQTerminalIPythonApp',
224 226 """Launch the IPython terminal-based Console."""
225 227 ),
226 228 ))
227 229
228 230 # *do* autocreate requested profile, but don't create the config file.
229 231 auto_create=Bool(True)
230 232 # configurables
231 233 ignore_old_config=Bool(False, config=True,
232 234 help="Suppress warning messages about legacy config files"
233 235 )
234 236 quick = Bool(False, config=True,
235 237 help="""Start IPython quickly by skipping the loading of config files."""
236 238 )
237 239 def _quick_changed(self, name, old, new):
238 240 if new:
239 241 self.load_config_file = lambda *a, **kw: None
240 242 self.ignore_old_config=True
241 243
242 244 gui = CaselessStrEnum(('qt', 'wx', 'gtk', 'glut', 'pyglet'), config=True,
243 245 help="Enable GUI event loop integration ('qt', 'wx', 'gtk', 'glut', 'pyglet')."
244 246 )
245 247 pylab = CaselessStrEnum(['tk', 'qt', 'wx', 'gtk', 'osx', 'auto'],
246 248 config=True,
247 249 help="""Pre-load matplotlib and numpy for interactive use,
248 250 selecting a particular matplotlib backend and loop integration.
249 251 """
250 252 )
251 253 display_banner = Bool(True, config=True,
252 254 help="Whether to display a banner upon starting IPython."
253 255 )
254 256
255 257 # if there is code of files to run from the cmd line, don't interact
256 258 # unless the --i flag (App.force_interact) is true.
257 259 force_interact = Bool(False, config=True,
258 260 help="""If a command or file is given via the command-line,
259 261 e.g. 'ipython foo.py"""
260 262 )
261 263 def _force_interact_changed(self, name, old, new):
262 264 if new:
263 265 self.interact = True
264 266
265 267 def _file_to_run_changed(self, name, old, new):
266 268 if new and not self.force_interact:
267 269 self.interact = False
268 270 _code_to_run_changed = _file_to_run_changed
269 271
270 272 # internal, not-configurable
271 273 interact=Bool(True)
272 274
273 275
274 276 def parse_command_line(self, argv=None):
275 277 """override to allow old '-pylab' flag with deprecation warning"""
276 278
277 279 argv = sys.argv[1:] if argv is None else argv
278 280
279 281 if '-pylab' in argv:
280 282 # deprecated `-pylab` given,
281 283 # warn and transform into current syntax
282 284 argv = argv[:] # copy, don't clobber
283 285 idx = argv.index('-pylab')
284 286 warn.warn("`-pylab` flag has been deprecated.\n"
285 287 " Use `--pylab` instead, or `--pylab=foo` to specify a backend.")
286 288 sub = '--pylab'
287 289 if len(argv) > idx+1:
288 290 # check for gui arg, as in '-pylab qt'
289 291 gui = argv[idx+1]
290 292 if gui in ('wx', 'qt', 'qt4', 'gtk', 'auto'):
291 293 sub = '--pylab='+gui
292 294 argv.pop(idx+1)
293 295 argv[idx] = sub
294 296
295 297 return super(TerminalIPythonApp, self).parse_command_line(argv)
296 298
297 299 @catch_config_error
298 300 def initialize(self, argv=None):
299 301 """Do actions after construct, but before starting the app."""
300 302 super(TerminalIPythonApp, self).initialize(argv)
301 303 if self.subapp is not None:
302 304 # don't bother initializing further, starting subapp
303 305 return
304 306 if not self.ignore_old_config:
305 307 check_for_old_config(self.ipython_dir)
306 308 # print self.extra_args
307 309 if self.extra_args:
308 310 self.file_to_run = self.extra_args[0]
309 311 # create the shell
310 312 self.init_shell()
311 313 # and draw the banner
312 314 self.init_banner()
313 315 # Now a variety of things that happen after the banner is printed.
314 316 self.init_gui_pylab()
315 317 self.init_extensions()
316 318 self.init_code()
317 319
318 320 def init_shell(self):
319 321 """initialize the InteractiveShell instance"""
320 322 # I am a little hesitant to put these into InteractiveShell itself.
321 323 # But that might be the place for them
322 324 sys.path.insert(0, '')
323 325
324 326 # Create an InteractiveShell instance.
325 327 # shell.display_banner should always be False for the terminal
326 328 # based app, because we call shell.show_banner() by hand below
327 329 # so the banner shows *before* all extension loading stuff.
328 330 self.shell = TerminalInteractiveShell.instance(config=self.config,
329 331 display_banner=False, profile_dir=self.profile_dir,
330 332 ipython_dir=self.ipython_dir)
331 333 self.shell.configurables.append(self)
332 334
333 335 def init_banner(self):
334 336 """optionally display the banner"""
335 337 if self.display_banner and self.interact:
336 338 self.shell.show_banner()
337 339 # Make sure there is a space below the banner.
338 340 if self.log_level <= logging.INFO: print
339 341
340 342
341 343 def init_gui_pylab(self):
342 344 """Enable GUI event loop integration, taking pylab into account."""
343 345 gui = self.gui
344 346
345 347 # Using `pylab` will also require gui activation, though which toolkit
346 348 # to use may be chosen automatically based on mpl configuration.
347 349 if self.pylab:
348 350 activate = self.shell.enable_pylab
349 351 if self.pylab == 'auto':
350 352 gui = None
351 353 else:
352 354 gui = self.pylab
353 355 else:
354 356 # Enable only GUI integration, no pylab
355 357 activate = inputhook.enable_gui
356 358
357 359 if gui or self.pylab:
358 360 try:
359 361 self.log.info("Enabling GUI event loop integration, "
360 362 "toolkit=%s, pylab=%s" % (gui, self.pylab) )
361 363 if self.pylab:
362 364 activate(gui, import_all=self.pylab_import_all)
363 365 else:
364 366 activate(gui)
365 367 except:
366 368 self.log.warn("Error in enabling GUI event loop integration:")
367 369 self.shell.showtraceback()
368 370
369 371 def start(self):
370 372 if self.subapp is not None:
371 373 return self.subapp.start()
372 374 # perform any prexec steps:
373 375 if self.interact:
374 376 self.log.debug("Starting IPython's mainloop...")
375 377 self.shell.mainloop()
376 378 else:
377 379 self.log.debug("IPython not interactive...")
378 380
379 381
380 382 def load_default_config(ipython_dir=None):
381 383 """Load the default config file from the default ipython_dir.
382 384
383 385 This is useful for embedded shells.
384 386 """
385 387 if ipython_dir is None:
386 388 ipython_dir = get_ipython_dir()
387 389 profile_dir = os.path.join(ipython_dir, 'profile_default')
388 390 cl = PyFileConfigLoader(default_config_file_name, profile_dir)
389 391 try:
390 392 config = cl.load_config()
391 393 except ConfigFileNotFound:
392 394 # no config found
393 395 config = Config()
394 396 return config
395 397
396 398
397 399 def launch_new_instance():
398 400 """Create and run a full blown IPython instance"""
399 401 app = TerminalIPythonApp.instance()
400 402 app.initialize()
401 403 app.start()
402 404
403 405
404 406 if __name__ == '__main__':
405 407 launch_new_instance()
@@ -1,124 +1,137 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 from IPython.frontend.terminal.ipapp import TerminalIPythonApp
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.zmq.ipkernel import IPKernelApp
26 26 from IPython.zmq.session import Session, default_secure
27 27 from IPython.zmq.zmqshell import ZMQInteractiveShell
28 28 from IPython.frontend.kernelmixinapp import (
29 IPythonMixinConsoleApp, app_aliases, app_flags
29 IPythonMixinConsoleApp, app_aliases, app_flags, aliases, app_aliases, flags
30 30 )
31 31
32 32 from IPython.frontend.zmqterminal.interactiveshell import ZMQTerminalInteractiveShell
33 33
34 34 #-----------------------------------------------------------------------------
35 35 # Globals
36 36 #-----------------------------------------------------------------------------
37 37
38 38 _examples = """
39 39 ipython console # start the ZMQ-based console
40 40 ipython console --existing # connect to an existing ipython session
41 41 """
42 42
43 43 #-----------------------------------------------------------------------------
44 44 # Flags and Aliases
45 45 #-----------------------------------------------------------------------------
46 46
47 # XXX: the app_flags should really be flags from the mixin
48 flags = dict(app_flags)
49 frontend_flags = { }
47 # copy flags from mixin:
48 flags = dict(flags)
49 # start with mixin frontend flags:
50 frontend_flags = dict(app_flags)
51 # add TerminalIPApp flags:
52 frontend_flags.update(term_flags)
53 # pylab is not frontend-specific in two-process IPython
54 frontend_flags.pop('pylab')
55 # disable quick startup, as it won't propagate to the kernel anyway
56 frontend_flags.pop('quick')
57 # update full dict with frontend flags:
50 58 flags.update(frontend_flags)
51 59
52 frontend_flags = frontend_flags.keys()
53
54 aliases = dict(app_aliases)
60 # copy flags from mixin
61 aliases = dict(aliases)
62 # start with mixin frontend flags
63 frontend_aliases = dict(app_aliases)
64 # load updated frontend flags into full dict
65 aliases.update(frontend_aliases)
55 66
56 frontend_aliases = dict()
67 # get flags&aliases into sets, and remove a couple that
68 # shouldn't be scrubbed from backend flags:
69 frontend_aliases = set(frontend_aliases.keys())
70 frontend_flags = set(frontend_flags.keys())
57 71
58 aliases.update(frontend_aliases)
59 72
60 73 #-----------------------------------------------------------------------------
61 74 # Classes
62 75 #-----------------------------------------------------------------------------
63 76
64 77
65 78 class ZMQTerminalIPythonApp(TerminalIPythonApp, IPythonMixinConsoleApp):
66 name = "ipython console"
79 name = "ipython-console"
67 80 """Start a terminal frontend to the IPython zmq kernel."""
68 81
69 82 description = """
70 83 The IPython terminal-based Console.
71 84
72 85 This launches a Console application inside a terminal.
73 86
74 87 The Console supports various extra features beyond the traditional
75 88 single-process Terminal IPython shell, such as connecting to an
76 89 existing ipython session, via:
77 90
78 91 ipython console --existing
79 92
80 93 where the previous session could have been created by another ipython
81 94 console, an ipython qtconsole, or by opening an ipython notebook.
82 95
83 96 """
84 97 examples = _examples
85 98
86 classes = List([IPKernelApp, ZMQTerminalInteractiveShell])
99 classes = List([IPKernelApp, ZMQTerminalInteractiveShell, Session])
87 100 flags = Dict(flags)
88 101 aliases = Dict(aliases)
89 102 subcommands = Dict()
103
90 104 def parse_command_line(self, argv=None):
91 105 super(ZMQTerminalIPythonApp, self).parse_command_line(argv)
92 IPythonMixinConsoleApp.parse_command_line(self,argv)
93 106 self.swallow_args(frontend_aliases,frontend_flags,argv=argv)
94 107
95 108 def init_shell(self):
96 109 IPythonMixinConsoleApp.initialize(self)
97 110 # relay sigint to kernel
98 111 signal.signal(signal.SIGINT, self.handle_sigint)
99 112 self.shell = ZMQTerminalInteractiveShell.instance(config=self.config,
100 113 display_banner=False, profile_dir=self.profile_dir,
101 114 ipython_dir=self.ipython_dir, kernel_manager=self.kernel_manager)
102 115
103 116 def handle_sigint(self, *args):
104 117 self.shell.write('KeyboardInterrupt\n')
105 118 if self.kernel_manager.has_kernel:
106 119 self.kernel_manager.interrupt_kernel()
107 120 else:
108 121 print 'Kernel process is either remote or unspecified.',
109 122 print 'Cannot interrupt.'
110 123
111 124 def init_code(self):
112 125 # no-op in the frontend, code gets run in the backend
113 126 pass
114 127
115 128 def launch_new_instance():
116 129 """Create and run a full blown IPython instance"""
117 130 app = ZMQTerminalIPythonApp.instance()
118 131 app.initialize()
119 132 app.start()
120 133
121 134
122 135 if __name__ == '__main__':
123 136 launch_new_instance()
124 137
General Comments 0
You need to be logged in to leave comments. Login now