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= |
|
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 option |
|
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 |
|
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 = |
|
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 |
|
|
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. |
|
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