File size: 12,048 Bytes
d1ceb73 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 |
"""Apps for managing kernel specs."""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
from __future__ import annotations
import errno
import json
import os.path
import sys
import typing as t
from jupyter_core.application import JupyterApp, base_aliases, base_flags
from traitlets import Bool, Dict, Instance, List, Unicode
from traitlets.config.application import Application
from . import __version__
from .kernelspec import KernelSpecManager
from .provisioning.factory import KernelProvisionerFactory
class ListKernelSpecs(JupyterApp):
"""An app to list kernel specs."""
version = __version__
description = """List installed kernel specifications."""
kernel_spec_manager = Instance(KernelSpecManager)
json_output = Bool(
False,
help="output spec name and location as machine-readable json.",
config=True,
)
flags = {
"json": (
{"ListKernelSpecs": {"json_output": True}},
"output spec name and location as machine-readable json.",
),
"debug": base_flags["debug"],
}
def _kernel_spec_manager_default(self) -> KernelSpecManager:
return KernelSpecManager(parent=self, data_dir=self.data_dir)
def start(self) -> dict[str, t.Any] | None: # type:ignore[override]
"""Start the application."""
paths = self.kernel_spec_manager.find_kernel_specs()
specs = self.kernel_spec_manager.get_all_specs()
if not self.json_output:
if not specs:
print("No kernels available")
return None
# pad to width of longest kernel name
name_len = len(sorted(paths, key=lambda name: len(name))[-1])
def path_key(item: t.Any) -> t.Any:
"""sort key function for Jupyter path priority"""
path = item[1]
for idx, prefix in enumerate(self.jupyter_path):
if path.startswith(prefix):
return (idx, path)
# not in jupyter path, artificially added to the front
return (-1, path)
print("Available kernels:")
for kernelname, path in sorted(paths.items(), key=path_key):
print(f" {kernelname.ljust(name_len)} {path}")
else:
print(json.dumps({"kernelspecs": specs}, indent=2))
return specs
class InstallKernelSpec(JupyterApp):
"""An app to install a kernel spec."""
version = __version__
description = """Install a kernel specification directory.
Given a SOURCE DIRECTORY containing a kernel spec,
jupyter will copy that directory into one of the Jupyter kernel directories.
The default is to install kernelspecs for all users.
`--user` can be specified to install a kernel only for the current user.
"""
examples = """
jupyter kernelspec install /path/to/my_kernel --user
"""
usage = "jupyter kernelspec install SOURCE_DIR [--options]"
kernel_spec_manager = Instance(KernelSpecManager)
def _kernel_spec_manager_default(self) -> KernelSpecManager:
return KernelSpecManager(data_dir=self.data_dir)
sourcedir = Unicode()
kernel_name = Unicode("", config=True, help="Install the kernel spec with this name")
def _kernel_name_default(self) -> str:
return os.path.basename(self.sourcedir)
user = Bool(
False,
config=True,
help="""
Try to install the kernel spec to the per-user directory instead of
the system or environment directory.
""",
)
prefix = Unicode(
"",
config=True,
help="""Specify a prefix to install to, e.g. an env.
The kernelspec will be installed in PREFIX/share/jupyter/kernels/
""",
)
replace = Bool(False, config=True, help="Replace any existing kernel spec with this name.")
aliases = {
"name": "InstallKernelSpec.kernel_name",
"prefix": "InstallKernelSpec.prefix",
}
aliases.update(base_aliases)
flags = {
"user": (
{"InstallKernelSpec": {"user": True}},
"Install to the per-user kernel registry",
),
"replace": (
{"InstallKernelSpec": {"replace": True}},
"Replace any existing kernel spec with this name.",
),
"sys-prefix": (
{"InstallKernelSpec": {"prefix": sys.prefix}},
"Install to Python's sys.prefix. Useful in conda/virtual environments.",
),
"debug": base_flags["debug"],
}
def parse_command_line(self, argv: None | list[str]) -> None: # type:ignore[override]
"""Parse the command line args."""
super().parse_command_line(argv)
# accept positional arg as profile name
if self.extra_args:
self.sourcedir = self.extra_args[0]
else:
print("No source directory specified.", file=sys.stderr)
self.exit(1)
def start(self) -> None:
"""Start the application."""
if self.user and self.prefix:
self.exit("Can't specify both user and prefix. Please choose one or the other.")
try:
self.kernel_spec_manager.install_kernel_spec(
self.sourcedir,
kernel_name=self.kernel_name,
user=self.user,
prefix=self.prefix,
replace=self.replace,
)
except OSError as e:
if e.errno == errno.EACCES:
print(e, file=sys.stderr)
if not self.user:
print("Perhaps you want to install with `sudo` or `--user`?", file=sys.stderr)
self.exit(1)
elif e.errno == errno.EEXIST:
print(f"A kernel spec is already present at {e.filename}", file=sys.stderr)
self.exit(1)
raise
class RemoveKernelSpec(JupyterApp):
"""An app to remove a kernel spec."""
version = __version__
description = """Remove one or more Jupyter kernelspecs by name."""
examples = """jupyter kernelspec remove python2 [my_kernel ...]"""
force = Bool(False, config=True, help="""Force removal, don't prompt for confirmation.""")
spec_names = List(Unicode())
kernel_spec_manager = Instance(KernelSpecManager)
def _kernel_spec_manager_default(self) -> KernelSpecManager:
return KernelSpecManager(data_dir=self.data_dir, parent=self)
flags = {
"f": ({"RemoveKernelSpec": {"force": True}}, force.help),
}
flags.update(JupyterApp.flags)
def parse_command_line(self, argv: list[str] | None) -> None: # type:ignore[override]
"""Parse the command line args."""
super().parse_command_line(argv)
# accept positional arg as profile name
if self.extra_args:
self.spec_names = sorted(set(self.extra_args)) # remove duplicates
else:
self.exit("No kernelspec specified.")
def start(self) -> None:
"""Start the application."""
self.kernel_spec_manager.ensure_native_kernel = False
spec_paths = self.kernel_spec_manager.find_kernel_specs()
missing = set(self.spec_names).difference(set(spec_paths))
if missing:
self.exit("Couldn't find kernel spec(s): %s" % ", ".join(missing))
if not (self.force or self.answer_yes):
print("Kernel specs to remove:")
for name in self.spec_names:
path = spec_paths.get(name, name)
print(f" {name.ljust(20)}\t{path.ljust(20)}")
answer = input("Remove %i kernel specs [y/N]: " % len(self.spec_names))
if not answer.lower().startswith("y"):
return
for kernel_name in self.spec_names:
try:
path = self.kernel_spec_manager.remove_kernel_spec(kernel_name)
except OSError as e:
if e.errno == errno.EACCES:
print(e, file=sys.stderr)
print("Perhaps you want sudo?", file=sys.stderr)
self.exit(1)
else:
raise
print(f"Removed {path}")
class InstallNativeKernelSpec(JupyterApp):
"""An app to install the native kernel spec."""
version = __version__
description = """[DEPRECATED] Install the IPython kernel spec directory for this Python."""
kernel_spec_manager = Instance(KernelSpecManager)
def _kernel_spec_manager_default(self) -> KernelSpecManager: # pragma: no cover
return KernelSpecManager(data_dir=self.data_dir)
user = Bool(
False,
config=True,
help="""
Try to install the kernel spec to the per-user directory instead of
the system or environment directory.
""",
)
flags = {
"user": (
{"InstallNativeKernelSpec": {"user": True}},
"Install to the per-user kernel registry",
),
"debug": base_flags["debug"],
}
def start(self) -> None: # pragma: no cover
"""Start the application."""
self.log.warning(
"`jupyter kernelspec install-self` is DEPRECATED as of 4.0."
" You probably want `ipython kernel install` to install the IPython kernelspec."
)
try:
from ipykernel import kernelspec
except ModuleNotFoundError:
print("ipykernel not available, can't install its spec.", file=sys.stderr)
self.exit(1)
try:
kernelspec.install(self.kernel_spec_manager, user=self.user)
except OSError as e:
if e.errno == errno.EACCES:
print(e, file=sys.stderr)
if not self.user:
print(
"Perhaps you want to install with `sudo` or `--user`?",
file=sys.stderr,
)
self.exit(1)
self.exit(e) # type:ignore[arg-type]
class ListProvisioners(JupyterApp):
"""An app to list provisioners."""
version = __version__
description = """List available provisioners for use in kernel specifications."""
def start(self) -> None:
"""Start the application."""
kfp = KernelProvisionerFactory.instance(parent=self)
print("Available kernel provisioners:")
provisioners = kfp.get_provisioner_entries()
# pad to width of longest kernel name
name_len = len(sorted(provisioners, key=lambda name: len(name))[-1])
for name in sorted(provisioners):
print(f" {name.ljust(name_len)} {provisioners[name]}")
class KernelSpecApp(Application):
"""An app to manage kernel specs."""
version = __version__
name = "jupyter kernelspec"
description = """Manage Jupyter kernel specifications."""
subcommands = Dict(
{
"list": (ListKernelSpecs, ListKernelSpecs.description.splitlines()[0]),
"install": (
InstallKernelSpec,
InstallKernelSpec.description.splitlines()[0],
),
"uninstall": (RemoveKernelSpec, "Alias for remove"),
"remove": (RemoveKernelSpec, RemoveKernelSpec.description.splitlines()[0]),
"install-self": (
InstallNativeKernelSpec,
InstallNativeKernelSpec.description.splitlines()[0],
),
"provisioners": (ListProvisioners, ListProvisioners.description.splitlines()[0]),
}
)
aliases = {}
flags = {}
def start(self) -> None:
"""Start the application."""
if self.subapp is None:
print("No subcommand specified. Must specify one of: %s" % list(self.subcommands))
print()
self.print_description()
self.print_subcommands()
self.exit(1)
else:
return self.subapp.start()
if __name__ == "__main__":
KernelSpecApp.launch_instance()
|