Skip to content

Commit

Permalink
Lay foundation to pass notebook names to kernel at startup.
Browse files Browse the repository at this point in the history
This has been a controversial topic from some time:

jupyter/notebook#1000
https://forums.databricks.com/questions/21390/is-there-any-way-to-get-the-current-notebook-name.html
https://stackoverflow.com/questions/12544056/how-do-i-get-the-current-ipython-jupyter-notebook-name
https://ask.sagemath.org/question/36873/access-notebook-filename-from-jupyter-with-sagemath-kernel/

This is also sometime critical to linter, and tab completion to know
current name.

Of course current answer is that the question is ill-defined,
there might not be a file associated with the current kernel, there
might be multiple files, files might not be on the same system, it could
change through the execution and many other gotchas.

This suggest to add an JPY_KERNEL_SESSION_NAME env variable which is not
too visible, but give an escape hatch which should mostly be correct
unless the notebook is renamed or kernel attached to a new one.

Do do so this handles the new associated_file parameters in a few
function of the kernel manager. On jupyter_server this one line change
make the notebook name available using typical local installs:

```diff
diff --git a/notebook/services/sessions/sessionmanager.py b/notebook/services/sessions/sessionmanager.py
index 92b2a7345..f7b4011ce 100644
--- a/notebook/services/sessions/sessionmanager.py
+++ b/notebook/services/sessions/sessionmanager.py
@@ -108,7 +108,9 @@ class SessionManager(LoggingConfigurable):
         # allow contents manager to specify kernels cwd
         kernel_path = self.contents_manager.get_kernel_path(path=path)
         kernel_id = yield maybe_future(
-            self.kernel_manager.start_kernel(path=kernel_path, kernel_name=kernel_name)
+            self.kernel_manager.start_kernel(
+                path=kernel_path, kernel_name=kernel_name, session_name=path
+            )
         )
         # py2-compat
         raise gen.Return(kernel_id)
```diff

Of course only launchers that will pass forward this value will allow
the env variable to be set.

I'm thinking that various kernels may use this and expose it in
different ways. like __notebook_name__ if it ends with `.ipynb` in
ipykernel.
  • Loading branch information
Carreau committed Sep 14, 2021
1 parent 7b5a95c commit d0ee6c9
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 2 deletions.
12 changes: 12 additions & 0 deletions jupyter_client/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,17 @@
from jupyter_client import kernelspec


# Name of the env variable that contains the name of the current session associated
# with the kernel we are launching.
# Frontends can decide to set this session name to the name of the file when
# when the kernel is started.
# This is useful in notebook context to find which notebook we are working with
# though we might not be working with a notebook, we could be working with a
# markdown file, or python file.
# as with other Jupyter Related Env variable with use the JPY prefix.
JPY_KERNEL_SESSION_NAME = 'JPY_SESSION_NAME'


class _ShutdownStatus(Enum):
"""
Expand Down Expand Up @@ -332,6 +343,7 @@ async def _async_start_kernel(self, **kw):

# launch the kernel subprocess
self.log.debug("Starting kernel: %s", kernel_cmd)
kw.pop('session_name', None)
await ensure_async(self._launch_kernel(kernel_cmd, **kw))
await ensure_async(self.post_start_kernel(**kw))

Expand Down
3 changes: 3 additions & 0 deletions jupyter_client/multikernelmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,9 @@ def pre_start_kernel(
constructor_kwargs = {}
if self.kernel_spec_manager:
constructor_kwargs["kernel_spec_manager"] = self.kernel_spec_manager

if 'session_name' in kwargs:
constructor_kwargs['session_name'] = kwargs.copy().pop('session_name')
km = self.kernel_manager_factory(
connection_file=os.path.join(self.connection_dir, "kernel-%s.json" % kernel_id),
parent=self,
Expand Down
3 changes: 1 addition & 2 deletions jupyter_client/provisioning/local_provisioner.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ async def pre_launch(self, **kwargs: Any) -> Dict[str, Any]:

# This should be considered temporary until a better division of labor can be defined.
km = self.parent
extra_arguments = kwargs.pop('extra_arguments', [])
if km:
if km.transport == 'tcp' and not is_local_ip(km.ip):
raise RuntimeError(
Expand All @@ -149,7 +150,6 @@ async def pre_launch(self, **kwargs: Any) -> Dict[str, Any]:
"Currently valid addresses are: %s" % (km.ip, local_ips())
)
# build the Popen cmd
extra_arguments = kwargs.pop('extra_arguments', [])

# write connection file / get default ports
# TODO - change when handshake pattern is adopted
Expand All @@ -169,7 +169,6 @@ async def pre_launch(self, **kwargs: Any) -> Dict[str, Any]:
extra_arguments=extra_arguments
) # This needs to remain here for b/c
else:
extra_arguments = kwargs.pop('extra_arguments', [])
kernel_cmd = self.kernel_spec.argv + extra_arguments

return await super().pre_launch(cmd=kernel_cmd, **kwargs)
Expand Down
12 changes: 12 additions & 0 deletions jupyter_client/provisioning/provisioner_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,16 @@

from ..connect import KernelConnectionInfo

# Name of the env variable that contains the name of the current session associated
# with the kernel we are launching.
# Frontends can decide to set this session name to the name of the file when
# when the kernel is started.
# This is useful in notebook context to find which notebook we are working with
# though we might not be working with a notebook, we could be working with a
# markdown file, or python file.
# as with other Jupyter Related Env variable with use the JPY prefix.
JPY_SESSION_NAME = 'JPY_SESSION_NAME'


class KernelProvisionerMeta(ABCMeta, type(LoggingConfigurable)): # type: ignore
pass
Expand Down Expand Up @@ -160,6 +170,8 @@ async def pre_launch(self, **kwargs: Any) -> Dict[str, Any]:
:meth:`launch_kernel()`.
"""
env = kwargs.pop('env', os.environ).copy()
if 'session_name' in kwargs:
env.update({JPY_SESSION_NAME: kwargs['session_name']})
env.update(self.__apply_env_substitutions(env))
self._finalize_env(env)
kwargs['env'] = env
Expand Down

0 comments on commit d0ee6c9

Please sign in to comment.