Skip to content

Commit a53ade9

Browse files
Path resolution by kernel manager and providers (#1005)
* Path resolution by kernel manager and providers * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Cleanup * Do not resolve for non-existent files * Fix spurious `async` * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update resolve_path return type to str | None --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent b32dcce commit a53ade9

File tree

3 files changed

+33
-0
lines changed

3 files changed

+33
-0
lines changed

jupyter_client/manager.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,11 @@ def client(self, **kwargs: t.Any) -> BlockingKernelClient:
280280
# Kernel management
281281
# --------------------------------------------------------------------------
282282

283+
def resolve_path(self, path: str) -> str | None:
284+
"""Resolve path to given file."""
285+
assert self.provisioner is not None
286+
return self.provisioner.resolve_path(path)
287+
283288
def update_env(self, *, env: t.Dict[str, str]) -> None:
284289
"""
285290
Allow to update the environment of a kernel manager.

jupyter_client/provisioning/local_provisioner.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
# Distributed under the terms of the Modified BSD License.
55
import asyncio
66
import os
7+
import pathlib
78
import signal
89
import sys
910
from typing import TYPE_CHECKING, Any
@@ -32,6 +33,7 @@ class LocalProvisioner(KernelProvisionerBase):
3233
pgid = None
3334
ip = None
3435
ports_cached = False
36+
cwd = None
3537

3638
@property
3739
def has_process(self) -> bool:
@@ -212,6 +214,7 @@ async def pre_launch(self, **kwargs: Any) -> dict[str, Any]:
212214

213215
async def launch_kernel(self, cmd: list[str], **kwargs: Any) -> KernelConnectionInfo:
214216
"""Launch a kernel with a command."""
217+
215218
scrubbed_kwargs = LocalProvisioner._scrub_kwargs(kwargs)
216219
self.process = launch_kernel(cmd, **scrubbed_kwargs)
217220
pgid = None
@@ -223,8 +226,18 @@ async def launch_kernel(self, cmd: list[str], **kwargs: Any) -> KernelConnection
223226

224227
self.pid = self.process.pid
225228
self.pgid = pgid
229+
self.cwd = kwargs.get("cwd", pathlib.Path.cwd())
226230
return self.connection_info
227231

232+
def resolve_path(self, path_str: str) -> str | None:
233+
"""Resolve path to given file."""
234+
path = pathlib.Path(path_str).expanduser()
235+
if not path.is_absolute() and self.cwd:
236+
path = (pathlib.Path(self.cwd) / path).resolve()
237+
if path.exists():
238+
return path.as_posix()
239+
return None
240+
228241
@staticmethod
229242
def _scrub_kwargs(kwargs: dict[str, Any]) -> dict[str, Any]:
230243
"""Remove any keyword arguments that Popen does not tolerate."""

jupyter_client/provisioning/provisioner_base.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,21 @@ def get_stable_start_time(self, recommended: float = 10.0) -> float:
218218
"""
219219
return recommended
220220

221+
def resolve_path(self, path: str) -> str | None:
222+
"""
223+
Returns the path resolved relative to kernel working directory.
224+
225+
For example, path `my_code.py` for a kernel started in `/tmp/`
226+
should result in `/tmp/my_code.py`, while path `~/test.py` for
227+
a kernel started in `/home/my_user/` should resolve to the
228+
(fully specified) `/home/my_user/test.py` path.
229+
230+
The provisioner may choose not to resolve any paths, or restrict
231+
the resolution to paths local to the kernel working directory
232+
to prevent path traversal and exposure of file system layout.
233+
"""
234+
return None
235+
221236
def _finalize_env(self, env: dict[str, str]) -> None:
222237
"""
223238
Ensures env is appropriate prior to launch.

0 commit comments

Comments
 (0)