"""The IPython kernel spec for Jupyter""" # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. from __future__ import print_function import errno import json import os import shutil import sys import tempfile from jupyter_client.kernelspec import KernelSpecManager pjoin = os.path.join KERNEL_NAME = 'python%i' % sys.version_info[0] # path to kernelspec resources RESOURCES = pjoin(os.path.dirname(__file__), 'resources') def make_ipkernel_cmd(mod='ipykernel_launcher', executable=None, extra_arguments=None, **kw): """Build Popen command list for launching an IPython kernel. Parameters ---------- mod : str, optional (default 'ipykernel') A string of an IPython module whose __main__ starts an IPython kernel executable : str, optional (default sys.executable) The Python executable to use for the kernel process. extra_arguments : list, optional A list of extra arguments to pass when executing the launch code. Returns ------- A Popen command list """ if executable is None: executable = sys.executable extra_arguments = extra_arguments or [] arguments = [executable, '-m', mod, '-f', '{connection_file}'] arguments.extend(extra_arguments) return arguments def get_kernel_dict(extra_arguments=None): """Construct dict for kernel.json""" return { 'argv': make_ipkernel_cmd(extra_arguments=extra_arguments), 'display_name': 'Python %i' % sys.version_info[0], 'language': 'python', } def write_kernel_spec(path=None, overrides=None, extra_arguments=None): """Write a kernel spec directory to `path` If `path` is not specified, a temporary directory is created. If `overrides` is given, the kernelspec JSON is updated before writing. The path to the kernelspec is always returned. """ if path is None: path = os.path.join(tempfile.mkdtemp(suffix='_kernels'), KERNEL_NAME) # stage resources shutil.copytree(RESOURCES, path) # write kernel.json kernel_dict = get_kernel_dict(extra_arguments) if overrides: kernel_dict.update(overrides) with open(pjoin(path, 'kernel.json'), 'w') as f: json.dump(kernel_dict, f, indent=1) return path def install(kernel_spec_manager=None, user=False, kernel_name=KERNEL_NAME, display_name=None, prefix=None, profile=None): """Install the IPython kernelspec for Jupyter Parameters ---------- kernel_spec_manager: KernelSpecManager [optional] A KernelSpecManager to use for installation. If none provided, a default instance will be created. user: bool [default: False] Whether to do a user-only install, or system-wide. kernel_name: str, optional Specify a name for the kernelspec. This is needed for having multiple IPython kernels for different environments. display_name: str, optional Specify the display name for the kernelspec profile: str, optional Specify a custom profile to be loaded by the kernel. prefix: str, optional Specify an install prefix for the kernelspec. This is needed to install into a non-default location, such as a conda/virtual-env. Returns ------- The path where the kernelspec was installed. """ if kernel_spec_manager is None: kernel_spec_manager = KernelSpecManager() if (kernel_name != KERNEL_NAME) and (display_name is None): # kernel_name is specified and display_name is not # default display_name to kernel_name display_name = kernel_name overrides = {} if display_name: overrides["display_name"] = display_name if profile: extra_arguments = ["--profile", profile] if not display_name: # add the profile to the default display name overrides["display_name"] = 'Python %i [profile=%s]' % (sys.version_info[0], profile) else: extra_arguments = None path = write_kernel_spec(overrides=overrides, extra_arguments=extra_arguments) dest = kernel_spec_manager.install_kernel_spec( path, kernel_name=kernel_name, user=user, prefix=prefix) # cleanup afterward shutil.rmtree(path) return dest # Entrypoint from traitlets.config import Application class InstallIPythonKernelSpecApp(Application): """Dummy app wrapping argparse""" name = 'ipython-kernel-install' def initialize(self, argv=None): if argv is None: argv = sys.argv[1:] self.argv = argv def start(self): import argparse parser = argparse.ArgumentParser(prog=self.name, description="Install the IPython kernel spec.") parser.add_argument('--user', action='store_true', help="Install for the current user instead of system-wide") parser.add_argument('--name', type=str, default=KERNEL_NAME, help="Specify a name for the kernelspec." " This is needed to have multiple IPython kernels at the same time.") parser.add_argument('--display-name', type=str, help="Specify the display name for the kernelspec." " This is helpful when you have multiple IPython kernels.") parser.add_argument('--profile', type=str, help="Specify an IPython profile to load. " "This can be used to create custom versions of the kernel.") parser.add_argument('--prefix', type=str, help="Specify an install prefix for the kernelspec." " This is needed to install into a non-default location, such as a conda/virtual-env.") parser.add_argument('--sys-prefix', action='store_const', const=sys.prefix, dest='prefix', help="Install to Python's sys.prefix." " Shorthand for --prefix='%s'. For use in conda/virtual-envs." % sys.prefix) opts = parser.parse_args(self.argv) try: dest = install(user=opts.user, kernel_name=opts.name, profile=opts.profile, prefix=opts.prefix, display_name=opts.display_name) except OSError as e: if e.errno == errno.EACCES: print(e, file=sys.stderr) if opts.user: print("Perhaps you want `sudo` or `--user`?", file=sys.stderr) self.exit(1) raise print("Installed kernelspec %s in %s" % (opts.name, dest)) if __name__ == '__main__': InstallIPythonKernelSpecApp.launch_instance()