##// END OF EJS Templates
Default to systemwide installation for kernelspecs
Thomas Kluyver -
Show More
@@ -1,221 +1,222 b''
1 1 import io
2 2 import json
3 3 import os
4 4 import shutil
5 5 import sys
6 6
7 7 pjoin = os.path.join
8 8
9 9 from IPython.utils.path import get_ipython_dir
10 10 from IPython.utils.py3compat import PY3
11 11 from IPython.utils.traitlets import HasTraits, List, Unicode, Dict, Any
12 12 from .launcher import make_ipkernel_cmd
13 13
14 14 if os.name == 'nt':
15 15 programdata = os.environ.get('PROGRAMDATA', None)
16 16 if programdata:
17 17 SYSTEM_KERNEL_DIRS = [pjoin(programdata, 'ipython', 'kernels')]
18 18 else: # PROGRAMDATA is not defined by default on XP.
19 19 SYSTEM_KERNEL_DIRS = []
20 20 else:
21 21 SYSTEM_KERNEL_DIRS = ["/usr/share/jupyter/kernels",
22 22 "/usr/local/share/jupyter/kernels",
23 23 ]
24 24
25 25 NATIVE_KERNEL_NAME = 'python3' if PY3 else 'python2'
26 26
27 27 def _pythonfirst(s):
28 28 "Sort key function that will put strings starting with 'python' first."
29 29 if s == NATIVE_KERNEL_NAME:
30 30 return ' ' + s # Two spaces to sort this first of all
31 31 elif s.startswith('python'):
32 32 # Space is not valid in kernel names, so this should sort first
33 33 return ' ' + s
34 34 return s
35 35
36 36 class KernelSpec(HasTraits):
37 37 argv = List()
38 38 display_name = Unicode()
39 39 env = Dict()
40 40 resource_dir = Unicode()
41 41
42 42 @classmethod
43 43 def from_resource_dir(cls, resource_dir):
44 44 """Create a KernelSpec object by reading kernel.json
45 45
46 46 Pass the path to the *directory* containing kernel.json.
47 47 """
48 48 kernel_file = pjoin(resource_dir, 'kernel.json')
49 49 with io.open(kernel_file, 'r', encoding='utf-8') as f:
50 50 kernel_dict = json.load(f)
51 51 return cls(resource_dir=resource_dir, **kernel_dict)
52 52
53 53 def to_dict(self):
54 54 d = dict(argv=self.argv,
55 55 env=self.env,
56 56 display_name=self.display_name,
57 57 )
58 58
59 59 return d
60 60
61 61 def to_json(self):
62 62 return json.dumps(self.to_dict())
63 63
64 64 def _is_kernel_dir(path):
65 65 """Is ``path`` a kernel directory?"""
66 66 return os.path.isdir(path) and os.path.isfile(pjoin(path, 'kernel.json'))
67 67
68 68 def _list_kernels_in(dir):
69 69 """Return a mapping of kernel names to resource directories from dir.
70 70
71 71 If dir is None or does not exist, returns an empty dict.
72 72 """
73 73 if dir is None or not os.path.isdir(dir):
74 74 return {}
75 75 return {f.lower(): pjoin(dir, f) for f in os.listdir(dir)
76 76 if _is_kernel_dir(pjoin(dir, f))}
77 77
78 78 class NoSuchKernel(KeyError):
79 79 def __init__(self, name):
80 80 self.name = name
81 81
82 82 class KernelSpecManager(HasTraits):
83 83 ipython_dir = Unicode()
84 84 def _ipython_dir_default(self):
85 85 return get_ipython_dir()
86 86
87 87 user_kernel_dir = Unicode()
88 88 def _user_kernel_dir_default(self):
89 89 return pjoin(self.ipython_dir, 'kernels')
90 90
91 91 @property
92 92 def env_kernel_dir(self):
93 93 return pjoin(sys.prefix, 'share', 'jupyter', 'kernels')
94 94
95 95 kernel_dirs = List(
96 96 help="List of kernel directories to search. Later ones take priority over earlier."
97 97 )
98 98 def _kernel_dirs_default(self):
99 99 dirs = SYSTEM_KERNEL_DIRS[:]
100 100 if self.env_kernel_dir not in dirs:
101 101 dirs.append(self.env_kernel_dir)
102 102 dirs.append(self.user_kernel_dir)
103 103 return dirs
104 104
105 105 @property
106 106 def _native_kernel_dict(self):
107 107 """Makes a kernel directory for the native kernel.
108 108
109 109 The native kernel is the kernel using the same Python runtime as this
110 110 process. This will put its informatino in the user kernels directory.
111 111 """
112 112 return {'argv': make_ipkernel_cmd(),
113 113 'display_name': 'IPython (Python %d)' % (3 if PY3 else 2),
114 114 }
115 115
116 116 @property
117 117 def _native_kernel_resource_dir(self):
118 118 return pjoin(os.path.dirname(__file__), 'resources')
119 119
120 120 def find_kernel_specs(self):
121 121 """Returns a dict mapping kernel names to resource directories."""
122 122 d = {}
123 123 for kernel_dir in self.kernel_dirs:
124 124 d.update(_list_kernels_in(kernel_dir))
125 125
126 126 d[NATIVE_KERNEL_NAME] = self._native_kernel_resource_dir
127 127 return d
128 128 # TODO: Caching?
129 129
130 130 def get_kernel_spec(self, kernel_name):
131 131 """Returns a :class:`KernelSpec` instance for the given kernel_name.
132 132
133 133 Raises :exc:`NoSuchKernel` if the given kernel name is not found.
134 134 """
135 135 if kernel_name in {'python', NATIVE_KERNEL_NAME}:
136 136 return KernelSpec(resource_dir=self._native_kernel_resource_dir,
137 137 **self._native_kernel_dict)
138 138
139 139 d = self.find_kernel_specs()
140 140 try:
141 141 resource_dir = d[kernel_name.lower()]
142 142 except KeyError:
143 143 raise NoSuchKernel(kernel_name)
144 144 return KernelSpec.from_resource_dir(resource_dir)
145 145
146 def _get_destination_dir(self, kernel_name, system=False):
147 if system:
146 def _get_destination_dir(self, kernel_name, user=False):
147 if user:
148 return os.path.join(self.user_kernel_dir, kernel_name)
149 else:
148 150 if SYSTEM_KERNEL_DIRS:
149 151 return os.path.join(SYSTEM_KERNEL_DIRS[-1], kernel_name)
150 152 else:
151 153 raise EnvironmentError("No system kernel directory is available")
152 else:
153 return os.path.join(self.user_kernel_dir, kernel_name)
154 154
155 def install_kernel_spec(self, source_dir, kernel_name=None, system=False,
155
156 def install_kernel_spec(self, source_dir, kernel_name=None, user=False,
156 157 replace=False):
157 158 """Install a kernel spec by copying its directory.
158 159
159 160 If ``kernel_name`` is not given, the basename of ``source_dir`` will
160 161 be used.
161 162
162 163 If ``system`` is True, it will attempt to install into the systemwide
163 164 kernel registry. If the process does not have appropriate permissions,
164 165 an :exc:`OSError` will be raised.
165 166
166 167 If ``replace`` is True, this will replace an existing kernel of the same
167 168 name. Otherwise, if the destination already exists, an :exc:`OSError`
168 169 will be raised.
169 170 """
170 171 if not kernel_name:
171 172 kernel_name = os.path.basename(source_dir)
172 173 kernel_name = kernel_name.lower()
173 174
174 destination = self._get_destination_dir(kernel_name, system=system)
175 destination = self._get_destination_dir(kernel_name, user=user)
175 176
176 177 if replace and os.path.isdir(destination):
177 178 shutil.rmtree(destination)
178 179
179 180 shutil.copytree(source_dir, destination)
180 181
181 def install_native_kernel_spec(self, system=False):
182 def install_native_kernel_spec(self, user=False):
182 183 """Install the native kernel spec to the filesystem
183 184
184 185 This allows a Python 3 frontend to use a Python 2 kernel, or vice versa.
185 186 The kernelspec will be written pointing to the Python executable on
186 187 which this is run.
187 188
188 189 If ``system`` is True, it will attempt to install into the systemwide
189 190 kernel registry. If the process does not have appropriate permissions,
190 191 an :exc:`OSError` will be raised.
191 192 """
192 path = self._get_destination_dir(NATIVE_KERNEL_NAME, system=system)
193 path = self._get_destination_dir(NATIVE_KERNEL_NAME, user=user)
193 194 os.makedirs(path, mode=0o755)
194 195 with open(pjoin(path, 'kernel.json'), 'w') as f:
195 196 json.dump(self._native_kernel_dict, f, indent=1)
196 197 copy_from = self._native_kernel_resource_dir
197 198 for file in os.listdir(copy_from):
198 199 shutil.copy(pjoin(copy_from, file), path)
199 200 return path
200 201
201 202 def find_kernel_specs():
202 203 """Returns a dict mapping kernel names to resource directories."""
203 204 return KernelSpecManager().find_kernel_specs()
204 205
205 206 def get_kernel_spec(kernel_name):
206 207 """Returns a :class:`KernelSpec` instance for the given kernel_name.
207 208
208 209 Raises KeyError if the given kernel name is not found.
209 210 """
210 211 return KernelSpecManager().get_kernel_spec(kernel_name)
211 212
212 def install_kernel_spec(source_dir, kernel_name=None, system=False, replace=False):
213 def install_kernel_spec(source_dir, kernel_name=None, user=False, replace=False):
213 214 return KernelSpecManager().install_kernel_spec(source_dir, kernel_name,
214 system, replace)
215 user, replace)
215 216
216 217 install_kernel_spec.__doc__ = KernelSpecManager.install_kernel_spec.__doc__
217 218
218 def install_native_kernel_spec(self, system=False):
219 return KernelSpecManager().install_native_kernel_spec(system=system)
219 def install_native_kernel_spec(user=False):
220 return KernelSpecManager().install_native_kernel_spec(user=user)
220 221
221 222 install_native_kernel_spec.__doc__ = KernelSpecManager.install_native_kernel_spec.__doc__
General Comments 0
You need to be logged in to leave comments. Login now