|
13 | 13 | from jupyter_core.application import (
|
14 | 14 | JupyterApp, base_flags, base_aliases
|
15 | 15 | )
|
16 |
| -from traitlets import Instance, Dict, Unicode, Bool |
| 16 | +from traitlets import Instance, Dict, Unicode, Bool, List |
17 | 17 |
|
18 | 18 | from . import __version__
|
19 | 19 | from .kernelspec import KernelSpecManager
|
20 | 20 |
|
| 21 | +try: |
| 22 | + raw_input |
| 23 | +except NameError: |
| 24 | + # py3 |
| 25 | + raw_input = input |
| 26 | + |
21 | 27 | class ListKernelSpecs(JupyterApp):
|
22 | 28 | version = __version__
|
23 | 29 | description = """List installed kernel specifications."""
|
@@ -145,6 +151,61 @@ def start(self):
|
145 | 151 | self.exit(1)
|
146 | 152 | raise
|
147 | 153 |
|
| 154 | +class RemoveKernelSpec(JupyterApp): |
| 155 | + version = __version__ |
| 156 | + description = """Remove one or more Jupyter kernelspecs by name.""" |
| 157 | + examples = """jupyter kernelspec remove python2 [my_kernel ...]""" |
| 158 | + |
| 159 | + force = Bool(False, config=True, |
| 160 | + help="""Force removal, don't prompt for confirmation.""" |
| 161 | + ) |
| 162 | + spec_names = List(Unicode()) |
| 163 | + |
| 164 | + kernel_spec_manager = Instance(KernelSpecManager) |
| 165 | + def _kernel_spec_manager_default(self): |
| 166 | + return KernelSpecManager(data_dir=self.data_dir, parent=self) |
| 167 | + |
| 168 | + flags = { |
| 169 | + 'f': ({'RemoveKernelSpec': {'force': True}}, force.get_metadata('help')), |
| 170 | + } |
| 171 | + flags.update(JupyterApp.flags) |
| 172 | + |
| 173 | + def parse_command_line(self, argv): |
| 174 | + super(RemoveKernelSpec, self).parse_command_line(argv) |
| 175 | + # accept positional arg as profile name |
| 176 | + if self.extra_args: |
| 177 | + self.spec_names = sorted(set(self.extra_args)) # remove duplicates |
| 178 | + else: |
| 179 | + self.exit("No kernelspec specified.") |
| 180 | + |
| 181 | + def start(self): |
| 182 | + self.kernel_spec_manager.ensure_native_kernel = False |
| 183 | + spec_paths = self.kernel_spec_manager.find_kernel_specs() |
| 184 | + missing = set(self.spec_names).difference(set(spec_paths)) |
| 185 | + if missing: |
| 186 | + self.exit("Couldn't find kernel spec(s): %s" % ', '.join(missing)) |
| 187 | + |
| 188 | + if not self.force: |
| 189 | + print("Kernel specs to remove:") |
| 190 | + for name in self.spec_names: |
| 191 | + print(" %s\t%s" % (name.ljust(20), spec_paths[name])) |
| 192 | + answer = raw_input("Remove %i kernel specs [y/N]: " % len(self.spec_names)) |
| 193 | + if not answer.lower().startswith('y'): |
| 194 | + return |
| 195 | + |
| 196 | + for kernel_name in self.spec_names: |
| 197 | + try: |
| 198 | + path = self.kernel_spec_manager.remove_kernel_spec(kernel_name) |
| 199 | + except OSError as e: |
| 200 | + if e.errno == errno.EACCES: |
| 201 | + print(e, file=sys.stderr) |
| 202 | + print("Perhaps you want sudo?", file=sys.stderr) |
| 203 | + self.exit(1) |
| 204 | + else: |
| 205 | + raise |
| 206 | + self.log.info("Removed %s", path) |
| 207 | + |
| 208 | + |
148 | 209 | class InstallNativeKernelSpec(JupyterApp):
|
149 | 210 | version = __version__
|
150 | 211 | description = """[DEPRECATED] Install the IPython kernel spec directory for this Python."""
|
@@ -189,6 +250,8 @@ class KernelSpecApp(Application):
|
189 | 250 | subcommands = Dict({
|
190 | 251 | 'list': (ListKernelSpecs, ListKernelSpecs.description.splitlines()[0]),
|
191 | 252 | 'install': (InstallKernelSpec, InstallKernelSpec.description.splitlines()[0]),
|
| 253 | + 'uninstall': (RemoveKernelSpec, "Alias for remove"), |
| 254 | + 'remove': (RemoveKernelSpec, RemoveKernelSpec.description.splitlines()[0]), |
192 | 255 | 'install-self': (InstallNativeKernelSpec, InstallNativeKernelSpec.description.splitlines()[0]),
|
193 | 256 | })
|
194 | 257 |
|
|
0 commit comments