diff --git a/ipykernel/kernelspec.py b/ipykernel/kernelspec.py index 10d320859..dcd57d4c1 100644 --- a/ipykernel/kernelspec.py +++ b/ipykernel/kernelspec.py @@ -44,22 +44,22 @@ def make_ipkernel_cmd(mod='ipykernel', executable=None, extra_arguments=None, ** if executable is None: executable = sys.executable extra_arguments = extra_arguments or [] - arguments = [ executable, '-m', mod, '-f', '{connection_file}' ] + arguments = [executable, '-m', mod, '-f', '{connection_file}'] arguments.extend(extra_arguments) return arguments -def get_kernel_dict(): +def get_kernel_dict(extra_arguments=None): """Construct dict for kernel.json""" return { - 'argv': make_ipkernel_cmd(), + '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): +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. @@ -73,7 +73,8 @@ def write_kernel_spec(path=None, overrides=None): # stage resources shutil.copytree(RESOURCES, path) # write kernel.json - kernel_dict = get_kernel_dict() + kernel_dict = get_kernel_dict(extra_arguments) + if overrides: kernel_dict.update(overrides) with open(pjoin(path, 'kernel.json'), 'w') as f: @@ -82,7 +83,8 @@ def write_kernel_spec(path=None, overrides=None): return path -def install(kernel_spec_manager=None, user=False, kernel_name=KERNEL_NAME, display_name=None, prefix=None): +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 @@ -96,12 +98,14 @@ def install(kernel_spec_manager=None, user=False, kernel_name=KERNEL_NAME, displ 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. - display_name: str, optional - Specify the display name for the kernelspec - + Returns ------- @@ -114,13 +118,19 @@ def install(kernel_spec_manager=None, user=False, kernel_name=KERNEL_NAME, displ # kernel_name is specified and display_name is not # default display_name to kernel_name display_name = kernel_name + overrides = {} if display_name: - overrides = dict(display_name=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: - overrides = None - path = write_kernel_spec(overrides=overrides) - dest = kernel_spec_manager.install_kernel_spec(path, - kernel_name=kernel_name, user=user, prefix=prefix) + 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 @@ -151,6 +161,9 @@ def start(self): 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.") @@ -159,9 +172,8 @@ def start(self): " 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, prefix=opts.prefix, - display_name=opts.display_name, - ) + 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) diff --git a/ipykernel/tests/test_kernelspec.py b/ipykernel/tests/test_kernelspec.py index c849436fe..f1c0c4282 100644 --- a/ipykernel/tests/test_kernelspec.py +++ b/ipykernel/tests/test_kernelspec.py @@ -52,6 +52,18 @@ def test_get_kernel_dict(): assert_kernel_dict(d) +def assert_kernel_dict_with_profile(d): + nt.assert_equal(d['argv'], make_ipkernel_cmd( + extra_arguments=["--profile", "test"])) + nt.assert_equal(d['display_name'], 'Python %i' % sys.version_info[0]) + nt.assert_equal(d['language'], 'python') + + +def test_get_kernel_dict_with_profile(): + d = get_kernel_dict(["--profile", "test"]) + assert_kernel_dict_with_profile(d) + + def assert_is_spec(path): for fname in os.listdir(RESOURCES): dst = pjoin(path, fname) @@ -107,3 +119,28 @@ def test_install(): assert_is_spec(os.path.join(system_jupyter_dir, 'kernels', KERNEL_NAME)) +def test_install_profile(): + system_jupyter_dir = tempfile.mkdtemp() + + with mock.patch('jupyter_client.kernelspec.SYSTEM_JUPYTER_PATH', + [system_jupyter_dir]): + install(profile="Test") + + spec = os.path.join(system_jupyter_dir, 'kernels', KERNEL_NAME, "kernel.json") + with open(spec) as f: + spec = json.load(f) + nt.assert_true(spec["display_name"].endswith(" [profile=Test]")) + nt.assert_equal(spec["argv"][-2:], ["--profile", "Test"]) + + +def test_install_display_name_overrides_profile(): + system_jupyter_dir = tempfile.mkdtemp() + + with mock.patch('jupyter_client.kernelspec.SYSTEM_JUPYTER_PATH', + [system_jupyter_dir]): + install(display_name="Display", profile="Test") + + spec = os.path.join(system_jupyter_dir, 'kernels', KERNEL_NAME, "kernel.json") + with open(spec) as f: + spec = json.load(f) + nt.assert_equal(spec["display_name"], "Display")