##// END OF EJS Templates
Don't blend kernel and frontend CLI args...
Min RK -
Show More
@@ -1,344 +1,330 b''
1 """ A minimal application base mixin for all ZMQ based IPython frontends.
1 """ A minimal application base mixin for all ZMQ based IPython frontends.
2
2
3 This is not a complete console app, as subprocess will not be able to receive
3 This is not a complete console app, as subprocess will not be able to receive
4 input, there is no real readline support, among other limitations. This is a
4 input, there is no real readline support, among other limitations. This is a
5 refactoring of what used to be the IPython/qt/console/qtconsoleapp.py
5 refactoring of what used to be the IPython/qt/console/qtconsoleapp.py
6 """
6 """
7 # Copyright (c) IPython Development Team.
7 # Copyright (c) IPython Development Team.
8 # Distributed under the terms of the Modified BSD License.
8 # Distributed under the terms of the Modified BSD License.
9
9
10 import atexit
10 import atexit
11 import os
11 import os
12 import signal
12 import signal
13 import sys
13 import sys
14 import uuid
14 import uuid
15
15
16
16
17 from IPython.config.application import boolean_flag
17 from IPython.config.application import boolean_flag
18 from IPython.core.profiledir import ProfileDir
18 from IPython.core.profiledir import ProfileDir
19 from IPython.kernel.blocking import BlockingKernelClient
19 from IPython.kernel.blocking import BlockingKernelClient
20 from IPython.kernel import KernelManager
20 from IPython.kernel import KernelManager
21 from IPython.kernel import tunnel_to_kernel, find_connection_file, swallow_argv
21 from IPython.kernel import tunnel_to_kernel, find_connection_file
22 from IPython.kernel.kernelspec import NoSuchKernel
22 from IPython.kernel.kernelspec import NoSuchKernel
23 from IPython.utils.path import filefind
23 from IPython.utils.path import filefind
24 from IPython.utils.traitlets import (
24 from IPython.utils.traitlets import (
25 Dict, List, Unicode, CUnicode, CBool, Any
25 Dict, List, Unicode, CUnicode, CBool, Any
26 )
26 )
27 from IPython.kernel.zmq.kernelapp import (
28 kernel_flags,
29 kernel_aliases,
30 IPKernelApp
31 )
32 from IPython.kernel.zmq.pylab.config import InlineBackend
33 from IPython.kernel.zmq.session import Session
27 from IPython.kernel.zmq.session import Session
34 from IPython.kernel.zmq.zmqshell import ZMQInteractiveShell
35 from IPython.kernel.connect import ConnectionFileMixin
28 from IPython.kernel.connect import ConnectionFileMixin
36
29
37 from IPython.utils.localinterfaces import localhost
30 from IPython.utils.localinterfaces import localhost
38
31
39 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
40 # Aliases and Flags
33 # Aliases and Flags
41 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
42
35
43 flags = dict(kernel_flags)
36 flags = {}
44
37
45 # the flags that are specific to the frontend
38 # the flags that are specific to the frontend
46 # these must be scrubbed before being passed to the kernel,
39 # these must be scrubbed before being passed to the kernel,
47 # or it will raise an error on unrecognized flags
40 # or it will raise an error on unrecognized flags
48 app_flags = {
41 app_flags = {
49 'existing' : ({'IPythonConsoleApp' : {'existing' : 'kernel*.json'}},
42 'existing' : ({'IPythonConsoleApp' : {'existing' : 'kernel*.json'}},
50 "Connect to an existing kernel. If no argument specified, guess most recent"),
43 "Connect to an existing kernel. If no argument specified, guess most recent"),
51 }
44 }
52 app_flags.update(boolean_flag(
45 app_flags.update(boolean_flag(
53 'confirm-exit', 'IPythonConsoleApp.confirm_exit',
46 'confirm-exit', 'IPythonConsoleApp.confirm_exit',
54 """Set to display confirmation dialog on exit. You can always use 'exit' or 'quit',
47 """Set to display confirmation dialog on exit. You can always use 'exit' or 'quit',
55 to force a direct exit without any confirmation.
48 to force a direct exit without any confirmation.
56 """,
49 """,
57 """Don't prompt the user when exiting. This will terminate the kernel
50 """Don't prompt the user when exiting. This will terminate the kernel
58 if it is owned by the frontend, and leave it alive if it is external.
51 if it is owned by the frontend, and leave it alive if it is external.
59 """
52 """
60 ))
53 ))
61 flags.update(app_flags)
54 flags.update(app_flags)
62
55
63 aliases = dict(kernel_aliases)
56 aliases = {}
64
57
65 # also scrub aliases from the frontend
58 # also scrub aliases from the frontend
66 app_aliases = dict(
59 app_aliases = dict(
67 ip = 'IPythonConsoleApp.ip',
60 ip = 'IPythonConsoleApp.ip',
68 transport = 'IPythonConsoleApp.transport',
61 transport = 'IPythonConsoleApp.transport',
69 hb = 'IPythonConsoleApp.hb_port',
62 hb = 'IPythonConsoleApp.hb_port',
70 shell = 'IPythonConsoleApp.shell_port',
63 shell = 'IPythonConsoleApp.shell_port',
71 iopub = 'IPythonConsoleApp.iopub_port',
64 iopub = 'IPythonConsoleApp.iopub_port',
72 stdin = 'IPythonConsoleApp.stdin_port',
65 stdin = 'IPythonConsoleApp.stdin_port',
73 existing = 'IPythonConsoleApp.existing',
66 existing = 'IPythonConsoleApp.existing',
74 f = 'IPythonConsoleApp.connection_file',
67 f = 'IPythonConsoleApp.connection_file',
75
68
76 kernel = 'IPythonConsoleApp.kernel_name',
69 kernel = 'IPythonConsoleApp.kernel_name',
77
70
78 ssh = 'IPythonConsoleApp.sshserver',
71 ssh = 'IPythonConsoleApp.sshserver',
79 )
72 )
80 aliases.update(app_aliases)
73 aliases.update(app_aliases)
81
74
82 #-----------------------------------------------------------------------------
75 #-----------------------------------------------------------------------------
83 # Classes
76 # Classes
84 #-----------------------------------------------------------------------------
77 #-----------------------------------------------------------------------------
85
78
86 classes = [KernelManager, ProfileDir, Session]
79 classes = [KernelManager, ProfileDir, Session]
87
80
88 class IPythonConsoleApp(ConnectionFileMixin):
81 class IPythonConsoleApp(ConnectionFileMixin):
89 name = 'ipython-console-mixin'
82 name = 'ipython-console-mixin'
90
83
91 description = """
84 description = """
92 The IPython Mixin Console.
85 The IPython Mixin Console.
93
86
94 This class contains the common portions of console client (QtConsole,
87 This class contains the common portions of console client (QtConsole,
95 ZMQ-based terminal console, etc). It is not a full console, in that
88 ZMQ-based terminal console, etc). It is not a full console, in that
96 launched terminal subprocesses will not be able to accept input.
89 launched terminal subprocesses will not be able to accept input.
97
90
98 The Console using this mixing supports various extra features beyond
91 The Console using this mixing supports various extra features beyond
99 the single-process Terminal IPython shell, such as connecting to
92 the single-process Terminal IPython shell, such as connecting to
100 existing kernel, via:
93 existing kernel, via:
101
94
102 ipython <appname> --existing
95 ipython <appname> --existing
103
96
104 as well as tunnel via SSH
97 as well as tunnel via SSH
105
98
106 """
99 """
107
100
108 classes = classes
101 classes = classes
109 flags = Dict(flags)
102 flags = Dict(flags)
110 aliases = Dict(aliases)
103 aliases = Dict(aliases)
111 kernel_manager_class = KernelManager
104 kernel_manager_class = KernelManager
112 kernel_client_class = BlockingKernelClient
105 kernel_client_class = BlockingKernelClient
113
106
114 kernel_argv = List(Unicode)
107 kernel_argv = List(Unicode)
115 # frontend flags&aliases to be stripped when building kernel_argv
108 # frontend flags&aliases to be stripped when building kernel_argv
116 frontend_flags = Any(app_flags)
109 frontend_flags = Any(app_flags)
117 frontend_aliases = Any(app_aliases)
110 frontend_aliases = Any(app_aliases)
118
111
119 # create requested profiles by default, if they don't exist:
112 # create requested profiles by default, if they don't exist:
120 auto_create = CBool(True)
113 auto_create = CBool(True)
121 # connection info:
114 # connection info:
122
115
123 sshserver = Unicode('', config=True,
116 sshserver = Unicode('', config=True,
124 help="""The SSH server to use to connect to the kernel.""")
117 help="""The SSH server to use to connect to the kernel.""")
125 sshkey = Unicode('', config=True,
118 sshkey = Unicode('', config=True,
126 help="""Path to the ssh key to use for logging in to the ssh server.""")
119 help="""Path to the ssh key to use for logging in to the ssh server.""")
127
120
128 def _connection_file_default(self):
121 def _connection_file_default(self):
129 return 'kernel-%i.json' % os.getpid()
122 return 'kernel-%i.json' % os.getpid()
130
123
131 existing = CUnicode('', config=True,
124 existing = CUnicode('', config=True,
132 help="""Connect to an already running kernel""")
125 help="""Connect to an already running kernel""")
133
126
134 kernel_name = Unicode('python', config=True,
127 kernel_name = Unicode('python', config=True,
135 help="""The name of the default kernel to start.""")
128 help="""The name of the default kernel to start.""")
136
129
137 confirm_exit = CBool(True, config=True,
130 confirm_exit = CBool(True, config=True,
138 help="""
131 help="""
139 Set to display confirmation dialog on exit. You can always use 'exit' or 'quit',
132 Set to display confirmation dialog on exit. You can always use 'exit' or 'quit',
140 to force a direct exit without any confirmation.""",
133 to force a direct exit without any confirmation.""",
141 )
134 )
142
135
143 @property
136 def build_kernel_argv(self, argv=None):
144 def help_classes(self):
137 """build argv to be passed to kernel subprocess
145 """ConsoleApps can configure kernels on the command-line
146
138
147 But this shouldn't be written to a file
139 Override in subclasses if any args should be passed to the kernel
148 """
140 """
149 return self.classes + [IPKernelApp] + IPKernelApp.classes
141 self.kernel_argv = self.extra_args
150
151 def build_kernel_argv(self, argv=None):
152 """build argv to be passed to kernel subprocess"""
153 if argv is None:
154 argv = sys.argv[1:]
155 self.kernel_argv = swallow_argv(argv, self.frontend_aliases, self.frontend_flags)
156
142
157 def init_connection_file(self):
143 def init_connection_file(self):
158 """find the connection file, and load the info if found.
144 """find the connection file, and load the info if found.
159
145
160 The current working directory and the current profile's security
146 The current working directory and the current profile's security
161 directory will be searched for the file if it is not given by
147 directory will be searched for the file if it is not given by
162 absolute path.
148 absolute path.
163
149
164 When attempting to connect to an existing kernel and the `--existing`
150 When attempting to connect to an existing kernel and the `--existing`
165 argument does not match an existing file, it will be interpreted as a
151 argument does not match an existing file, it will be interpreted as a
166 fileglob, and the matching file in the current profile's security dir
152 fileglob, and the matching file in the current profile's security dir
167 with the latest access time will be used.
153 with the latest access time will be used.
168
154
169 After this method is called, self.connection_file contains the *full path*
155 After this method is called, self.connection_file contains the *full path*
170 to the connection file, never just its name.
156 to the connection file, never just its name.
171 """
157 """
172 if self.existing:
158 if self.existing:
173 try:
159 try:
174 cf = find_connection_file(self.existing)
160 cf = find_connection_file(self.existing)
175 except Exception:
161 except Exception:
176 self.log.critical("Could not find existing kernel connection file %s", self.existing)
162 self.log.critical("Could not find existing kernel connection file %s", self.existing)
177 self.exit(1)
163 self.exit(1)
178 self.log.debug("Connecting to existing kernel: %s" % cf)
164 self.log.debug("Connecting to existing kernel: %s" % cf)
179 self.connection_file = cf
165 self.connection_file = cf
180 else:
166 else:
181 # not existing, check if we are going to write the file
167 # not existing, check if we are going to write the file
182 # and ensure that self.connection_file is a full path, not just the shortname
168 # and ensure that self.connection_file is a full path, not just the shortname
183 try:
169 try:
184 cf = find_connection_file(self.connection_file)
170 cf = find_connection_file(self.connection_file)
185 except Exception:
171 except Exception:
186 # file might not exist
172 # file might not exist
187 if self.connection_file == os.path.basename(self.connection_file):
173 if self.connection_file == os.path.basename(self.connection_file):
188 # just shortname, put it in security dir
174 # just shortname, put it in security dir
189 cf = os.path.join(self.profile_dir.security_dir, self.connection_file)
175 cf = os.path.join(self.profile_dir.security_dir, self.connection_file)
190 else:
176 else:
191 cf = self.connection_file
177 cf = self.connection_file
192 self.connection_file = cf
178 self.connection_file = cf
193 try:
179 try:
194 self.connection_file = filefind(self.connection_file, ['.', self.profile_dir.security_dir])
180 self.connection_file = filefind(self.connection_file, ['.', self.profile_dir.security_dir])
195 except IOError:
181 except IOError:
196 self.log.debug("Connection File not found: %s", self.connection_file)
182 self.log.debug("Connection File not found: %s", self.connection_file)
197 return
183 return
198
184
199 # should load_connection_file only be used for existing?
185 # should load_connection_file only be used for existing?
200 # as it is now, this allows reusing ports if an existing
186 # as it is now, this allows reusing ports if an existing
201 # file is requested
187 # file is requested
202 try:
188 try:
203 self.load_connection_file()
189 self.load_connection_file()
204 except Exception:
190 except Exception:
205 self.log.error("Failed to load connection file: %r", self.connection_file, exc_info=True)
191 self.log.error("Failed to load connection file: %r", self.connection_file, exc_info=True)
206 self.exit(1)
192 self.exit(1)
207
193
208 def init_ssh(self):
194 def init_ssh(self):
209 """set up ssh tunnels, if needed."""
195 """set up ssh tunnels, if needed."""
210 if not self.existing or (not self.sshserver and not self.sshkey):
196 if not self.existing or (not self.sshserver and not self.sshkey):
211 return
197 return
212 self.load_connection_file()
198 self.load_connection_file()
213
199
214 transport = self.transport
200 transport = self.transport
215 ip = self.ip
201 ip = self.ip
216
202
217 if transport != 'tcp':
203 if transport != 'tcp':
218 self.log.error("Can only use ssh tunnels with TCP sockets, not %s", transport)
204 self.log.error("Can only use ssh tunnels with TCP sockets, not %s", transport)
219 sys.exit(-1)
205 sys.exit(-1)
220
206
221 if self.sshkey and not self.sshserver:
207 if self.sshkey and not self.sshserver:
222 # specifying just the key implies that we are connecting directly
208 # specifying just the key implies that we are connecting directly
223 self.sshserver = ip
209 self.sshserver = ip
224 ip = localhost()
210 ip = localhost()
225
211
226 # build connection dict for tunnels:
212 # build connection dict for tunnels:
227 info = dict(ip=ip,
213 info = dict(ip=ip,
228 shell_port=self.shell_port,
214 shell_port=self.shell_port,
229 iopub_port=self.iopub_port,
215 iopub_port=self.iopub_port,
230 stdin_port=self.stdin_port,
216 stdin_port=self.stdin_port,
231 hb_port=self.hb_port
217 hb_port=self.hb_port
232 )
218 )
233
219
234 self.log.info("Forwarding connections to %s via %s"%(ip, self.sshserver))
220 self.log.info("Forwarding connections to %s via %s"%(ip, self.sshserver))
235
221
236 # tunnels return a new set of ports, which will be on localhost:
222 # tunnels return a new set of ports, which will be on localhost:
237 self.ip = localhost()
223 self.ip = localhost()
238 try:
224 try:
239 newports = tunnel_to_kernel(info, self.sshserver, self.sshkey)
225 newports = tunnel_to_kernel(info, self.sshserver, self.sshkey)
240 except:
226 except:
241 # even catch KeyboardInterrupt
227 # even catch KeyboardInterrupt
242 self.log.error("Could not setup tunnels", exc_info=True)
228 self.log.error("Could not setup tunnels", exc_info=True)
243 self.exit(1)
229 self.exit(1)
244
230
245 self.shell_port, self.iopub_port, self.stdin_port, self.hb_port = newports
231 self.shell_port, self.iopub_port, self.stdin_port, self.hb_port = newports
246
232
247 cf = self.connection_file
233 cf = self.connection_file
248 base,ext = os.path.splitext(cf)
234 base,ext = os.path.splitext(cf)
249 base = os.path.basename(base)
235 base = os.path.basename(base)
250 self.connection_file = os.path.basename(base)+'-ssh'+ext
236 self.connection_file = os.path.basename(base)+'-ssh'+ext
251 self.log.info("To connect another client via this tunnel, use:")
237 self.log.info("To connect another client via this tunnel, use:")
252 self.log.info("--existing %s" % self.connection_file)
238 self.log.info("--existing %s" % self.connection_file)
253
239
254 def _new_connection_file(self):
240 def _new_connection_file(self):
255 cf = ''
241 cf = ''
256 while not cf:
242 while not cf:
257 # we don't need a 128b id to distinguish kernels, use more readable
243 # we don't need a 128b id to distinguish kernels, use more readable
258 # 48b node segment (12 hex chars). Users running more than 32k simultaneous
244 # 48b node segment (12 hex chars). Users running more than 32k simultaneous
259 # kernels can subclass.
245 # kernels can subclass.
260 ident = str(uuid.uuid4()).split('-')[-1]
246 ident = str(uuid.uuid4()).split('-')[-1]
261 cf = os.path.join(self.profile_dir.security_dir, 'kernel-%s.json' % ident)
247 cf = os.path.join(self.profile_dir.security_dir, 'kernel-%s.json' % ident)
262 # only keep if it's actually new. Protect against unlikely collision
248 # only keep if it's actually new. Protect against unlikely collision
263 # in 48b random search space
249 # in 48b random search space
264 cf = cf if not os.path.exists(cf) else ''
250 cf = cf if not os.path.exists(cf) else ''
265 return cf
251 return cf
266
252
267 def init_kernel_manager(self):
253 def init_kernel_manager(self):
268 # Don't let Qt or ZMQ swallow KeyboardInterupts.
254 # Don't let Qt or ZMQ swallow KeyboardInterupts.
269 if self.existing:
255 if self.existing:
270 self.kernel_manager = None
256 self.kernel_manager = None
271 return
257 return
272 signal.signal(signal.SIGINT, signal.SIG_DFL)
258 signal.signal(signal.SIGINT, signal.SIG_DFL)
273
259
274 # Create a KernelManager and start a kernel.
260 # Create a KernelManager and start a kernel.
275 try:
261 try:
276 self.kernel_manager = self.kernel_manager_class(
262 self.kernel_manager = self.kernel_manager_class(
277 ip=self.ip,
263 ip=self.ip,
278 session=self.session,
264 session=self.session,
279 transport=self.transport,
265 transport=self.transport,
280 shell_port=self.shell_port,
266 shell_port=self.shell_port,
281 iopub_port=self.iopub_port,
267 iopub_port=self.iopub_port,
282 stdin_port=self.stdin_port,
268 stdin_port=self.stdin_port,
283 hb_port=self.hb_port,
269 hb_port=self.hb_port,
284 connection_file=self.connection_file,
270 connection_file=self.connection_file,
285 kernel_name=self.kernel_name,
271 kernel_name=self.kernel_name,
286 parent=self,
272 parent=self,
287 ipython_dir=self.ipython_dir,
273 ipython_dir=self.ipython_dir,
288 )
274 )
289 except NoSuchKernel:
275 except NoSuchKernel:
290 self.log.critical("Could not find kernel %s", self.kernel_name)
276 self.log.critical("Could not find kernel %s", self.kernel_name)
291 self.exit(1)
277 self.exit(1)
292
278
293 self.kernel_manager.client_factory = self.kernel_client_class
279 self.kernel_manager.client_factory = self.kernel_client_class
294 # FIXME: remove special treatment of IPython kernels
280 # FIXME: remove special treatment of IPython kernels
295 kwargs = {}
281 kwargs = {}
296 if self.kernel_manager.ipython_kernel:
282 if self.kernel_manager.ipython_kernel:
297 kwargs['extra_arguments'] = self.kernel_argv
283 kwargs['extra_arguments'] = self.kernel_argv
298 self.kernel_manager.start_kernel(**kwargs)
284 self.kernel_manager.start_kernel(**kwargs)
299 atexit.register(self.kernel_manager.cleanup_ipc_files)
285 atexit.register(self.kernel_manager.cleanup_ipc_files)
300
286
301 if self.sshserver:
287 if self.sshserver:
302 # ssh, write new connection file
288 # ssh, write new connection file
303 self.kernel_manager.write_connection_file()
289 self.kernel_manager.write_connection_file()
304
290
305 # in case KM defaults / ssh writing changes things:
291 # in case KM defaults / ssh writing changes things:
306 km = self.kernel_manager
292 km = self.kernel_manager
307 self.shell_port=km.shell_port
293 self.shell_port=km.shell_port
308 self.iopub_port=km.iopub_port
294 self.iopub_port=km.iopub_port
309 self.stdin_port=km.stdin_port
295 self.stdin_port=km.stdin_port
310 self.hb_port=km.hb_port
296 self.hb_port=km.hb_port
311 self.connection_file = km.connection_file
297 self.connection_file = km.connection_file
312
298
313 atexit.register(self.kernel_manager.cleanup_connection_file)
299 atexit.register(self.kernel_manager.cleanup_connection_file)
314
300
315 def init_kernel_client(self):
301 def init_kernel_client(self):
316 if self.kernel_manager is not None:
302 if self.kernel_manager is not None:
317 self.kernel_client = self.kernel_manager.client()
303 self.kernel_client = self.kernel_manager.client()
318 else:
304 else:
319 self.kernel_client = self.kernel_client_class(
305 self.kernel_client = self.kernel_client_class(
320 session=self.session,
306 session=self.session,
321 ip=self.ip,
307 ip=self.ip,
322 transport=self.transport,
308 transport=self.transport,
323 shell_port=self.shell_port,
309 shell_port=self.shell_port,
324 iopub_port=self.iopub_port,
310 iopub_port=self.iopub_port,
325 stdin_port=self.stdin_port,
311 stdin_port=self.stdin_port,
326 hb_port=self.hb_port,
312 hb_port=self.hb_port,
327 connection_file=self.connection_file,
313 connection_file=self.connection_file,
328 parent=self,
314 parent=self,
329 )
315 )
330
316
331 self.kernel_client.start_channels()
317 self.kernel_client.start_channels()
332
318
333
319
334
320
335 def initialize(self, argv=None):
321 def initialize(self, argv=None):
336 """
322 """
337 Classes which mix this class in should call:
323 Classes which mix this class in should call:
338 IPythonConsoleApp.initialize(self,argv)
324 IPythonConsoleApp.initialize(self,argv)
339 """
325 """
340 self.init_connection_file()
326 self.init_connection_file()
341 self.init_ssh()
327 self.init_ssh()
342 self.init_kernel_manager()
328 self.init_kernel_manager()
343 self.init_kernel_client()
329 self.init_kernel_client()
344
330
@@ -1,395 +1,380 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """
3 """
4 The :class:`~IPython.core.application.Application` object for the command
4 The :class:`~IPython.core.application.Application` object for the command
5 line :command:`ipython` program.
5 line :command:`ipython` program.
6
7 Authors
8 -------
9
10 * Brian Granger
11 * Fernando Perez
12 * Min Ragan-Kelley
13 """
6 """
14
7
15 #-----------------------------------------------------------------------------
8 # Copyright (c) IPython Development Team.
16 # Copyright (C) 2008-2011 The IPython Development Team
9 # Distributed under the terms of the Modified BSD License.
17 #
18 # Distributed under the terms of the BSD License. The full license is in
19 # the file COPYING, distributed as part of this software.
20 #-----------------------------------------------------------------------------
21
22 #-----------------------------------------------------------------------------
23 # Imports
24 #-----------------------------------------------------------------------------
25
10
26 from __future__ import absolute_import
11 from __future__ import absolute_import
27 from __future__ import print_function
12 from __future__ import print_function
28
13
29 import logging
14 import logging
30 import os
15 import os
31 import sys
16 import sys
32
17
33 from IPython.config.loader import Config
18 from IPython.config.loader import Config
34 from IPython.config.application import boolean_flag, catch_config_error, Application
19 from IPython.config.application import boolean_flag, catch_config_error, Application
35 from IPython.core import release
20 from IPython.core import release
36 from IPython.core import usage
21 from IPython.core import usage
37 from IPython.core.completer import IPCompleter
22 from IPython.core.completer import IPCompleter
38 from IPython.core.crashhandler import CrashHandler
23 from IPython.core.crashhandler import CrashHandler
39 from IPython.core.formatters import PlainTextFormatter
24 from IPython.core.formatters import PlainTextFormatter
40 from IPython.core.history import HistoryManager
25 from IPython.core.history import HistoryManager
41 from IPython.core.prompts import PromptManager
26 from IPython.core.prompts import PromptManager
42 from IPython.core.application import (
27 from IPython.core.application import (
43 ProfileDir, BaseIPythonApplication, base_flags, base_aliases
28 ProfileDir, BaseIPythonApplication, base_flags, base_aliases
44 )
29 )
45 from IPython.core.magics import ScriptMagics
30 from IPython.core.magics import ScriptMagics
46 from IPython.core.shellapp import (
31 from IPython.core.shellapp import (
47 InteractiveShellApp, shell_flags, shell_aliases
32 InteractiveShellApp, shell_flags, shell_aliases
48 )
33 )
49 from IPython.extensions.storemagic import StoreMagics
34 from IPython.extensions.storemagic import StoreMagics
50 from IPython.terminal.interactiveshell import TerminalInteractiveShell
35 from IPython.terminal.interactiveshell import TerminalInteractiveShell
51 from IPython.utils import warn
36 from IPython.utils import warn
52 from IPython.utils.path import get_ipython_dir, check_for_old_config
37 from IPython.utils.path import get_ipython_dir, check_for_old_config
53 from IPython.utils.traitlets import (
38 from IPython.utils.traitlets import (
54 Bool, List, Dict,
39 Bool, List, Dict,
55 )
40 )
56
41
57 #-----------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
58 # Globals, utilities and helpers
43 # Globals, utilities and helpers
59 #-----------------------------------------------------------------------------
44 #-----------------------------------------------------------------------------
60
45
61 _examples = """
46 _examples = """
62 ipython --matplotlib # enable matplotlib integration
47 ipython --matplotlib # enable matplotlib integration
63 ipython --matplotlib=qt # enable matplotlib integration with qt4 backend
48 ipython --matplotlib=qt # enable matplotlib integration with qt4 backend
64
49
65 ipython --log-level=DEBUG # set logging to DEBUG
50 ipython --log-level=DEBUG # set logging to DEBUG
66 ipython --profile=foo # start with profile foo
51 ipython --profile=foo # start with profile foo
67
52
68 ipython qtconsole # start the qtconsole GUI application
53 ipython qtconsole # start the qtconsole GUI application
69 ipython help qtconsole # show the help for the qtconsole subcmd
54 ipython help qtconsole # show the help for the qtconsole subcmd
70
55
71 ipython console # start the terminal-based console application
56 ipython console # start the terminal-based console application
72 ipython help console # show the help for the console subcmd
57 ipython help console # show the help for the console subcmd
73
58
74 ipython notebook # start the IPython notebook
59 ipython notebook # start the IPython notebook
75 ipython help notebook # show the help for the notebook subcmd
60 ipython help notebook # show the help for the notebook subcmd
76
61
77 ipython profile create foo # create profile foo w/ default config files
62 ipython profile create foo # create profile foo w/ default config files
78 ipython help profile # show the help for the profile subcmd
63 ipython help profile # show the help for the profile subcmd
79
64
80 ipython locate # print the path to the IPython directory
65 ipython locate # print the path to the IPython directory
81 ipython locate profile foo # print the path to the directory for profile `foo`
66 ipython locate profile foo # print the path to the directory for profile `foo`
82
67
83 ipython nbconvert # convert notebooks to/from other formats
68 ipython nbconvert # convert notebooks to/from other formats
84 """
69 """
85
70
86 #-----------------------------------------------------------------------------
71 #-----------------------------------------------------------------------------
87 # Crash handler for this application
72 # Crash handler for this application
88 #-----------------------------------------------------------------------------
73 #-----------------------------------------------------------------------------
89
74
90 class IPAppCrashHandler(CrashHandler):
75 class IPAppCrashHandler(CrashHandler):
91 """sys.excepthook for IPython itself, leaves a detailed report on disk."""
76 """sys.excepthook for IPython itself, leaves a detailed report on disk."""
92
77
93 def __init__(self, app):
78 def __init__(self, app):
94 contact_name = release.author
79 contact_name = release.author
95 contact_email = release.author_email
80 contact_email = release.author_email
96 bug_tracker = 'https://github.com/ipython/ipython/issues'
81 bug_tracker = 'https://github.com/ipython/ipython/issues'
97 super(IPAppCrashHandler,self).__init__(
82 super(IPAppCrashHandler,self).__init__(
98 app, contact_name, contact_email, bug_tracker
83 app, contact_name, contact_email, bug_tracker
99 )
84 )
100
85
101 def make_report(self,traceback):
86 def make_report(self,traceback):
102 """Return a string containing a crash report."""
87 """Return a string containing a crash report."""
103
88
104 sec_sep = self.section_sep
89 sec_sep = self.section_sep
105 # Start with parent report
90 # Start with parent report
106 report = [super(IPAppCrashHandler, self).make_report(traceback)]
91 report = [super(IPAppCrashHandler, self).make_report(traceback)]
107 # Add interactive-specific info we may have
92 # Add interactive-specific info we may have
108 rpt_add = report.append
93 rpt_add = report.append
109 try:
94 try:
110 rpt_add(sec_sep+"History of session input:")
95 rpt_add(sec_sep+"History of session input:")
111 for line in self.app.shell.user_ns['_ih']:
96 for line in self.app.shell.user_ns['_ih']:
112 rpt_add(line)
97 rpt_add(line)
113 rpt_add('\n*** Last line of input (may not be in above history):\n')
98 rpt_add('\n*** Last line of input (may not be in above history):\n')
114 rpt_add(self.app.shell._last_input_line+'\n')
99 rpt_add(self.app.shell._last_input_line+'\n')
115 except:
100 except:
116 pass
101 pass
117
102
118 return ''.join(report)
103 return ''.join(report)
119
104
120 #-----------------------------------------------------------------------------
105 #-----------------------------------------------------------------------------
121 # Aliases and Flags
106 # Aliases and Flags
122 #-----------------------------------------------------------------------------
107 #-----------------------------------------------------------------------------
123 flags = dict(base_flags)
108 flags = dict(base_flags)
124 flags.update(shell_flags)
109 flags.update(shell_flags)
125 frontend_flags = {}
110 frontend_flags = {}
126 addflag = lambda *args: frontend_flags.update(boolean_flag(*args))
111 addflag = lambda *args: frontend_flags.update(boolean_flag(*args))
127 addflag('autoedit-syntax', 'TerminalInteractiveShell.autoedit_syntax',
112 addflag('autoedit-syntax', 'TerminalInteractiveShell.autoedit_syntax',
128 'Turn on auto editing of files with syntax errors.',
113 'Turn on auto editing of files with syntax errors.',
129 'Turn off auto editing of files with syntax errors.'
114 'Turn off auto editing of files with syntax errors.'
130 )
115 )
131 addflag('banner', 'TerminalIPythonApp.display_banner',
116 addflag('banner', 'TerminalIPythonApp.display_banner',
132 "Display a banner upon starting IPython.",
117 "Display a banner upon starting IPython.",
133 "Don't display a banner upon starting IPython."
118 "Don't display a banner upon starting IPython."
134 )
119 )
135 addflag('confirm-exit', 'TerminalInteractiveShell.confirm_exit',
120 addflag('confirm-exit', 'TerminalInteractiveShell.confirm_exit',
136 """Set to confirm when you try to exit IPython with an EOF (Control-D
121 """Set to confirm when you try to exit IPython with an EOF (Control-D
137 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
122 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
138 you can force a direct exit without any confirmation.""",
123 you can force a direct exit without any confirmation.""",
139 "Don't prompt the user when exiting."
124 "Don't prompt the user when exiting."
140 )
125 )
141 addflag('term-title', 'TerminalInteractiveShell.term_title',
126 addflag('term-title', 'TerminalInteractiveShell.term_title',
142 "Enable auto setting the terminal title.",
127 "Enable auto setting the terminal title.",
143 "Disable auto setting the terminal title."
128 "Disable auto setting the terminal title."
144 )
129 )
145 classic_config = Config()
130 classic_config = Config()
146 classic_config.InteractiveShell.cache_size = 0
131 classic_config.InteractiveShell.cache_size = 0
147 classic_config.PlainTextFormatter.pprint = False
132 classic_config.PlainTextFormatter.pprint = False
148 classic_config.PromptManager.in_template = '>>> '
133 classic_config.PromptManager.in_template = '>>> '
149 classic_config.PromptManager.in2_template = '... '
134 classic_config.PromptManager.in2_template = '... '
150 classic_config.PromptManager.out_template = ''
135 classic_config.PromptManager.out_template = ''
151 classic_config.InteractiveShell.separate_in = ''
136 classic_config.InteractiveShell.separate_in = ''
152 classic_config.InteractiveShell.separate_out = ''
137 classic_config.InteractiveShell.separate_out = ''
153 classic_config.InteractiveShell.separate_out2 = ''
138 classic_config.InteractiveShell.separate_out2 = ''
154 classic_config.InteractiveShell.colors = 'NoColor'
139 classic_config.InteractiveShell.colors = 'NoColor'
155 classic_config.InteractiveShell.xmode = 'Plain'
140 classic_config.InteractiveShell.xmode = 'Plain'
156
141
157 frontend_flags['classic']=(
142 frontend_flags['classic']=(
158 classic_config,
143 classic_config,
159 "Gives IPython a similar feel to the classic Python prompt."
144 "Gives IPython a similar feel to the classic Python prompt."
160 )
145 )
161 # # log doesn't make so much sense this way anymore
146 # # log doesn't make so much sense this way anymore
162 # paa('--log','-l',
147 # paa('--log','-l',
163 # action='store_true', dest='InteractiveShell.logstart',
148 # action='store_true', dest='InteractiveShell.logstart',
164 # help="Start logging to the default log file (./ipython_log.py).")
149 # help="Start logging to the default log file (./ipython_log.py).")
165 #
150 #
166 # # quick is harder to implement
151 # # quick is harder to implement
167 frontend_flags['quick']=(
152 frontend_flags['quick']=(
168 {'TerminalIPythonApp' : {'quick' : True}},
153 {'TerminalIPythonApp' : {'quick' : True}},
169 "Enable quick startup with no config files."
154 "Enable quick startup with no config files."
170 )
155 )
171
156
172 frontend_flags['i'] = (
157 frontend_flags['i'] = (
173 {'TerminalIPythonApp' : {'force_interact' : True}},
158 {'TerminalIPythonApp' : {'force_interact' : True}},
174 """If running code from the command line, become interactive afterwards."""
159 """If running code from the command line, become interactive afterwards."""
175 )
160 )
176 flags.update(frontend_flags)
161 flags.update(frontend_flags)
177
162
178 aliases = dict(base_aliases)
163 aliases = dict(base_aliases)
179 aliases.update(shell_aliases)
164 aliases.update(shell_aliases)
180
165
181 #-----------------------------------------------------------------------------
166 #-----------------------------------------------------------------------------
182 # Main classes and functions
167 # Main classes and functions
183 #-----------------------------------------------------------------------------
168 #-----------------------------------------------------------------------------
184
169
185
170
186 class LocateIPythonApp(BaseIPythonApplication):
171 class LocateIPythonApp(BaseIPythonApplication):
187 description = """print the path to the IPython dir"""
172 description = """print the path to the IPython dir"""
188 subcommands = Dict(dict(
173 subcommands = Dict(dict(
189 profile=('IPython.core.profileapp.ProfileLocate',
174 profile=('IPython.core.profileapp.ProfileLocate',
190 "print the path to an IPython profile directory",
175 "print the path to an IPython profile directory",
191 ),
176 ),
192 ))
177 ))
193 def start(self):
178 def start(self):
194 if self.subapp is not None:
179 if self.subapp is not None:
195 return self.subapp.start()
180 return self.subapp.start()
196 else:
181 else:
197 print(self.ipython_dir)
182 print(self.ipython_dir)
198
183
199
184
200 class TerminalIPythonApp(BaseIPythonApplication, InteractiveShellApp):
185 class TerminalIPythonApp(BaseIPythonApplication, InteractiveShellApp):
201 name = u'ipython'
186 name = u'ipython'
202 description = usage.cl_usage
187 description = usage.cl_usage
203 crash_handler_class = IPAppCrashHandler
188 crash_handler_class = IPAppCrashHandler
204 examples = _examples
189 examples = _examples
205
190
206 flags = Dict(flags)
191 flags = Dict(flags)
207 aliases = Dict(aliases)
192 aliases = Dict(aliases)
208 classes = List()
193 classes = List()
209 def _classes_default(self):
194 def _classes_default(self):
210 """This has to be in a method, for TerminalIPythonApp to be available."""
195 """This has to be in a method, for TerminalIPythonApp to be available."""
211 return [
196 return [
212 InteractiveShellApp, # ShellApp comes before TerminalApp, because
197 InteractiveShellApp, # ShellApp comes before TerminalApp, because
213 self.__class__, # it will also affect subclasses (e.g. QtConsole)
198 self.__class__, # it will also affect subclasses (e.g. QtConsole)
214 TerminalInteractiveShell,
199 TerminalInteractiveShell,
215 PromptManager,
200 PromptManager,
216 HistoryManager,
201 HistoryManager,
217 ProfileDir,
202 ProfileDir,
218 PlainTextFormatter,
203 PlainTextFormatter,
219 IPCompleter,
204 IPCompleter,
220 ScriptMagics,
205 ScriptMagics,
221 StoreMagics,
206 StoreMagics,
222 ]
207 ]
223
208
224 subcommands = dict(
209 subcommands = dict(
225 qtconsole=('IPython.qt.console.qtconsoleapp.IPythonQtConsoleApp',
210 qtconsole=('IPython.qt.console.qtconsoleapp.IPythonQtConsoleApp',
226 """Launch the IPython Qt Console."""
211 """Launch the IPython Qt Console."""
227 ),
212 ),
228 notebook=('IPython.html.notebookapp.NotebookApp',
213 notebook=('IPython.html.notebookapp.NotebookApp',
229 """Launch the IPython HTML Notebook Server."""
214 """Launch the IPython HTML Notebook Server."""
230 ),
215 ),
231 profile = ("IPython.core.profileapp.ProfileApp",
216 profile = ("IPython.core.profileapp.ProfileApp",
232 "Create and manage IPython profiles."
217 "Create and manage IPython profiles."
233 ),
218 ),
234 kernel = ("IPython.kernel.zmq.kernelapp.IPKernelApp",
219 kernel = ("IPython.kernel.zmq.kernelapp.IPKernelApp",
235 "Start a kernel without an attached frontend."
220 "Start a kernel without an attached frontend."
236 ),
221 ),
237 console=('IPython.terminal.console.app.ZMQTerminalIPythonApp',
222 console=('IPython.terminal.console.app.ZMQTerminalIPythonApp',
238 """Launch the IPython terminal-based Console."""
223 """Launch the IPython terminal-based Console."""
239 ),
224 ),
240 locate=('IPython.terminal.ipapp.LocateIPythonApp',
225 locate=('IPython.terminal.ipapp.LocateIPythonApp',
241 LocateIPythonApp.description
226 LocateIPythonApp.description
242 ),
227 ),
243 history=('IPython.core.historyapp.HistoryApp',
228 history=('IPython.core.historyapp.HistoryApp',
244 "Manage the IPython history database."
229 "Manage the IPython history database."
245 ),
230 ),
246 nbconvert=('IPython.nbconvert.nbconvertapp.NbConvertApp',
231 nbconvert=('IPython.nbconvert.nbconvertapp.NbConvertApp',
247 "Convert notebooks to/from other formats."
232 "Convert notebooks to/from other formats."
248 ),
233 ),
249 trust=('IPython.nbformat.sign.TrustNotebookApp',
234 trust=('IPython.nbformat.sign.TrustNotebookApp',
250 "Sign notebooks to trust their potentially unsafe contents at load."
235 "Sign notebooks to trust their potentially unsafe contents at load."
251 ),
236 ),
252 kernelspec=('IPython.kernel.kernelspecapp.KernelSpecApp',
237 kernelspec=('IPython.kernel.kernelspecapp.KernelSpecApp',
253 "Manage IPython kernel specifications."
238 "Manage IPython kernel specifications."
254 ),
239 ),
255 )
240 )
256 subcommands['install-nbextension'] = (
241 subcommands['install-nbextension'] = (
257 "IPython.html.nbextensions.NBExtensionApp",
242 "IPython.html.nbextensions.NBExtensionApp",
258 "Install IPython notebook extension files"
243 "Install IPython notebook extension files"
259 )
244 )
260
245
261 # *do* autocreate requested profile, but don't create the config file.
246 # *do* autocreate requested profile, but don't create the config file.
262 auto_create=Bool(True)
247 auto_create=Bool(True)
263 # configurables
248 # configurables
264 ignore_old_config=Bool(False, config=True,
249 ignore_old_config=Bool(False, config=True,
265 help="Suppress warning messages about legacy config files"
250 help="Suppress warning messages about legacy config files"
266 )
251 )
267 quick = Bool(False, config=True,
252 quick = Bool(False, config=True,
268 help="""Start IPython quickly by skipping the loading of config files."""
253 help="""Start IPython quickly by skipping the loading of config files."""
269 )
254 )
270 def _quick_changed(self, name, old, new):
255 def _quick_changed(self, name, old, new):
271 if new:
256 if new:
272 self.load_config_file = lambda *a, **kw: None
257 self.load_config_file = lambda *a, **kw: None
273 self.ignore_old_config=True
258 self.ignore_old_config=True
274
259
275 display_banner = Bool(True, config=True,
260 display_banner = Bool(True, config=True,
276 help="Whether to display a banner upon starting IPython."
261 help="Whether to display a banner upon starting IPython."
277 )
262 )
278
263
279 # if there is code of files to run from the cmd line, don't interact
264 # if there is code of files to run from the cmd line, don't interact
280 # unless the --i flag (App.force_interact) is true.
265 # unless the --i flag (App.force_interact) is true.
281 force_interact = Bool(False, config=True,
266 force_interact = Bool(False, config=True,
282 help="""If a command or file is given via the command-line,
267 help="""If a command or file is given via the command-line,
283 e.g. 'ipython foo.py', start an interactive shell after executing the
268 e.g. 'ipython foo.py', start an interactive shell after executing the
284 file or command."""
269 file or command."""
285 )
270 )
286 def _force_interact_changed(self, name, old, new):
271 def _force_interact_changed(self, name, old, new):
287 if new:
272 if new:
288 self.interact = True
273 self.interact = True
289
274
290 def _file_to_run_changed(self, name, old, new):
275 def _file_to_run_changed(self, name, old, new):
291 if new:
276 if new:
292 self.something_to_run = True
277 self.something_to_run = True
293 if new and not self.force_interact:
278 if new and not self.force_interact:
294 self.interact = False
279 self.interact = False
295 _code_to_run_changed = _file_to_run_changed
280 _code_to_run_changed = _file_to_run_changed
296 _module_to_run_changed = _file_to_run_changed
281 _module_to_run_changed = _file_to_run_changed
297
282
298 # internal, not-configurable
283 # internal, not-configurable
299 interact=Bool(True)
284 interact=Bool(True)
300 something_to_run=Bool(False)
285 something_to_run=Bool(False)
301
286
302 def parse_command_line(self, argv=None):
287 def parse_command_line(self, argv=None):
303 """override to allow old '-pylab' flag with deprecation warning"""
288 """override to allow old '-pylab' flag with deprecation warning"""
304
289
305 argv = sys.argv[1:] if argv is None else argv
290 argv = sys.argv[1:] if argv is None else argv
306
291
307 if '-pylab' in argv:
292 if '-pylab' in argv:
308 # deprecated `-pylab` given,
293 # deprecated `-pylab` given,
309 # warn and transform into current syntax
294 # warn and transform into current syntax
310 argv = argv[:] # copy, don't clobber
295 argv = argv[:] # copy, don't clobber
311 idx = argv.index('-pylab')
296 idx = argv.index('-pylab')
312 warn.warn("`-pylab` flag has been deprecated.\n"
297 warn.warn("`-pylab` flag has been deprecated.\n"
313 " Use `--matplotlib <backend>` and import pylab manually.")
298 " Use `--matplotlib <backend>` and import pylab manually.")
314 argv[idx] = '--pylab'
299 argv[idx] = '--pylab'
315
300
316 return super(TerminalIPythonApp, self).parse_command_line(argv)
301 return super(TerminalIPythonApp, self).parse_command_line(argv)
317
302
318 @catch_config_error
303 @catch_config_error
319 def initialize(self, argv=None):
304 def initialize(self, argv=None):
320 """Do actions after construct, but before starting the app."""
305 """Do actions after construct, but before starting the app."""
321 super(TerminalIPythonApp, self).initialize(argv)
306 super(TerminalIPythonApp, self).initialize(argv)
322 if self.subapp is not None:
307 if self.subapp is not None:
323 # don't bother initializing further, starting subapp
308 # don't bother initializing further, starting subapp
324 return
309 return
325 if not self.ignore_old_config:
310 if not self.ignore_old_config:
326 check_for_old_config(self.ipython_dir)
311 check_for_old_config(self.ipython_dir)
327 # print self.extra_args
312 # print self.extra_args
328 if self.extra_args and not self.something_to_run:
313 if self.extra_args and not self.something_to_run:
329 self.file_to_run = self.extra_args[0]
314 self.file_to_run = self.extra_args[0]
330 self.init_path()
315 self.init_path()
331 # create the shell
316 # create the shell
332 self.init_shell()
317 self.init_shell()
333 # and draw the banner
318 # and draw the banner
334 self.init_banner()
319 self.init_banner()
335 # Now a variety of things that happen after the banner is printed.
320 # Now a variety of things that happen after the banner is printed.
336 self.init_gui_pylab()
321 self.init_gui_pylab()
337 self.init_extensions()
322 self.init_extensions()
338 self.init_code()
323 self.init_code()
339
324
340 def init_shell(self):
325 def init_shell(self):
341 """initialize the InteractiveShell instance"""
326 """initialize the InteractiveShell instance"""
342 # Create an InteractiveShell instance.
327 # Create an InteractiveShell instance.
343 # shell.display_banner should always be False for the terminal
328 # shell.display_banner should always be False for the terminal
344 # based app, because we call shell.show_banner() by hand below
329 # based app, because we call shell.show_banner() by hand below
345 # so the banner shows *before* all extension loading stuff.
330 # so the banner shows *before* all extension loading stuff.
346 self.shell = TerminalInteractiveShell.instance(parent=self,
331 self.shell = TerminalInteractiveShell.instance(parent=self,
347 display_banner=False, profile_dir=self.profile_dir,
332 display_banner=False, profile_dir=self.profile_dir,
348 ipython_dir=self.ipython_dir, user_ns=self.user_ns)
333 ipython_dir=self.ipython_dir, user_ns=self.user_ns)
349 self.shell.configurables.append(self)
334 self.shell.configurables.append(self)
350
335
351 def init_banner(self):
336 def init_banner(self):
352 """optionally display the banner"""
337 """optionally display the banner"""
353 if self.display_banner and self.interact:
338 if self.display_banner and self.interact:
354 self.shell.show_banner()
339 self.shell.show_banner()
355 # Make sure there is a space below the banner.
340 # Make sure there is a space below the banner.
356 if self.log_level <= logging.INFO: print()
341 if self.log_level <= logging.INFO: print()
357
342
358 def _pylab_changed(self, name, old, new):
343 def _pylab_changed(self, name, old, new):
359 """Replace --pylab='inline' with --pylab='auto'"""
344 """Replace --pylab='inline' with --pylab='auto'"""
360 if new == 'inline':
345 if new == 'inline':
361 warn.warn("'inline' not available as pylab backend, "
346 warn.warn("'inline' not available as pylab backend, "
362 "using 'auto' instead.")
347 "using 'auto' instead.")
363 self.pylab = 'auto'
348 self.pylab = 'auto'
364
349
365 def start(self):
350 def start(self):
366 if self.subapp is not None:
351 if self.subapp is not None:
367 return self.subapp.start()
352 return self.subapp.start()
368 # perform any prexec steps:
353 # perform any prexec steps:
369 if self.interact:
354 if self.interact:
370 self.log.debug("Starting IPython's mainloop...")
355 self.log.debug("Starting IPython's mainloop...")
371 self.shell.mainloop()
356 self.shell.mainloop()
372 else:
357 else:
373 self.log.debug("IPython not interactive...")
358 self.log.debug("IPython not interactive...")
374
359
375 def load_default_config(ipython_dir=None):
360 def load_default_config(ipython_dir=None):
376 """Load the default config file from the default ipython_dir.
361 """Load the default config file from the default ipython_dir.
377
362
378 This is useful for embedded shells.
363 This is useful for embedded shells.
379 """
364 """
380 if ipython_dir is None:
365 if ipython_dir is None:
381 ipython_dir = get_ipython_dir()
366 ipython_dir = get_ipython_dir()
382
367
383 profile_dir = os.path.join(ipython_dir, 'profile_default')
368 profile_dir = os.path.join(ipython_dir, 'profile_default')
384
369
385 config = Config()
370 config = Config()
386 for cf in Application._load_config_files("ipython_config", path=profile_dir):
371 for cf in Application._load_config_files("ipython_config", path=profile_dir):
387 config.update(cf)
372 config.update(cf)
388
373
389 return config
374 return config
390
375
391 launch_new_instance = TerminalIPythonApp.launch_instance
376 launch_new_instance = TerminalIPythonApp.launch_instance
392
377
393
378
394 if __name__ == '__main__':
379 if __name__ == '__main__':
395 launch_new_instance()
380 launch_new_instance()
@@ -1,149 +1,145 b''
1 """ A minimal application using the ZMQ-based terminal IPython frontend.
1 """ A minimal application using the ZMQ-based terminal IPython frontend.
2
2
3 This is not a complete console app, as subprocess will not be able to receive
3 This is not a complete console app, as subprocess will not be able to receive
4 input, there is no real readline support, among other limitations.
4 input, there is no real readline support, among other limitations.
5
6 Authors:
7
8 * Min RK
9 * Paul Ivanov
10
11 """
5 """
12
6
13 #-----------------------------------------------------------------------------
7 # Copyright (c) IPython Development Team.
14 # Imports
8 # Distributed under the terms of the Modified BSD License.
15 #-----------------------------------------------------------------------------
9
16 import signal
10 import signal
17
11
18 from IPython.terminal.ipapp import TerminalIPythonApp, frontend_flags as term_flags
12 from IPython.terminal.ipapp import TerminalIPythonApp, frontend_flags as term_flags
19
13
20 from IPython.utils.traitlets import (
14 from IPython.utils.traitlets import (
21 Dict, Any
15 Dict, Any
22 )
16 )
23 from IPython.utils.warn import error
17 from IPython.utils.warn import error
24
18
25 from IPython.consoleapp import (
19 from IPython.consoleapp import (
26 IPythonConsoleApp, app_aliases, app_flags, aliases, flags
20 IPythonConsoleApp, app_aliases, app_flags, aliases, flags
27 )
21 )
28
22
29 from IPython.terminal.console.interactiveshell import ZMQTerminalInteractiveShell
23 from IPython.terminal.console.interactiveshell import ZMQTerminalInteractiveShell
30
24
31 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
32 # Globals
26 # Globals
33 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
34
28
35 _examples = """
29 _examples = """
36 ipython console # start the ZMQ-based console
30 ipython console # start the ZMQ-based console
37 ipython console --existing # connect to an existing ipython session
31 ipython console --existing # connect to an existing ipython session
38 """
32 """
39
33
40 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
41 # Flags and Aliases
35 # Flags and Aliases
42 #-----------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
43
37
44 # copy flags from mixin:
38 # copy flags from mixin:
45 flags = dict(flags)
39 flags = dict(flags)
46 # start with mixin frontend flags:
40 # start with mixin frontend flags:
47 frontend_flags = dict(app_flags)
41 frontend_flags = dict(app_flags)
48 # add TerminalIPApp flags:
42 # add TerminalIPApp flags:
49 frontend_flags.update(term_flags)
43 frontend_flags.update(term_flags)
50 # disable quick startup, as it won't propagate to the kernel anyway
44 # disable quick startup, as it won't propagate to the kernel anyway
51 frontend_flags.pop('quick')
45 frontend_flags.pop('quick')
52 # update full dict with frontend flags:
46 # update full dict with frontend flags:
53 flags.update(frontend_flags)
47 flags.update(frontend_flags)
54
48
55 # copy flags from mixin
49 # copy flags from mixin
56 aliases = dict(aliases)
50 aliases = dict(aliases)
57 # start with mixin frontend flags
51 # start with mixin frontend flags
58 frontend_aliases = dict(app_aliases)
52 frontend_aliases = dict(app_aliases)
59 # load updated frontend flags into full dict
53 # load updated frontend flags into full dict
60 aliases.update(frontend_aliases)
54 aliases.update(frontend_aliases)
61
55
62 # get flags&aliases into sets, and remove a couple that
56 # get flags&aliases into sets, and remove a couple that
63 # shouldn't be scrubbed from backend flags:
57 # shouldn't be scrubbed from backend flags:
64 frontend_aliases = set(frontend_aliases.keys())
58 frontend_aliases = set(frontend_aliases.keys())
65 frontend_flags = set(frontend_flags.keys())
59 frontend_flags = set(frontend_flags.keys())
66
60
67
61
68 #-----------------------------------------------------------------------------
62 #-----------------------------------------------------------------------------
69 # Classes
63 # Classes
70 #-----------------------------------------------------------------------------
64 #-----------------------------------------------------------------------------
71
65
72
66
73 class ZMQTerminalIPythonApp(TerminalIPythonApp, IPythonConsoleApp):
67 class ZMQTerminalIPythonApp(TerminalIPythonApp, IPythonConsoleApp):
74 name = "ipython-console"
68 name = "ipython-console"
75 """Start a terminal frontend to the IPython zmq kernel."""
69 """Start a terminal frontend to the IPython zmq kernel."""
76
70
77 description = """
71 description = """
78 The IPython terminal-based Console.
72 The IPython terminal-based Console.
79
73
80 This launches a Console application inside a terminal.
74 This launches a Console application inside a terminal.
81
75
82 The Console supports various extra features beyond the traditional
76 The Console supports various extra features beyond the traditional
83 single-process Terminal IPython shell, such as connecting to an
77 single-process Terminal IPython shell, such as connecting to an
84 existing ipython session, via:
78 existing ipython session, via:
85
79
86 ipython console --existing
80 ipython console --existing
87
81
88 where the previous session could have been created by another ipython
82 where the previous session could have been created by another ipython
89 console, an ipython qtconsole, or by opening an ipython notebook.
83 console, an ipython qtconsole, or by opening an ipython notebook.
90
84
91 """
85 """
92 examples = _examples
86 examples = _examples
93
87
94 classes = [ZMQTerminalInteractiveShell] + IPythonConsoleApp.classes
88 classes = [ZMQTerminalInteractiveShell] + IPythonConsoleApp.classes
95 flags = Dict(flags)
89 flags = Dict(flags)
96 aliases = Dict(aliases)
90 aliases = Dict(aliases)
97 frontend_aliases = Any(frontend_aliases)
91 frontend_aliases = Any(frontend_aliases)
98 frontend_flags = Any(frontend_flags)
92 frontend_flags = Any(frontend_flags)
99
93
100 subcommands = Dict()
94 subcommands = Dict()
101
95
96 force_interact = True
97
102 def parse_command_line(self, argv=None):
98 def parse_command_line(self, argv=None):
103 super(ZMQTerminalIPythonApp, self).parse_command_line(argv)
99 super(ZMQTerminalIPythonApp, self).parse_command_line(argv)
104 self.build_kernel_argv(argv)
100 self.build_kernel_argv(self.extra_args)
105
101
106 def init_shell(self):
102 def init_shell(self):
107 IPythonConsoleApp.initialize(self)
103 IPythonConsoleApp.initialize(self)
108 # relay sigint to kernel
104 # relay sigint to kernel
109 signal.signal(signal.SIGINT, self.handle_sigint)
105 signal.signal(signal.SIGINT, self.handle_sigint)
110 self.shell = ZMQTerminalInteractiveShell.instance(parent=self,
106 self.shell = ZMQTerminalInteractiveShell.instance(parent=self,
111 display_banner=False, profile_dir=self.profile_dir,
107 display_banner=False, profile_dir=self.profile_dir,
112 ipython_dir=self.ipython_dir,
108 ipython_dir=self.ipython_dir,
113 manager=self.kernel_manager,
109 manager=self.kernel_manager,
114 client=self.kernel_client,
110 client=self.kernel_client,
115 )
111 )
116
112
117 def init_gui_pylab(self):
113 def init_gui_pylab(self):
118 # no-op, because we don't want to import matplotlib in the frontend.
114 # no-op, because we don't want to import matplotlib in the frontend.
119 pass
115 pass
120
116
121 def handle_sigint(self, *args):
117 def handle_sigint(self, *args):
122 if self.shell._executing:
118 if self.shell._executing:
123 if self.kernel_manager:
119 if self.kernel_manager:
124 # interrupt already gets passed to subprocess by signal handler.
120 # interrupt already gets passed to subprocess by signal handler.
125 # Only if we prevent that should we need to explicitly call
121 # Only if we prevent that should we need to explicitly call
126 # interrupt_kernel, until which time, this would result in a
122 # interrupt_kernel, until which time, this would result in a
127 # double-interrupt:
123 # double-interrupt:
128 # self.kernel_manager.interrupt_kernel()
124 # self.kernel_manager.interrupt_kernel()
129 pass
125 pass
130 else:
126 else:
131 self.shell.write_err('\n')
127 self.shell.write_err('\n')
132 error("Cannot interrupt kernels we didn't start.\n")
128 error("Cannot interrupt kernels we didn't start.\n")
133 else:
129 else:
134 # raise the KeyboardInterrupt if we aren't waiting for execution,
130 # raise the KeyboardInterrupt if we aren't waiting for execution,
135 # so that the interact loop advances, and prompt is redrawn, etc.
131 # so that the interact loop advances, and prompt is redrawn, etc.
136 raise KeyboardInterrupt
132 raise KeyboardInterrupt
137
133
138
134
139 def init_code(self):
135 def init_code(self):
140 # no-op in the frontend, code gets run in the backend
136 # no-op in the frontend, code gets run in the backend
141 pass
137 pass
142
138
143
139
144 launch_new_instance = ZMQTerminalIPythonApp.launch_instance
140 launch_new_instance = ZMQTerminalIPythonApp.launch_instance
145
141
146
142
147 if __name__ == '__main__':
143 if __name__ == '__main__':
148 launch_new_instance()
144 launch_new_instance()
149
145
@@ -1,386 +1,379 b''
1 """ A minimal application using the Qt console-style IPython frontend.
1 """ A minimal application using the Qt console-style IPython frontend.
2
2
3 This is not a complete console app, as subprocess will not be able to receive
3 This is not a complete console app, as subprocess will not be able to receive
4 input, there is no real readline support, among other limitations.
4 input, there is no real readline support, among other limitations.
5 """
5 """
6
6
7 # Copyright (c) IPython Development Team.
7 # Copyright (c) IPython Development Team.
8 # Distributed under the terms of the Modified BSD License.
8 # Distributed under the terms of the Modified BSD License.
9
9
10 #-----------------------------------------------------------------------------
11 # Imports
12 #-----------------------------------------------------------------------------
13
14 # stdlib imports
15 import os
10 import os
16 import signal
11 import signal
17 import sys
12 import sys
18
13
19 # If run on Windows, install an exception hook which pops up a
14 # If run on Windows, install an exception hook which pops up a
20 # message box. Pythonw.exe hides the console, so without this
15 # message box. Pythonw.exe hides the console, so without this
21 # the application silently fails to load.
16 # the application silently fails to load.
22 #
17 #
23 # We always install this handler, because the expectation is for
18 # We always install this handler, because the expectation is for
24 # qtconsole to bring up a GUI even if called from the console.
19 # qtconsole to bring up a GUI even if called from the console.
25 # The old handler is called, so the exception is printed as well.
20 # The old handler is called, so the exception is printed as well.
26 # If desired, check for pythonw with an additional condition
21 # If desired, check for pythonw with an additional condition
27 # (sys.executable.lower().find('pythonw.exe') >= 0).
22 # (sys.executable.lower().find('pythonw.exe') >= 0).
28 if os.name == 'nt':
23 if os.name == 'nt':
29 old_excepthook = sys.excepthook
24 old_excepthook = sys.excepthook
30
25
31 # Exclude this from our autogenerated API docs.
26 # Exclude this from our autogenerated API docs.
32 undoc = lambda func: func
27 undoc = lambda func: func
33
28
34 @undoc
29 @undoc
35 def gui_excepthook(exctype, value, tb):
30 def gui_excepthook(exctype, value, tb):
36 try:
31 try:
37 import ctypes, traceback
32 import ctypes, traceback
38 MB_ICONERROR = 0x00000010
33 MB_ICONERROR = 0x00000010
39 title = u'Error starting IPython QtConsole'
34 title = u'Error starting IPython QtConsole'
40 msg = u''.join(traceback.format_exception(exctype, value, tb))
35 msg = u''.join(traceback.format_exception(exctype, value, tb))
41 ctypes.windll.user32.MessageBoxW(0, msg, title, MB_ICONERROR)
36 ctypes.windll.user32.MessageBoxW(0, msg, title, MB_ICONERROR)
42 finally:
37 finally:
43 # Also call the old exception hook to let it do
38 # Also call the old exception hook to let it do
44 # its thing too.
39 # its thing too.
45 old_excepthook(exctype, value, tb)
40 old_excepthook(exctype, value, tb)
46
41
47 sys.excepthook = gui_excepthook
42 sys.excepthook = gui_excepthook
48
43
49 # System library imports
50 from IPython.external.qt import QtCore, QtGui
44 from IPython.external.qt import QtCore, QtGui
51
45
52 # Local imports
53 from IPython.config.application import boolean_flag
46 from IPython.config.application import boolean_flag
54 from IPython.config.application import catch_config_error
47 from IPython.config.application import catch_config_error
55 from IPython.core.application import BaseIPythonApplication
48 from IPython.core.application import BaseIPythonApplication
56 from IPython.qt.console.ipython_widget import IPythonWidget
49 from IPython.qt.console.ipython_widget import IPythonWidget
57 from IPython.qt.console.rich_ipython_widget import RichIPythonWidget
50 from IPython.qt.console.rich_ipython_widget import RichIPythonWidget
58 from IPython.qt.console import styles
51 from IPython.qt.console import styles
59 from IPython.qt.console.mainwindow import MainWindow
52 from IPython.qt.console.mainwindow import MainWindow
60 from IPython.qt.client import QtKernelClient
53 from IPython.qt.client import QtKernelClient
61 from IPython.qt.manager import QtKernelManager
54 from IPython.qt.manager import QtKernelManager
62 from IPython.utils.traitlets import (
55 from IPython.utils.traitlets import (
63 Dict, Unicode, CBool, Any
56 Dict, Unicode, CBool, Any
64 )
57 )
65
58
66 from IPython.consoleapp import (
59 from IPython.consoleapp import (
67 IPythonConsoleApp, app_aliases, app_flags, flags, aliases
60 IPythonConsoleApp, app_aliases, app_flags, flags, aliases
68 )
61 )
69
62
70 #-----------------------------------------------------------------------------
63 #-----------------------------------------------------------------------------
71 # Network Constants
64 # Network Constants
72 #-----------------------------------------------------------------------------
65 #-----------------------------------------------------------------------------
73
66
74 from IPython.utils.localinterfaces import is_local_ip
67 from IPython.utils.localinterfaces import is_local_ip
75
68
76 #-----------------------------------------------------------------------------
69 #-----------------------------------------------------------------------------
77 # Globals
70 # Globals
78 #-----------------------------------------------------------------------------
71 #-----------------------------------------------------------------------------
79
72
80 _examples = """
73 _examples = """
81 ipython qtconsole # start the qtconsole
74 ipython qtconsole # start the qtconsole
82 ipython qtconsole --matplotlib=inline # start with matplotlib inline plotting mode
75 ipython qtconsole --matplotlib=inline # start with matplotlib inline plotting mode
83 """
76 """
84
77
85 #-----------------------------------------------------------------------------
78 #-----------------------------------------------------------------------------
86 # Aliases and Flags
79 # Aliases and Flags
87 #-----------------------------------------------------------------------------
80 #-----------------------------------------------------------------------------
88
81
89 # start with copy of flags
82 # start with copy of flags
90 flags = dict(flags)
83 flags = dict(flags)
91 qt_flags = {
84 qt_flags = {
92 'plain' : ({'IPythonQtConsoleApp' : {'plain' : True}},
85 'plain' : ({'IPythonQtConsoleApp' : {'plain' : True}},
93 "Disable rich text support."),
86 "Disable rich text support."),
94 }
87 }
95 qt_flags.update(boolean_flag(
88 qt_flags.update(boolean_flag(
96 'banner', 'IPythonQtConsoleApp.display_banner',
89 'banner', 'IPythonQtConsoleApp.display_banner',
97 "Display a banner upon starting the QtConsole.",
90 "Display a banner upon starting the QtConsole.",
98 "Don't display a banner upon starting the QtConsole."
91 "Don't display a banner upon starting the QtConsole."
99 ))
92 ))
100
93
101 # and app_flags from the Console Mixin
94 # and app_flags from the Console Mixin
102 qt_flags.update(app_flags)
95 qt_flags.update(app_flags)
103 # add frontend flags to the full set
96 # add frontend flags to the full set
104 flags.update(qt_flags)
97 flags.update(qt_flags)
105
98
106 # start with copy of front&backend aliases list
99 # start with copy of front&backend aliases list
107 aliases = dict(aliases)
100 aliases = dict(aliases)
108 qt_aliases = dict(
101 qt_aliases = dict(
109 style = 'IPythonWidget.syntax_style',
102 style = 'IPythonWidget.syntax_style',
110 stylesheet = 'IPythonQtConsoleApp.stylesheet',
103 stylesheet = 'IPythonQtConsoleApp.stylesheet',
111 colors = 'ZMQInteractiveShell.colors',
104 colors = 'ZMQInteractiveShell.colors',
112
105
113 editor = 'IPythonWidget.editor',
106 editor = 'IPythonWidget.editor',
114 paging = 'ConsoleWidget.paging',
107 paging = 'ConsoleWidget.paging',
115 )
108 )
116 # and app_aliases from the Console Mixin
109 # and app_aliases from the Console Mixin
117 qt_aliases.update(app_aliases)
110 qt_aliases.update(app_aliases)
118 qt_aliases.update({'gui-completion':'ConsoleWidget.gui_completion'})
111 qt_aliases.update({'gui-completion':'ConsoleWidget.gui_completion'})
119 # add frontend aliases to the full set
112 # add frontend aliases to the full set
120 aliases.update(qt_aliases)
113 aliases.update(qt_aliases)
121
114
122 # get flags&aliases into sets, and remove a couple that
115 # get flags&aliases into sets, and remove a couple that
123 # shouldn't be scrubbed from backend flags:
116 # shouldn't be scrubbed from backend flags:
124 qt_aliases = set(qt_aliases.keys())
117 qt_aliases = set(qt_aliases.keys())
125 qt_aliases.remove('colors')
118 qt_aliases.remove('colors')
126 qt_flags = set(qt_flags.keys())
119 qt_flags = set(qt_flags.keys())
127
120
128 #-----------------------------------------------------------------------------
121 #-----------------------------------------------------------------------------
129 # Classes
122 # Classes
130 #-----------------------------------------------------------------------------
123 #-----------------------------------------------------------------------------
131
124
132 #-----------------------------------------------------------------------------
125 #-----------------------------------------------------------------------------
133 # IPythonQtConsole
126 # IPythonQtConsole
134 #-----------------------------------------------------------------------------
127 #-----------------------------------------------------------------------------
135
128
136
129
137 class IPythonQtConsoleApp(BaseIPythonApplication, IPythonConsoleApp):
130 class IPythonQtConsoleApp(BaseIPythonApplication, IPythonConsoleApp):
138 name = 'ipython-qtconsole'
131 name = 'ipython-qtconsole'
139
132
140 description = """
133 description = """
141 The IPython QtConsole.
134 The IPython QtConsole.
142
135
143 This launches a Console-style application using Qt. It is not a full
136 This launches a Console-style application using Qt. It is not a full
144 console, in that launched terminal subprocesses will not be able to accept
137 console, in that launched terminal subprocesses will not be able to accept
145 input.
138 input.
146
139
147 The QtConsole supports various extra features beyond the Terminal IPython
140 The QtConsole supports various extra features beyond the Terminal IPython
148 shell, such as inline plotting with matplotlib, via:
141 shell, such as inline plotting with matplotlib, via:
149
142
150 ipython qtconsole --matplotlib=inline
143 ipython qtconsole --matplotlib=inline
151
144
152 as well as saving your session as HTML, and printing the output.
145 as well as saving your session as HTML, and printing the output.
153
146
154 """
147 """
155 examples = _examples
148 examples = _examples
156
149
157 classes = [IPythonWidget] + IPythonConsoleApp.classes
150 classes = [IPythonWidget] + IPythonConsoleApp.classes
158 flags = Dict(flags)
151 flags = Dict(flags)
159 aliases = Dict(aliases)
152 aliases = Dict(aliases)
160 frontend_flags = Any(qt_flags)
153 frontend_flags = Any(qt_flags)
161 frontend_aliases = Any(qt_aliases)
154 frontend_aliases = Any(qt_aliases)
162 kernel_client_class = QtKernelClient
155 kernel_client_class = QtKernelClient
163 kernel_manager_class = QtKernelManager
156 kernel_manager_class = QtKernelManager
164
157
165 stylesheet = Unicode('', config=True,
158 stylesheet = Unicode('', config=True,
166 help="path to a custom CSS stylesheet")
159 help="path to a custom CSS stylesheet")
167
160
168 hide_menubar = CBool(False, config=True,
161 hide_menubar = CBool(False, config=True,
169 help="Start the console window with the menu bar hidden.")
162 help="Start the console window with the menu bar hidden.")
170
163
171 maximize = CBool(False, config=True,
164 maximize = CBool(False, config=True,
172 help="Start the console window maximized.")
165 help="Start the console window maximized.")
173
166
174 plain = CBool(False, config=True,
167 plain = CBool(False, config=True,
175 help="Use a plaintext widget instead of rich text (plain can't print/save).")
168 help="Use a plaintext widget instead of rich text (plain can't print/save).")
176
169
177 display_banner = CBool(True, config=True,
170 display_banner = CBool(True, config=True,
178 help="Whether to display a banner upon starting the QtConsole."
171 help="Whether to display a banner upon starting the QtConsole."
179 )
172 )
180
173
181 def _plain_changed(self, name, old, new):
174 def _plain_changed(self, name, old, new):
182 kind = 'plain' if new else 'rich'
175 kind = 'plain' if new else 'rich'
183 self.config.ConsoleWidget.kind = kind
176 self.config.ConsoleWidget.kind = kind
184 if new:
177 if new:
185 self.widget_factory = IPythonWidget
178 self.widget_factory = IPythonWidget
186 else:
179 else:
187 self.widget_factory = RichIPythonWidget
180 self.widget_factory = RichIPythonWidget
188
181
189 # the factory for creating a widget
182 # the factory for creating a widget
190 widget_factory = Any(RichIPythonWidget)
183 widget_factory = Any(RichIPythonWidget)
191
184
192 def parse_command_line(self, argv=None):
185 def parse_command_line(self, argv=None):
193 super(IPythonQtConsoleApp, self).parse_command_line(argv)
186 super(IPythonQtConsoleApp, self).parse_command_line(argv)
194 self.build_kernel_argv(argv)
187 self.build_kernel_argv(self.extra_args)
195
188
196
189
197 def new_frontend_master(self):
190 def new_frontend_master(self):
198 """ Create and return new frontend attached to new kernel, launched on localhost.
191 """ Create and return new frontend attached to new kernel, launched on localhost.
199 """
192 """
200 kernel_manager = self.kernel_manager_class(
193 kernel_manager = self.kernel_manager_class(
201 connection_file=self._new_connection_file(),
194 connection_file=self._new_connection_file(),
202 parent=self,
195 parent=self,
203 autorestart=True,
196 autorestart=True,
204 )
197 )
205 # start the kernel
198 # start the kernel
206 kwargs = {}
199 kwargs = {}
207 # FIXME: remove special treatment of IPython kernels
200 # FIXME: remove special treatment of IPython kernels
208 if self.kernel_manager.ipython_kernel:
201 if self.kernel_manager.ipython_kernel:
209 kwargs['extra_arguments'] = self.kernel_argv
202 kwargs['extra_arguments'] = self.kernel_argv
210 kernel_manager.start_kernel(**kwargs)
203 kernel_manager.start_kernel(**kwargs)
211 kernel_manager.client_factory = self.kernel_client_class
204 kernel_manager.client_factory = self.kernel_client_class
212 kernel_client = kernel_manager.client()
205 kernel_client = kernel_manager.client()
213 kernel_client.start_channels(shell=True, iopub=True)
206 kernel_client.start_channels(shell=True, iopub=True)
214 widget = self.widget_factory(config=self.config,
207 widget = self.widget_factory(config=self.config,
215 local_kernel=True)
208 local_kernel=True)
216 self.init_colors(widget)
209 self.init_colors(widget)
217 widget.kernel_manager = kernel_manager
210 widget.kernel_manager = kernel_manager
218 widget.kernel_client = kernel_client
211 widget.kernel_client = kernel_client
219 widget._existing = False
212 widget._existing = False
220 widget._may_close = True
213 widget._may_close = True
221 widget._confirm_exit = self.confirm_exit
214 widget._confirm_exit = self.confirm_exit
222 widget._display_banner = self.display_banner
215 widget._display_banner = self.display_banner
223 return widget
216 return widget
224
217
225 def new_frontend_slave(self, current_widget):
218 def new_frontend_slave(self, current_widget):
226 """Create and return a new frontend attached to an existing kernel.
219 """Create and return a new frontend attached to an existing kernel.
227
220
228 Parameters
221 Parameters
229 ----------
222 ----------
230 current_widget : IPythonWidget
223 current_widget : IPythonWidget
231 The IPythonWidget whose kernel this frontend is to share
224 The IPythonWidget whose kernel this frontend is to share
232 """
225 """
233 kernel_client = self.kernel_client_class(
226 kernel_client = self.kernel_client_class(
234 connection_file=current_widget.kernel_client.connection_file,
227 connection_file=current_widget.kernel_client.connection_file,
235 config = self.config,
228 config = self.config,
236 )
229 )
237 kernel_client.load_connection_file()
230 kernel_client.load_connection_file()
238 kernel_client.start_channels()
231 kernel_client.start_channels()
239 widget = self.widget_factory(config=self.config,
232 widget = self.widget_factory(config=self.config,
240 local_kernel=False)
233 local_kernel=False)
241 self.init_colors(widget)
234 self.init_colors(widget)
242 widget._existing = True
235 widget._existing = True
243 widget._may_close = False
236 widget._may_close = False
244 widget._confirm_exit = False
237 widget._confirm_exit = False
245 widget._display_banner = self.display_banner
238 widget._display_banner = self.display_banner
246 widget.kernel_client = kernel_client
239 widget.kernel_client = kernel_client
247 widget.kernel_manager = current_widget.kernel_manager
240 widget.kernel_manager = current_widget.kernel_manager
248 return widget
241 return widget
249
242
250 def init_qt_app(self):
243 def init_qt_app(self):
251 # separate from qt_elements, because it must run first
244 # separate from qt_elements, because it must run first
252 self.app = QtGui.QApplication([])
245 self.app = QtGui.QApplication([])
253
246
254 def init_qt_elements(self):
247 def init_qt_elements(self):
255 # Create the widget.
248 # Create the widget.
256
249
257 base_path = os.path.abspath(os.path.dirname(__file__))
250 base_path = os.path.abspath(os.path.dirname(__file__))
258 icon_path = os.path.join(base_path, 'resources', 'icon', 'IPythonConsole.svg')
251 icon_path = os.path.join(base_path, 'resources', 'icon', 'IPythonConsole.svg')
259 self.app.icon = QtGui.QIcon(icon_path)
252 self.app.icon = QtGui.QIcon(icon_path)
260 QtGui.QApplication.setWindowIcon(self.app.icon)
253 QtGui.QApplication.setWindowIcon(self.app.icon)
261
254
262 ip = self.ip
255 ip = self.ip
263 local_kernel = (not self.existing) or is_local_ip(ip)
256 local_kernel = (not self.existing) or is_local_ip(ip)
264 self.widget = self.widget_factory(config=self.config,
257 self.widget = self.widget_factory(config=self.config,
265 local_kernel=local_kernel)
258 local_kernel=local_kernel)
266 self.init_colors(self.widget)
259 self.init_colors(self.widget)
267 self.widget._existing = self.existing
260 self.widget._existing = self.existing
268 self.widget._may_close = not self.existing
261 self.widget._may_close = not self.existing
269 self.widget._confirm_exit = self.confirm_exit
262 self.widget._confirm_exit = self.confirm_exit
270 self.widget._display_banner = self.display_banner
263 self.widget._display_banner = self.display_banner
271
264
272 self.widget.kernel_manager = self.kernel_manager
265 self.widget.kernel_manager = self.kernel_manager
273 self.widget.kernel_client = self.kernel_client
266 self.widget.kernel_client = self.kernel_client
274 self.window = MainWindow(self.app,
267 self.window = MainWindow(self.app,
275 confirm_exit=self.confirm_exit,
268 confirm_exit=self.confirm_exit,
276 new_frontend_factory=self.new_frontend_master,
269 new_frontend_factory=self.new_frontend_master,
277 slave_frontend_factory=self.new_frontend_slave,
270 slave_frontend_factory=self.new_frontend_slave,
278 )
271 )
279 self.window.log = self.log
272 self.window.log = self.log
280 self.window.add_tab_with_frontend(self.widget)
273 self.window.add_tab_with_frontend(self.widget)
281 self.window.init_magic_helper()
274 self.window.init_magic_helper()
282 self.window.init_menu_bar()
275 self.window.init_menu_bar()
283
276
284 # Ignore on OSX, where there is always a menu bar
277 # Ignore on OSX, where there is always a menu bar
285 if sys.platform != 'darwin' and self.hide_menubar:
278 if sys.platform != 'darwin' and self.hide_menubar:
286 self.window.menuBar().setVisible(False)
279 self.window.menuBar().setVisible(False)
287
280
288 self.window.setWindowTitle('IPython')
281 self.window.setWindowTitle('IPython')
289
282
290 def init_colors(self, widget):
283 def init_colors(self, widget):
291 """Configure the coloring of the widget"""
284 """Configure the coloring of the widget"""
292 # Note: This will be dramatically simplified when colors
285 # Note: This will be dramatically simplified when colors
293 # are removed from the backend.
286 # are removed from the backend.
294
287
295 # parse the colors arg down to current known labels
288 # parse the colors arg down to current known labels
296 cfg = self.config
289 cfg = self.config
297 colors = cfg.ZMQInteractiveShell.colors if 'ZMQInteractiveShell.colors' in cfg else None
290 colors = cfg.ZMQInteractiveShell.colors if 'ZMQInteractiveShell.colors' in cfg else None
298 style = cfg.IPythonWidget.syntax_style if 'IPythonWidget.syntax_style' in cfg else None
291 style = cfg.IPythonWidget.syntax_style if 'IPythonWidget.syntax_style' in cfg else None
299 sheet = cfg.IPythonWidget.style_sheet if 'IPythonWidget.style_sheet' in cfg else None
292 sheet = cfg.IPythonWidget.style_sheet if 'IPythonWidget.style_sheet' in cfg else None
300
293
301 # find the value for colors:
294 # find the value for colors:
302 if colors:
295 if colors:
303 colors=colors.lower()
296 colors=colors.lower()
304 if colors in ('lightbg', 'light'):
297 if colors in ('lightbg', 'light'):
305 colors='lightbg'
298 colors='lightbg'
306 elif colors in ('dark', 'linux'):
299 elif colors in ('dark', 'linux'):
307 colors='linux'
300 colors='linux'
308 else:
301 else:
309 colors='nocolor'
302 colors='nocolor'
310 elif style:
303 elif style:
311 if style=='bw':
304 if style=='bw':
312 colors='nocolor'
305 colors='nocolor'
313 elif styles.dark_style(style):
306 elif styles.dark_style(style):
314 colors='linux'
307 colors='linux'
315 else:
308 else:
316 colors='lightbg'
309 colors='lightbg'
317 else:
310 else:
318 colors=None
311 colors=None
319
312
320 # Configure the style
313 # Configure the style
321 if style:
314 if style:
322 widget.style_sheet = styles.sheet_from_template(style, colors)
315 widget.style_sheet = styles.sheet_from_template(style, colors)
323 widget.syntax_style = style
316 widget.syntax_style = style
324 widget._syntax_style_changed()
317 widget._syntax_style_changed()
325 widget._style_sheet_changed()
318 widget._style_sheet_changed()
326 elif colors:
319 elif colors:
327 # use a default dark/light/bw style
320 # use a default dark/light/bw style
328 widget.set_default_style(colors=colors)
321 widget.set_default_style(colors=colors)
329
322
330 if self.stylesheet:
323 if self.stylesheet:
331 # we got an explicit stylesheet
324 # we got an explicit stylesheet
332 if os.path.isfile(self.stylesheet):
325 if os.path.isfile(self.stylesheet):
333 with open(self.stylesheet) as f:
326 with open(self.stylesheet) as f:
334 sheet = f.read()
327 sheet = f.read()
335 else:
328 else:
336 raise IOError("Stylesheet %r not found." % self.stylesheet)
329 raise IOError("Stylesheet %r not found." % self.stylesheet)
337 if sheet:
330 if sheet:
338 widget.style_sheet = sheet
331 widget.style_sheet = sheet
339 widget._style_sheet_changed()
332 widget._style_sheet_changed()
340
333
341
334
342 def init_signal(self):
335 def init_signal(self):
343 """allow clean shutdown on sigint"""
336 """allow clean shutdown on sigint"""
344 signal.signal(signal.SIGINT, lambda sig, frame: self.exit(-2))
337 signal.signal(signal.SIGINT, lambda sig, frame: self.exit(-2))
345 # need a timer, so that QApplication doesn't block until a real
338 # need a timer, so that QApplication doesn't block until a real
346 # Qt event fires (can require mouse movement)
339 # Qt event fires (can require mouse movement)
347 # timer trick from http://stackoverflow.com/q/4938723/938949
340 # timer trick from http://stackoverflow.com/q/4938723/938949
348 timer = QtCore.QTimer()
341 timer = QtCore.QTimer()
349 # Let the interpreter run each 200 ms:
342 # Let the interpreter run each 200 ms:
350 timer.timeout.connect(lambda: None)
343 timer.timeout.connect(lambda: None)
351 timer.start(200)
344 timer.start(200)
352 # hold onto ref, so the timer doesn't get cleaned up
345 # hold onto ref, so the timer doesn't get cleaned up
353 self._sigint_timer = timer
346 self._sigint_timer = timer
354
347
355 @catch_config_error
348 @catch_config_error
356 def initialize(self, argv=None):
349 def initialize(self, argv=None):
357 self.init_qt_app()
350 self.init_qt_app()
358 super(IPythonQtConsoleApp, self).initialize(argv)
351 super(IPythonQtConsoleApp, self).initialize(argv)
359 IPythonConsoleApp.initialize(self,argv)
352 IPythonConsoleApp.initialize(self,argv)
360 self.init_qt_elements()
353 self.init_qt_elements()
361 self.init_signal()
354 self.init_signal()
362
355
363 def start(self):
356 def start(self):
364
357
365 # draw the window
358 # draw the window
366 if self.maximize:
359 if self.maximize:
367 self.window.showMaximized()
360 self.window.showMaximized()
368 else:
361 else:
369 self.window.show()
362 self.window.show()
370 self.window.raise_()
363 self.window.raise_()
371
364
372 # Start the application main loop.
365 # Start the application main loop.
373 self.app.exec_()
366 self.app.exec_()
374
367
375 #-----------------------------------------------------------------------------
368 #-----------------------------------------------------------------------------
376 # Main entry point
369 # Main entry point
377 #-----------------------------------------------------------------------------
370 #-----------------------------------------------------------------------------
378
371
379 def main():
372 def main():
380 app = IPythonQtConsoleApp()
373 app = IPythonQtConsoleApp()
381 app.initialize()
374 app.initialize()
382 app.start()
375 app.start()
383
376
384
377
385 if __name__ == '__main__':
378 if __name__ == '__main__':
386 main()
379 main()
General Comments 0
You need to be logged in to leave comments. Login now