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