##// END OF EJS Templates
Merge pull request #5796 from takluyver/kernelspecapp...
Min RK -
r17012:fa2fba04 merge
parent child Browse files
Show More
@@ -0,0 +1,121 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 (
10 BaseIPythonApplication, base_flags, base_aliases
11 )
12 from IPython.utils.traitlets import Instance, Dict, Unicode, Bool
13
14 from .kernelspec import KernelSpecManager
15
16 def _pythonfirst(s):
17 "Sort key function that will put strings starting with 'python' first."
18 if s.startswith('python'):
19 # Space is not valid in kernel names, so this should sort first
20 return ' ' + s
21 return s
22
23 class ListKernelSpecs(BaseIPythonApplication):
24 description = """List installed kernel specifications."""
25 kernel_spec_manager = Instance(KernelSpecManager)
26
27 # Not all of the base aliases are meaningful (e.g. profile)
28 aliases = {k: base_aliases[k] for k in ['ipython-dir', 'log-level']}
29 flags = {'debug': base_flags['debug'],}
30
31 def _kernel_spec_manager_default(self):
32 return KernelSpecManager(ipython_dir=self.ipython_dir)
33
34 def start(self):
35 print("Available kernels:")
36 for kernelname in sorted(self.kernel_spec_manager.find_kernel_specs(),
37 key=_pythonfirst):
38 print(" %s" % kernelname)
39
40
41 class InstallKernelSpec(BaseIPythonApplication):
42 description = """Install a kernel specification directory."""
43 kernel_spec_manager = Instance(KernelSpecManager)
44
45 def _kernel_spec_manager_default(self):
46 return KernelSpecManager(ipython_dir=self.ipython_dir)
47
48 sourcedir = Unicode()
49 kernel_name = Unicode("", config=True,
50 help="Install the kernel spec with this name"
51 )
52 def _kernel_name_default(self):
53 return os.path.basename(self.sourcedir)
54
55 system = Bool(False, config=True,
56 help="""
57 Try to install the kernel spec to the systemwide directory instead of
58 the per-user directory.
59 """
60 )
61 replace = Bool(False, config=True,
62 help="Replace any existing kernel spec with this name."
63 )
64
65 aliases = {'name': 'InstallKernelSpec.kernel_name'}
66 for k in ['ipython-dir', 'log-level']:
67 aliases[k] = base_aliases[k]
68
69 flags = {'system': ({'InstallKernelSpec': {'system': True}},
70 "Install to the systemwide kernel registry"),
71 'replace': ({'InstallKernelSpec': {'replace': True}},
72 "Replace any existing kernel spec with this name."),
73 'debug': base_flags['debug'],
74 }
75
76 def parse_command_line(self, argv):
77 super(InstallKernelSpec, self).parse_command_line(argv)
78 # accept positional arg as profile name
79 if self.extra_args:
80 self.sourcedir = self.extra_args[0]
81 else:
82 print("No source directory specified.")
83 self.exit(1)
84
85 def start(self):
86 try:
87 self.kernel_spec_manager.install_kernel_spec(self.sourcedir,
88 kernel_name=self.kernel_name,
89 system=self.system,
90 replace=self.replace,
91 )
92 except OSError as e:
93 if e.errno == errno.EACCES:
94 print("Permission denied")
95 self.exit(1)
96 elif e.errno == errno.EEXIST:
97 print("A kernel spec is already present at %s" % e.filename)
98 self.exit(1)
99 raise
100
101 class KernelSpecApp(Application):
102 name = "ipython kernelspec"
103 description = """Manage IPython kernel specifications."""
104
105 subcommands = Dict(dict(
106 list = (ListKernelSpecs, ListKernelSpecs.description.splitlines()[0]),
107 install = (InstallKernelSpec, InstallKernelSpec.description.splitlines()[0])
108 ))
109
110 aliases = {}
111 flags = {}
112
113 def start(self):
114 if self.subapp is None:
115 print("No subcommand specified. Must specify one of: %s"% list(self.subcommands))
116 print()
117 self.print_description()
118 self.print_subcommands()
119 self.exit(1)
120 else:
121 return self.subapp.start()
@@ -311,7 +311,7 b' class Application(SingletonConfigurable):'
311 lines.append('-'*len(lines[0]))
311 lines.append('-'*len(lines[0]))
312 lines.append('')
312 lines.append('')
313 for p in wrap_paragraphs(self.subcommand_description.format(
313 for p in wrap_paragraphs(self.subcommand_description.format(
314 app=os.path.basename(self.argv[0]))):
314 app=self.name)):
315 lines.append(p)
315 lines.append(p)
316 lines.append('')
316 lines.append('')
317 for subc, (cls, help) in iteritems(self.subcommands):
317 for subc, (cls, help) in iteritems(self.subcommands):
@@ -131,7 +131,7 b' class BaseIPythonApplication(Application):'
131 help="""
131 help="""
132 The name of the IPython directory. This directory is used for logging
132 The name of the IPython directory. This directory is used for logging
133 configuration (through profiles), history storage, etc. The default
133 configuration (through profiles), history storage, etc. The default
134 is usually $HOME/.ipython. This options can also be specified through
134 is usually $HOME/.ipython. This option can also be specified through
135 the environment variable IPYTHONDIR.
135 the environment variable IPYTHONDIR.
136 """
136 """
137 )
137 )
@@ -294,7 +294,7 b' class ProfileCreate(BaseIPythonApplication):'
294
294
295
295
296 class ProfileApp(Application):
296 class ProfileApp(Application):
297 name = u'ipython-profile'
297 name = u'ipython profile'
298 description = profile_help
298 description = profile_help
299 examples = _main_examples
299 examples = _main_examples
300
300
@@ -1,6 +1,7 b''
1 import io
1 import io
2 import json
2 import json
3 import os
3 import os
4 import shutil
4 import sys
5 import sys
5
6
6 pjoin = os.path.join
7 pjoin = os.path.join
@@ -12,11 +13,13 b' from IPython.utils.traitlets import HasTraits, List, Unicode, Dict'
12 if os.name == 'nt':
13 if os.name == 'nt':
13 programdata = os.environ.get('PROGRAMDATA', None)
14 programdata = os.environ.get('PROGRAMDATA', None)
14 if programdata:
15 if programdata:
15 SYSTEM_KERNEL_DIR = pjoin(programdata, 'ipython', 'kernels')
16 SYSTEM_KERNEL_DIRS = [pjoin(programdata, 'ipython', 'kernels')]
16 else: # PROGRAMDATA is not defined by default on XP.
17 else: # PROGRAMDATA is not defined by default on XP.
17 SYSTEM_KERNEL_DIR = None
18 SYSTEM_KERNEL_DIRS = []
18 else:
19 else:
19 SYSTEM_KERNEL_DIR = "/usr/share/ipython/kernels"
20 SYSTEM_KERNEL_DIRS = ["/usr/share/ipython/kernels",
21 "/usr/local/share/ipython/kernels",
22 ]
20
23
21 NATIVE_KERNEL_NAME = 'python3' if PY3 else 'python2'
24 NATIVE_KERNEL_NAME = 'python3' if PY3 else 'python2'
22
25
@@ -79,8 +82,7 b' class KernelSpecManager(HasTraits):'
79 help="List of kernel directories to search. Later ones take priority over earlier."
82 help="List of kernel directories to search. Later ones take priority over earlier."
80 )
83 )
81 def _kernel_dirs_default(self):
84 def _kernel_dirs_default(self):
82 return [
85 return SYSTEM_KERNEL_DIRS + [
83 SYSTEM_KERNEL_DIR,
84 self.user_kernel_dir,
86 self.user_kernel_dir,
85 ]
87 ]
86
88
@@ -130,6 +132,38 b' class KernelSpecManager(HasTraits):'
130 raise NoSuchKernel(kernel_name)
132 raise NoSuchKernel(kernel_name)
131 return KernelSpec.from_resource_dir(resource_dir)
133 return KernelSpec.from_resource_dir(resource_dir)
132
134
135 def install_kernel_spec(self, source_dir, kernel_name=None, system=False,
136 replace=False):
137 """Install a kernel spec by copying its directory.
138
139 If ``kernel_name`` is not given, the basename of ``source_dir`` will
140 be used.
141
142 If ``system`` is True, it will attempt to install into the systemwide
143 kernel registry. If the process does not have appropriate permissions,
144 an :exc:`OSError` will be raised.
145
146 If ``replace`` is True, this will replace an existing kernel of the same
147 name. Otherwise, if the destination already exists, an :exc:`OSError`
148 will be raised.
149 """
150 if not kernel_name:
151 kernel_name = os.path.basename(source_dir)
152 kernel_name = kernel_name.lower()
153
154 if system:
155 if SYSTEM_KERNEL_DIRS:
156 destination = os.path.join(SYSTEM_KERNEL_DIRS[-1], kernel_name)
157 else:
158 raise EnvironmentError("No system kernel directory is available")
159 else:
160 destination = os.path.join(self.user_kernel_dir, kernel_name)
161
162 if replace and os.path.isdir(destination):
163 shutil.rmtree(destination)
164
165 shutil.copytree(source_dir, destination)
166
133 def find_kernel_specs():
167 def find_kernel_specs():
134 """Returns a dict mapping kernel names to resource directories."""
168 """Returns a dict mapping kernel names to resource directories."""
135 return KernelSpecManager().find_kernel_specs()
169 return KernelSpecManager().find_kernel_specs()
@@ -139,4 +173,9 b' def get_kernel_spec(kernel_name):'
139
173
140 Raises KeyError if the given kernel name is not found.
174 Raises KeyError if the given kernel name is not found.
141 """
175 """
142 return KernelSpecManager().get_kernel_spec(kernel_name) No newline at end of file
176 return KernelSpecManager().get_kernel_spec(kernel_name)
177
178 def install_kernel_spec(source_dir, kernel_name=None, system=False):
179 return KernelSpecManager().install_kernel_spec(source_dir, kernel_name, system)
180
181 install_kernel_spec.__doc__ = KernelSpecManager.install_kernel_spec.__doc__
@@ -3,6 +3,7 b' import os'
3 from os.path import join as pjoin
3 from os.path import join as pjoin
4 import unittest
4 import unittest
5
5
6 from IPython.testing.decorators import onlyif
6 from IPython.utils.tempdir import TemporaryDirectory
7 from IPython.utils.tempdir import TemporaryDirectory
7 from IPython.kernel import kernelspec
8 from IPython.kernel import kernelspec
8
9
@@ -13,7 +14,8 b" sample_kernel_json = {'argv':['cat', '{connection_file}'],"
13
14
14 class KernelSpecTests(unittest.TestCase):
15 class KernelSpecTests(unittest.TestCase):
15 def setUp(self):
16 def setUp(self):
16 self.tempdir = td = TemporaryDirectory()
17 td = TemporaryDirectory()
18 self.addCleanup(td.cleanup)
17 self.sample_kernel_dir = pjoin(td.name, 'kernels', 'Sample')
19 self.sample_kernel_dir = pjoin(td.name, 'kernels', 'Sample')
18 os.makedirs(self.sample_kernel_dir)
20 os.makedirs(self.sample_kernel_dir)
19 json_file = pjoin(self.sample_kernel_dir, 'kernel.json')
21 json_file = pjoin(self.sample_kernel_dir, 'kernel.json')
@@ -21,9 +23,12 b' class KernelSpecTests(unittest.TestCase):'
21 json.dump(sample_kernel_json, f)
23 json.dump(sample_kernel_json, f)
22
24
23 self.ksm = kernelspec.KernelSpecManager(ipython_dir=td.name)
25 self.ksm = kernelspec.KernelSpecManager(ipython_dir=td.name)
24
26
25 def tearDown(self):
27 td2 = TemporaryDirectory()
26 self.tempdir.cleanup()
28 self.addCleanup(td2.cleanup)
29 self.installable_kernel = td2.name
30 with open(pjoin(self.installable_kernel, 'kernel.json'), 'w') as f:
31 json.dump(sample_kernel_json, f)
27
32
28 def test_find_kernel_specs(self):
33 def test_find_kernel_specs(self):
29 kernels = self.ksm.find_kernel_specs()
34 kernels = self.ksm.find_kernel_specs()
@@ -36,4 +41,25 b' class KernelSpecTests(unittest.TestCase):'
36 self.assertEqual(ks.display_name, sample_kernel_json['display_name'])
41 self.assertEqual(ks.display_name, sample_kernel_json['display_name'])
37 self.assertEqual(ks.language, sample_kernel_json['language'])
42 self.assertEqual(ks.language, sample_kernel_json['language'])
38 self.assertEqual(ks.codemirror_mode, sample_kernel_json['language'])
43 self.assertEqual(ks.codemirror_mode, sample_kernel_json['language'])
39 self.assertEqual(ks.env, {}) No newline at end of file
44 self.assertEqual(ks.env, {})
45
46 def test_install_kernel_spec(self):
47 self.ksm.install_kernel_spec(self.installable_kernel,
48 kernel_name='tstinstalled')
49 self.assertIn('tstinstalled', self.ksm.find_kernel_specs())
50
51 with self.assertRaises(OSError):
52 self.ksm.install_kernel_spec(self.installable_kernel,
53 kernel_name='tstinstalled')
54
55 # Smoketest that this succeeds
56 self.ksm.install_kernel_spec(self.installable_kernel,
57 kernel_name='tstinstalled',
58 replace=True)
59
60 @onlyif(os.name != 'nt' and not os.access('/usr/share', os.W_OK), "needs Unix system without root privileges")
61 def test_cant_install_kernel_spec(self):
62 with self.assertRaises(OSError):
63 self.ksm.install_kernel_spec(self.installable_kernel,
64 kernel_name='tstinstalled',
65 system=True)
@@ -250,6 +250,9 b' class TerminalIPythonApp(BaseIPythonApplication, InteractiveShellApp):'
250 trust=('IPython.nbformat.sign.TrustNotebookApp',
250 trust=('IPython.nbformat.sign.TrustNotebookApp',
251 "Sign notebooks to trust their potentially unsafe contents at load."
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 subcommands['install-nbextension'] = (
257 subcommands['install-nbextension'] = (
255 "IPython.html.nbextensions.NBExtensionApp",
258 "IPython.html.nbextensions.NBExtensionApp",
General Comments 0
You need to be logged in to leave comments. Login now