##// END OF EJS Templates
Add kernelspec command line entry point
Thomas Kluyver -
Show More
@@ -0,0 +1,109 b''
1
2 # Copyright (c) IPython Development Team.
3 # Distributed under the terms of the Modified BSD License.
4
5 import errno
6 import os.path
7
8 from IPython.config.application import Application
9 from IPython.core.application import BaseIPythonApplication, base_flags
10 from IPython.utils.traitlets import Instance, Dict, Unicode, Bool
11
12 from .kernelspec import KernelSpecManager
13
14 def _pythonfirst(s):
15 "Sort key function that will put strings starting with 'python' first."
16 if s.startswith('python'):
17 return ''
18 return s
19
20 class ListKernelSpecs(BaseIPythonApplication):
21 description = """List installed kernel specifications."""
22 kernel_spec_manager = Instance(KernelSpecManager)
23
24 def _kernel_spec_manager_default(self):
25 return KernelSpecManager(ipython_dir=self.ipython_dir)
26
27 def start(self):
28 print("Available kernels:")
29 for kernelname in sorted(self.kernel_spec_manager.find_kernel_specs(),
30 key=_pythonfirst):
31 print(" %s" % kernelname)
32
33
34 class InstallKernelSpec(BaseIPythonApplication):
35 description = """Install a kernel specification directory."""
36 kernel_spec_manager = Instance(KernelSpecManager)
37
38 def _kernel_spec_manager_default(self):
39 return KernelSpecManager(ipython_dir=self.ipython_dir)
40
41 sourcedir = Unicode()
42 kernel_name = Unicode("", config=True,
43 help="Install the kernel spec with this name"
44 )
45 def _kernel_name_default(self):
46 return os.path.basename(self.sourcedir)
47
48 system = Bool(False, config=True,
49 help="""
50 Try to install the kernel spec to the systemwide directory instead of
51 the per-user directory.
52 """
53 )
54 replace = Bool(False, config=True,
55 help="Replace any existing kernel spec with this name."
56 )
57
58 aliases = {'name': 'InstallKernelSpec.kernel_name'}
59
60 flags = {'system': ({'InstallKernelSpec': {'system': True}},
61 "Install to the systemwide kernel registry"),
62 'replace': ({'InstallKernelSpec': {'replace': True}},
63 "Replace any existing kernel spec with this name."),
64 }
65 flags.update(base_flags)
66
67 def parse_command_line(self, argv):
68 super(InstallKernelSpec, self).parse_command_line(argv)
69 # accept positional arg as profile name
70 if self.extra_args:
71 self.sourcedir = self.extra_args[0]
72 else:
73 print("No source directory specified.")
74 self.exit(1)
75
76 def start(self):
77 try:
78 self.kernel_spec_manager.install_kernel_spec(self.sourcedir,
79 kernel_name=self.kernel_name,
80 system=self.system,
81 replace=self.replace,
82 )
83 except OSError as e:
84 if e.errno == errno.EACCES:
85 print("Permission denied")
86 self.exit(1)
87 elif e.errno == errno.EEXIST:
88 print("A kernel spec named %r is already present" % self.kernel_name)
89 self.exit(1)
90 raise
91
92 class KernelSpecApp(Application):
93 name = "ipython-kernelspec"
94 description = """Manage IPython kernel specifications."""
95
96 subcommands = Dict(dict(
97 list = (ListKernelSpecs, ListKernelSpecs.description.splitlines()[0]),
98 install = (InstallKernelSpec, InstallKernelSpec.description.splitlines()[0])
99 ))
100
101 def start(self):
102 if self.subapp is None:
103 print("No subcommand specified. Must specify one of: %s"% list(self.subcommands))
104 print()
105 self.print_description()
106 self.print_subcommands()
107 self.exit(1)
108 else:
109 return self.subapp.start()
@@ -1,393 +1,396 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 from __future__ import print_function
28 28
29 29 import logging
30 30 import os
31 31 import sys
32 32
33 33 from IPython.config.loader import Config
34 34 from IPython.config.application import boolean_flag, catch_config_error, Application
35 35 from IPython.core import release
36 36 from IPython.core import usage
37 37 from IPython.core.completer import IPCompleter
38 38 from IPython.core.crashhandler import CrashHandler
39 39 from IPython.core.formatters import PlainTextFormatter
40 40 from IPython.core.history import HistoryManager
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.magics import ScriptMagics
46 46 from IPython.core.shellapp import (
47 47 InteractiveShellApp, shell_flags, shell_aliases
48 48 )
49 49 from IPython.extensions.storemagic import StoreMagics
50 50 from IPython.terminal.interactiveshell import TerminalInteractiveShell
51 51 from IPython.utils import warn
52 52 from IPython.utils.path import get_ipython_dir, check_for_old_config
53 53 from IPython.utils.traitlets import (
54 54 Bool, List, Dict,
55 55 )
56 56
57 57 #-----------------------------------------------------------------------------
58 58 # Globals, utilities and helpers
59 59 #-----------------------------------------------------------------------------
60 60
61 61 _examples = """
62 62 ipython --matplotlib # enable matplotlib integration
63 63 ipython --matplotlib=qt # enable matplotlib integration with qt4 backend
64 64
65 65 ipython --log-level=DEBUG # set logging to DEBUG
66 66 ipython --profile=foo # start with profile foo
67 67
68 68 ipython qtconsole # start the qtconsole GUI application
69 69 ipython help qtconsole # show the help for the qtconsole subcmd
70 70
71 71 ipython console # start the terminal-based console application
72 72 ipython help console # show the help for the console subcmd
73 73
74 74 ipython notebook # start the IPython notebook
75 75 ipython help notebook # show the help for the notebook subcmd
76 76
77 77 ipython profile create foo # create profile foo w/ default config files
78 78 ipython help profile # show the help for the profile subcmd
79 79
80 80 ipython locate # print the path to the IPython directory
81 81 ipython locate profile foo # print the path to the directory for profile `foo`
82 82
83 83 ipython nbconvert # convert notebooks to/from other formats
84 84 """
85 85
86 86 #-----------------------------------------------------------------------------
87 87 # Crash handler for this application
88 88 #-----------------------------------------------------------------------------
89 89
90 90 class IPAppCrashHandler(CrashHandler):
91 91 """sys.excepthook for IPython itself, leaves a detailed report on disk."""
92 92
93 93 def __init__(self, app):
94 94 contact_name = release.author
95 95 contact_email = release.author_email
96 96 bug_tracker = 'https://github.com/ipython/ipython/issues'
97 97 super(IPAppCrashHandler,self).__init__(
98 98 app, contact_name, contact_email, bug_tracker
99 99 )
100 100
101 101 def make_report(self,traceback):
102 102 """Return a string containing a crash report."""
103 103
104 104 sec_sep = self.section_sep
105 105 # Start with parent report
106 106 report = [super(IPAppCrashHandler, self).make_report(traceback)]
107 107 # Add interactive-specific info we may have
108 108 rpt_add = report.append
109 109 try:
110 110 rpt_add(sec_sep+"History of session input:")
111 111 for line in self.app.shell.user_ns['_ih']:
112 112 rpt_add(line)
113 113 rpt_add('\n*** Last line of input (may not be in above history):\n')
114 114 rpt_add(self.app.shell._last_input_line+'\n')
115 115 except:
116 116 pass
117 117
118 118 return ''.join(report)
119 119
120 120 #-----------------------------------------------------------------------------
121 121 # Aliases and Flags
122 122 #-----------------------------------------------------------------------------
123 123 flags = dict(base_flags)
124 124 flags.update(shell_flags)
125 125 frontend_flags = {}
126 126 addflag = lambda *args: frontend_flags.update(boolean_flag(*args))
127 127 addflag('autoedit-syntax', 'TerminalInteractiveShell.autoedit_syntax',
128 128 'Turn on auto editing of files with syntax errors.',
129 129 'Turn off auto editing of files with syntax errors.'
130 130 )
131 131 addflag('banner', 'TerminalIPythonApp.display_banner',
132 132 "Display a banner upon starting IPython.",
133 133 "Don't display a banner upon starting IPython."
134 134 )
135 135 addflag('confirm-exit', 'TerminalInteractiveShell.confirm_exit',
136 136 """Set to confirm when you try to exit IPython with an EOF (Control-D
137 137 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
138 138 you can force a direct exit without any confirmation.""",
139 139 "Don't prompt the user when exiting."
140 140 )
141 141 addflag('term-title', 'TerminalInteractiveShell.term_title',
142 142 "Enable auto setting the terminal title.",
143 143 "Disable auto setting the terminal title."
144 144 )
145 145 classic_config = Config()
146 146 classic_config.InteractiveShell.cache_size = 0
147 147 classic_config.PlainTextFormatter.pprint = False
148 148 classic_config.PromptManager.in_template = '>>> '
149 149 classic_config.PromptManager.in2_template = '... '
150 150 classic_config.PromptManager.out_template = ''
151 151 classic_config.InteractiveShell.separate_in = ''
152 152 classic_config.InteractiveShell.separate_out = ''
153 153 classic_config.InteractiveShell.separate_out2 = ''
154 154 classic_config.InteractiveShell.colors = 'NoColor'
155 155 classic_config.InteractiveShell.xmode = 'Plain'
156 156
157 157 frontend_flags['classic']=(
158 158 classic_config,
159 159 "Gives IPython a similar feel to the classic Python prompt."
160 160 )
161 161 # # log doesn't make so much sense this way anymore
162 162 # paa('--log','-l',
163 163 # action='store_true', dest='InteractiveShell.logstart',
164 164 # help="Start logging to the default log file (./ipython_log.py).")
165 165 #
166 166 # # quick is harder to implement
167 167 frontend_flags['quick']=(
168 168 {'TerminalIPythonApp' : {'quick' : True}},
169 169 "Enable quick startup with no config files."
170 170 )
171 171
172 172 frontend_flags['i'] = (
173 173 {'TerminalIPythonApp' : {'force_interact' : True}},
174 174 """If running code from the command line, become interactive afterwards.
175 175 Note: can also be given simply as '-i'."""
176 176 )
177 177 flags.update(frontend_flags)
178 178
179 179 aliases = dict(base_aliases)
180 180 aliases.update(shell_aliases)
181 181
182 182 #-----------------------------------------------------------------------------
183 183 # Main classes and functions
184 184 #-----------------------------------------------------------------------------
185 185
186 186
187 187 class LocateIPythonApp(BaseIPythonApplication):
188 188 description = """print the path to the IPython dir"""
189 189 subcommands = Dict(dict(
190 190 profile=('IPython.core.profileapp.ProfileLocate',
191 191 "print the path to an IPython profile directory",
192 192 ),
193 193 ))
194 194 def start(self):
195 195 if self.subapp is not None:
196 196 return self.subapp.start()
197 197 else:
198 198 print(self.ipython_dir)
199 199
200 200
201 201 class TerminalIPythonApp(BaseIPythonApplication, InteractiveShellApp):
202 202 name = u'ipython'
203 203 description = usage.cl_usage
204 204 crash_handler_class = IPAppCrashHandler
205 205 examples = _examples
206 206
207 207 flags = Dict(flags)
208 208 aliases = Dict(aliases)
209 209 classes = List()
210 210 def _classes_default(self):
211 211 """This has to be in a method, for TerminalIPythonApp to be available."""
212 212 return [
213 213 InteractiveShellApp, # ShellApp comes before TerminalApp, because
214 214 self.__class__, # it will also affect subclasses (e.g. QtConsole)
215 215 TerminalInteractiveShell,
216 216 PromptManager,
217 217 HistoryManager,
218 218 ProfileDir,
219 219 PlainTextFormatter,
220 220 IPCompleter,
221 221 ScriptMagics,
222 222 StoreMagics,
223 223 ]
224 224
225 225 subcommands = dict(
226 226 qtconsole=('IPython.qt.console.qtconsoleapp.IPythonQtConsoleApp',
227 227 """Launch the IPython Qt Console."""
228 228 ),
229 229 notebook=('IPython.html.notebookapp.NotebookApp',
230 230 """Launch the IPython HTML Notebook Server."""
231 231 ),
232 232 profile = ("IPython.core.profileapp.ProfileApp",
233 233 "Create and manage IPython profiles."
234 234 ),
235 235 kernel = ("IPython.kernel.zmq.kernelapp.IPKernelApp",
236 236 "Start a kernel without an attached frontend."
237 237 ),
238 238 console=('IPython.terminal.console.app.ZMQTerminalIPythonApp',
239 239 """Launch the IPython terminal-based Console."""
240 240 ),
241 241 locate=('IPython.terminal.ipapp.LocateIPythonApp',
242 242 LocateIPythonApp.description
243 243 ),
244 244 history=('IPython.core.historyapp.HistoryApp',
245 245 "Manage the IPython history database."
246 246 ),
247 247 nbconvert=('IPython.nbconvert.nbconvertapp.NbConvertApp',
248 248 "Convert notebooks to/from other formats."
249 249 ),
250 250 trust=('IPython.nbformat.sign.TrustNotebookApp',
251 251 "Sign notebooks to trust their potentially unsafe contents at load."
252 252 ),
253 kernelspec=('IPython.kernel.kernelspecapp.KernelSpecApp',
254 "Manage IPython kernel specifications."
255 ),
253 256 )
254 257 subcommands['install-nbextension'] = (
255 258 "IPython.html.nbextensions.NBExtensionApp",
256 259 "Install IPython notebook extension files"
257 260 )
258 261
259 262 # *do* autocreate requested profile, but don't create the config file.
260 263 auto_create=Bool(True)
261 264 # configurables
262 265 ignore_old_config=Bool(False, config=True,
263 266 help="Suppress warning messages about legacy config files"
264 267 )
265 268 quick = Bool(False, config=True,
266 269 help="""Start IPython quickly by skipping the loading of config files."""
267 270 )
268 271 def _quick_changed(self, name, old, new):
269 272 if new:
270 273 self.load_config_file = lambda *a, **kw: None
271 274 self.ignore_old_config=True
272 275
273 276 display_banner = Bool(True, config=True,
274 277 help="Whether to display a banner upon starting IPython."
275 278 )
276 279
277 280 # if there is code of files to run from the cmd line, don't interact
278 281 # unless the --i flag (App.force_interact) is true.
279 282 force_interact = Bool(False, config=True,
280 283 help="""If a command or file is given via the command-line,
281 284 e.g. 'ipython foo.py', start an interactive shell after executing the
282 285 file or command."""
283 286 )
284 287 def _force_interact_changed(self, name, old, new):
285 288 if new:
286 289 self.interact = True
287 290
288 291 def _file_to_run_changed(self, name, old, new):
289 292 if new:
290 293 self.something_to_run = True
291 294 if new and not self.force_interact:
292 295 self.interact = False
293 296 _code_to_run_changed = _file_to_run_changed
294 297 _module_to_run_changed = _file_to_run_changed
295 298
296 299 # internal, not-configurable
297 300 interact=Bool(True)
298 301 something_to_run=Bool(False)
299 302
300 303 def parse_command_line(self, argv=None):
301 304 """override to allow old '-pylab' flag with deprecation warning"""
302 305
303 306 argv = sys.argv[1:] if argv is None else argv
304 307
305 308 if '-pylab' in argv:
306 309 # deprecated `-pylab` given,
307 310 # warn and transform into current syntax
308 311 argv = argv[:] # copy, don't clobber
309 312 idx = argv.index('-pylab')
310 313 warn.warn("`-pylab` flag has been deprecated.\n"
311 314 " Use `--matplotlib <backend>` and import pylab manually.")
312 315 argv[idx] = '--pylab'
313 316
314 317 return super(TerminalIPythonApp, self).parse_command_line(argv)
315 318
316 319 @catch_config_error
317 320 def initialize(self, argv=None):
318 321 """Do actions after construct, but before starting the app."""
319 322 super(TerminalIPythonApp, self).initialize(argv)
320 323 if self.subapp is not None:
321 324 # don't bother initializing further, starting subapp
322 325 return
323 326 if not self.ignore_old_config:
324 327 check_for_old_config(self.ipython_dir)
325 328 # print self.extra_args
326 329 if self.extra_args and not self.something_to_run:
327 330 self.file_to_run = self.extra_args[0]
328 331 self.init_path()
329 332 # create the shell
330 333 self.init_shell()
331 334 # and draw the banner
332 335 self.init_banner()
333 336 # Now a variety of things that happen after the banner is printed.
334 337 self.init_gui_pylab()
335 338 self.init_extensions()
336 339 self.init_code()
337 340
338 341 def init_shell(self):
339 342 """initialize the InteractiveShell instance"""
340 343 # Create an InteractiveShell instance.
341 344 # shell.display_banner should always be False for the terminal
342 345 # based app, because we call shell.show_banner() by hand below
343 346 # so the banner shows *before* all extension loading stuff.
344 347 self.shell = TerminalInteractiveShell.instance(parent=self,
345 348 display_banner=False, profile_dir=self.profile_dir,
346 349 ipython_dir=self.ipython_dir, user_ns=self.user_ns)
347 350 self.shell.configurables.append(self)
348 351
349 352 def init_banner(self):
350 353 """optionally display the banner"""
351 354 if self.display_banner and self.interact:
352 355 self.shell.show_banner()
353 356 # Make sure there is a space below the banner.
354 357 if self.log_level <= logging.INFO: print()
355 358
356 359 def _pylab_changed(self, name, old, new):
357 360 """Replace --pylab='inline' with --pylab='auto'"""
358 361 if new == 'inline':
359 362 warn.warn("'inline' not available as pylab backend, "
360 363 "using 'auto' instead.")
361 364 self.pylab = 'auto'
362 365
363 366 def start(self):
364 367 if self.subapp is not None:
365 368 return self.subapp.start()
366 369 # perform any prexec steps:
367 370 if self.interact:
368 371 self.log.debug("Starting IPython's mainloop...")
369 372 self.shell.mainloop()
370 373 else:
371 374 self.log.debug("IPython not interactive...")
372 375
373 376 def load_default_config(ipython_dir=None):
374 377 """Load the default config file from the default ipython_dir.
375 378
376 379 This is useful for embedded shells.
377 380 """
378 381 if ipython_dir is None:
379 382 ipython_dir = get_ipython_dir()
380 383
381 384 profile_dir = os.path.join(ipython_dir, 'profile_default')
382 385
383 386 config = Config()
384 387 for cf in Application._load_config_files("ipython_config", path=profile_dir):
385 388 config.update(cf)
386 389
387 390 return config
388 391
389 392 launch_new_instance = TerminalIPythonApp.launch_instance
390 393
391 394
392 395 if __name__ == '__main__':
393 396 launch_new_instance()
General Comments 0
You need to be logged in to leave comments. Login now