From c0f19d6881f13e65bdc3f644c2511b721c71d8ad Mon Sep 17 00:00:00 2001 From: Christoffer Artmann Date: Thu, 9 Oct 2025 12:55:41 +0200 Subject: [PATCH 1/2] fix: Install kernel spec into venv instead of user directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, ipykernel specs were installed with --user flag, placing them in ~/Library/Jupyter/kernels/. This caused the Deepnote Jupyter server to fall back to generic Python kernels because it couldn't discover the venv-specific kernel spec. This resulted in a mismatch where: - Shell commands (!pip install) used the venv's Python (via PATH) - But the kernel interpreter used system Python - Leading to ModuleNotFoundError for packages installed in the venv Now installing kernel specs with --prefix into the venv itself at /share/jupyter/kernels/. Jupyter automatically discovers kernel specs in this location when started with the venv's Python, ensuring the correct interpreter is used. Each .deepnote file maintains its own isolated venv with its own kernel spec, preventing conflicts when multiple notebooks are open. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../deepnote/deepnoteToolkitInstaller.node.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/kernels/deepnote/deepnoteToolkitInstaller.node.ts b/src/kernels/deepnote/deepnoteToolkitInstaller.node.ts index 38ca4b3f00..870506e1fe 100644 --- a/src/kernels/deepnote/deepnoteToolkitInstaller.node.ts +++ b/src/kernels/deepnote/deepnoteToolkitInstaller.node.ts @@ -65,6 +65,8 @@ export class DeepnoteToolkitInstaller implements IDeepnoteToolkitInstaller { const venvPath = this.getVenvPath(deepnoteFileUri); const venvKey = venvPath.fsPath; + logger.info(`Virtual environment at ${venvKey}.`); + // Wait for any pending installation for this venv to complete const pendingInstall = this.pendingInstallations.get(venvKey); if (pendingInstall) { @@ -210,6 +212,7 @@ export class DeepnoteToolkitInstaller implements IDeepnoteToolkitInstaller { logger.info('deepnote-toolkit installed successfully in venv'); // Install kernel spec so the kernel uses this venv's Python + // Install into the venv itself (not --user) so the Deepnote server can discover it logger.info('Installing kernel spec for venv...'); try { // Reuse the process service with system environment @@ -219,7 +222,8 @@ export class DeepnoteToolkitInstaller implements IDeepnoteToolkitInstaller { '-m', 'ipykernel', 'install', - '--user', + '--prefix', + venvPath.fsPath, '--name', `deepnote-venv-${this.getVenvHash(deepnoteFileUri)}`, '--display-name', @@ -227,7 +231,11 @@ export class DeepnoteToolkitInstaller implements IDeepnoteToolkitInstaller { ], { throwOnStdErr: false } ); - logger.info('Kernel spec installed successfully'); + logger.info( + `Kernel spec installed successfully to ${ + venvPath.fsPath + }/share/jupyter/kernels/deepnote-venv-${this.getVenvHash(deepnoteFileUri)}` + ); } catch (ex) { logger.warn(`Failed to install kernel spec: ${ex}`); // Don't fail the entire installation if kernel spec creation fails From 42007bd993cbf13c088c7ddac14dfa62e19470a9 Mon Sep 17 00:00:00 2001 From: Christoffer Artmann Date: Thu, 9 Oct 2025 13:12:35 +0200 Subject: [PATCH 2/2] pr feedback --- CLAUDE.md | 1 + .../deepnote/deepnoteToolkitInstaller.node.ts | 13 ++++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 54c7e7c525..4338c0e3ac 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,6 +1,7 @@ ## Code Style & Organization - Order method, fields and properties, first by accessibility and then by alphabetical order. - Don't add the Microsoft copyright header to new files. +- Use `Uri.joinPath()` for constructing file paths to ensure platform-correct path separators (e.g., `Uri.joinPath(venvPath, 'share', 'jupyter', 'kernels')` instead of string concatenation with `/`) ## Testing - Unit tests use Mocha/Chai framework with `.unit.test.ts` extension diff --git a/src/kernels/deepnote/deepnoteToolkitInstaller.node.ts b/src/kernels/deepnote/deepnoteToolkitInstaller.node.ts index 870506e1fe..b9ee1113dd 100644 --- a/src/kernels/deepnote/deepnoteToolkitInstaller.node.ts +++ b/src/kernels/deepnote/deepnoteToolkitInstaller.node.ts @@ -65,7 +65,7 @@ export class DeepnoteToolkitInstaller implements IDeepnoteToolkitInstaller { const venvPath = this.getVenvPath(deepnoteFileUri); const venvKey = venvPath.fsPath; - logger.info(`Virtual environment at ${venvKey}.`); + logger.info(`Ensuring virtual environment at ${venvKey}`); // Wait for any pending installation for this venv to complete const pendingInstall = this.pendingInstallations.get(venvKey); @@ -231,11 +231,14 @@ export class DeepnoteToolkitInstaller implements IDeepnoteToolkitInstaller { ], { throwOnStdErr: false } ); - logger.info( - `Kernel spec installed successfully to ${ - venvPath.fsPath - }/share/jupyter/kernels/deepnote-venv-${this.getVenvHash(deepnoteFileUri)}` + const kernelSpecPath = Uri.joinPath( + venvPath, + 'share', + 'jupyter', + 'kernels', + `deepnote-venv-${this.getVenvHash(deepnoteFileUri)}` ); + logger.info(`Kernel spec installed successfully to ${kernelSpecPath.fsPath}`); } catch (ex) { logger.warn(`Failed to install kernel spec: ${ex}`); // Don't fail the entire installation if kernel spec creation fails