Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 29 additions & 17 deletions ipykernel/kernelspec.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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:
Expand All @@ -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
Expand All @@ -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
-------

Expand All @@ -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]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If profile is given, this should probably be reflected in display_name if it has not been specified manually. Something like Python X [profile=Y], perhaps.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea, maybe it would be enough with e.g. "Python X [Y]" to make it more compact?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think the display_name horizontal space is so precious that profile= should be omitted, but feel free to try both and see how it feels. I don't think [name] is sufficiently clear, since it could be indicating other things, such as conda/virtual env, etc.

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
Expand Down Expand Up @@ -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.")
Expand All @@ -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)
Expand Down
37 changes: 37 additions & 0 deletions ipykernel/tests/test_kernelspec.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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")