You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

188 lines
6.5 KiB

4 years ago
  1. """The IPython kernel spec for Jupyter"""
  2. # Copyright (c) IPython Development Team.
  3. # Distributed under the terms of the Modified BSD License.
  4. from __future__ import print_function
  5. import errno
  6. import json
  7. import os
  8. import shutil
  9. import sys
  10. import tempfile
  11. from jupyter_client.kernelspec import KernelSpecManager
  12. pjoin = os.path.join
  13. KERNEL_NAME = 'python%i' % sys.version_info[0]
  14. # path to kernelspec resources
  15. RESOURCES = pjoin(os.path.dirname(__file__), 'resources')
  16. def make_ipkernel_cmd(mod='ipykernel_launcher', executable=None, extra_arguments=None, **kw):
  17. """Build Popen command list for launching an IPython kernel.
  18. Parameters
  19. ----------
  20. mod : str, optional (default 'ipykernel')
  21. A string of an IPython module whose __main__ starts an IPython kernel
  22. executable : str, optional (default sys.executable)
  23. The Python executable to use for the kernel process.
  24. extra_arguments : list, optional
  25. A list of extra arguments to pass when executing the launch code.
  26. Returns
  27. -------
  28. A Popen command list
  29. """
  30. if executable is None:
  31. executable = sys.executable
  32. extra_arguments = extra_arguments or []
  33. arguments = [executable, '-m', mod, '-f', '{connection_file}']
  34. arguments.extend(extra_arguments)
  35. return arguments
  36. def get_kernel_dict(extra_arguments=None):
  37. """Construct dict for kernel.json"""
  38. return {
  39. 'argv': make_ipkernel_cmd(extra_arguments=extra_arguments),
  40. 'display_name': 'Python %i' % sys.version_info[0],
  41. 'language': 'python',
  42. }
  43. def write_kernel_spec(path=None, overrides=None, extra_arguments=None):
  44. """Write a kernel spec directory to `path`
  45. If `path` is not specified, a temporary directory is created.
  46. If `overrides` is given, the kernelspec JSON is updated before writing.
  47. The path to the kernelspec is always returned.
  48. """
  49. if path is None:
  50. path = os.path.join(tempfile.mkdtemp(suffix='_kernels'), KERNEL_NAME)
  51. # stage resources
  52. shutil.copytree(RESOURCES, path)
  53. # write kernel.json
  54. kernel_dict = get_kernel_dict(extra_arguments)
  55. if overrides:
  56. kernel_dict.update(overrides)
  57. with open(pjoin(path, 'kernel.json'), 'w') as f:
  58. json.dump(kernel_dict, f, indent=1)
  59. return path
  60. def install(kernel_spec_manager=None, user=False, kernel_name=KERNEL_NAME, display_name=None,
  61. prefix=None, profile=None):
  62. """Install the IPython kernelspec for Jupyter
  63. Parameters
  64. ----------
  65. kernel_spec_manager: KernelSpecManager [optional]
  66. A KernelSpecManager to use for installation.
  67. If none provided, a default instance will be created.
  68. user: bool [default: False]
  69. Whether to do a user-only install, or system-wide.
  70. kernel_name: str, optional
  71. Specify a name for the kernelspec.
  72. This is needed for having multiple IPython kernels for different environments.
  73. display_name: str, optional
  74. Specify the display name for the kernelspec
  75. profile: str, optional
  76. Specify a custom profile to be loaded by the kernel.
  77. prefix: str, optional
  78. Specify an install prefix for the kernelspec.
  79. This is needed to install into a non-default location, such as a conda/virtual-env.
  80. Returns
  81. -------
  82. The path where the kernelspec was installed.
  83. """
  84. if kernel_spec_manager is None:
  85. kernel_spec_manager = KernelSpecManager()
  86. if (kernel_name != KERNEL_NAME) and (display_name is None):
  87. # kernel_name is specified and display_name is not
  88. # default display_name to kernel_name
  89. display_name = kernel_name
  90. overrides = {}
  91. if display_name:
  92. overrides["display_name"] = display_name
  93. if profile:
  94. extra_arguments = ["--profile", profile]
  95. if not display_name:
  96. # add the profile to the default display name
  97. overrides["display_name"] = 'Python %i [profile=%s]' % (sys.version_info[0], profile)
  98. else:
  99. extra_arguments = None
  100. path = write_kernel_spec(overrides=overrides, extra_arguments=extra_arguments)
  101. dest = kernel_spec_manager.install_kernel_spec(
  102. path, kernel_name=kernel_name, user=user, prefix=prefix)
  103. # cleanup afterward
  104. shutil.rmtree(path)
  105. return dest
  106. # Entrypoint
  107. from traitlets.config import Application
  108. class InstallIPythonKernelSpecApp(Application):
  109. """Dummy app wrapping argparse"""
  110. name = 'ipython-kernel-install'
  111. def initialize(self, argv=None):
  112. if argv is None:
  113. argv = sys.argv[1:]
  114. self.argv = argv
  115. def start(self):
  116. import argparse
  117. parser = argparse.ArgumentParser(prog=self.name,
  118. description="Install the IPython kernel spec.")
  119. parser.add_argument('--user', action='store_true',
  120. help="Install for the current user instead of system-wide")
  121. parser.add_argument('--name', type=str, default=KERNEL_NAME,
  122. help="Specify a name for the kernelspec."
  123. " This is needed to have multiple IPython kernels at the same time.")
  124. parser.add_argument('--display-name', type=str,
  125. help="Specify the display name for the kernelspec."
  126. " This is helpful when you have multiple IPython kernels.")
  127. parser.add_argument('--profile', type=str,
  128. help="Specify an IPython profile to load. "
  129. "This can be used to create custom versions of the kernel.")
  130. parser.add_argument('--prefix', type=str,
  131. help="Specify an install prefix for the kernelspec."
  132. " This is needed to install into a non-default location, such as a conda/virtual-env.")
  133. parser.add_argument('--sys-prefix', action='store_const', const=sys.prefix, dest='prefix',
  134. help="Install to Python's sys.prefix."
  135. " Shorthand for --prefix='%s'. For use in conda/virtual-envs." % sys.prefix)
  136. opts = parser.parse_args(self.argv)
  137. try:
  138. dest = install(user=opts.user, kernel_name=opts.name, profile=opts.profile,
  139. prefix=opts.prefix, display_name=opts.display_name)
  140. except OSError as e:
  141. if e.errno == errno.EACCES:
  142. print(e, file=sys.stderr)
  143. if opts.user:
  144. print("Perhaps you want `sudo` or `--user`?", file=sys.stderr)
  145. self.exit(1)
  146. raise
  147. print("Installed kernelspec %s in %s" % (opts.name, dest))
  148. if __name__ == '__main__':
  149. InstallIPythonKernelSpecApp.launch_instance()