Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to leave kernel sessions running when Positron is closed #5899

Merged
merged 16 commits into from
Jan 10, 2025
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
5 changes: 5 additions & 0 deletions extensions/positron-python/src/client/jupyter-adapter.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,11 @@ export interface JupyterAdapterApi extends vscode.Disposable {
extra?: JupyterKernelExtra | undefined,
): Promise<JupyterLanguageRuntimeSession>;

/**
* Validate an existing session for a Jupyter-compatible kernel.
*/
validateSession(sessionId: string): Promise<boolean>;

/**
* Restore a session for a Jupyter-compatible kernel.
*
Expand Down
27 changes: 26 additions & 1 deletion extensions/positron-python/src/client/positron/manager.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
/*---------------------------------------------------------------------------------------------
* Copyright (C) 2023-2024 Posit Software, PBC. All rights reserved.
* Copyright (C) 2023-2025 Posit Software, PBC. All rights reserved.
* Licensed under the Elastic License 2.0. See LICENSE.txt for license information.
*--------------------------------------------------------------------------------------------*/
/* eslint-disable global-require */
/* eslint-disable class-methods-use-this */
import * as portfinder from 'portfinder';
// eslint-disable-next-line import/no-unresolved
import * as positron from 'positron';
import * as vscode from 'vscode';
import * as path from 'path';
import * as os from 'os';

Expand Down Expand Up @@ -254,6 +255,30 @@ export class PythonRuntimeManager implements IPythonRuntimeManager {
return registeredMetadata ?? metadata;
}

/**
* Validate an existing session for a Jupyter-compatible kernel.
*
* @param sessionId The session ID to validate
* @returns True if the session is valid, false otherwise
*/
async validateSession(sessionId: string): Promise<boolean> {
const config = vscode.workspace.getConfiguration('kernelSupervisor');
if (config.get<boolean>('enable', true)) {
const ext = vscode.extensions.getExtension('positron.positron-supervisor');
if (!ext) {
throw new Error('Positron Supervisor extension not found');
}
if (!ext.isActive) {
await ext.activate();
}
return ext.exports.validateSession(sessionId);
}

// When not using the kernel supervisor, sessions are not
// persisted.
return false;
}

/**
* Wrapper for Python runtime discovery method that caches the metadata
* before it's returned to Positron.
Expand Down
14 changes: 12 additions & 2 deletions extensions/positron-python/src/client/positron/runtime.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
/*---------------------------------------------------------------------------------------------
* Copyright (C) 2024 Posit Software, PBC. All rights reserved.
* Copyright (C) 2024-2025 Posit Software, PBC. All rights reserved.
* Licensed under the Elastic License 2.0. See LICENSE.txt for license information.
*--------------------------------------------------------------------------------------------*/
/* eslint-disable global-require */
// eslint-disable-next-line import/no-unresolved
import * as positron from 'positron';
import * as vscode from 'vscode';
import * as fs from 'fs-extra';
import * as os from 'os';
import * as path from 'path';
Expand Down Expand Up @@ -104,6 +105,15 @@ export async function createPythonRuntimeMetadata(
pythonEnvironmentId: interpreter.id || '',
};

// Check the kernel supervisor's configuration; if it's enabled and
// configured to persist sessions, mark the session location as 'machine'
// so that Positron will reattach to the session after Positron is reopened.
const config = vscode.workspace.getConfiguration('kernelSupervisor');
const sessionLocation =
config.get<boolean>('enable', true) && config.get<string>('shutdownTimeout', 'immediately') !== 'immediately'
? positron.LanguageRuntimeSessionLocation.Machine
: positron.LanguageRuntimeSessionLocation.Workspace;

// Create the metadata for the language runtime
const metadata: positron.LanguageRuntimeMetadata = {
runtimeId,
Expand All @@ -119,7 +129,7 @@ export async function createPythonRuntimeMetadata(
.readFileSync(path.join(EXTENSION_ROOT_DIR, 'resources', 'branding', 'python-icon.svg'))
.toString('base64'),
startupBehavior,
sessionLocation: positron.LanguageRuntimeSessionLocation.Workspace,
sessionLocation,
extraRuntimeData,
};

Expand Down
8 changes: 6 additions & 2 deletions extensions/positron-r/src/jupyter-adapter.d.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
/*---------------------------------------------------------------------------------------------
* Copyright (C) 2023-2024 Posit Software, PBC. All rights reserved.
* Copyright (C) 2023-2025 Posit Software, PBC. All rights reserved.
* Licensed under the Elastic License 2.0. See LICENSE.txt for license information.
*--------------------------------------------------------------------------------------------*/

import * as vscode from 'vscode';

// eslint-disable-next-line import/no-unresolved
import * as positron from 'positron';

export interface JupyterSessionState {
Expand Down Expand Up @@ -149,6 +148,11 @@ export interface JupyterAdapterApi extends vscode.Disposable {
extra?: JupyterKernelExtra | undefined,
): Promise<JupyterLanguageRuntimeSession>;

/**
* Validate an existing session for a Jupyter-compatible kernel.
*/
validateSession(sessionId: string): Promise<boolean>;

/**
* Restore a session for a Jupyter-compatible kernel.
*
Expand Down
10 changes: 9 additions & 1 deletion extensions/positron-r/src/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,14 @@ export async function makeMetadata(
reasonDiscovered: rInst.reasonDiscovered,
};

// Check the kernel supervisor's configuration; if it's enabled and
// configured to persist sessions, mark the session location as 'machine'
// so that Positron will reattach to the session after Positron is reopened.
const config = vscode.workspace.getConfiguration('kernelSupervisor');
const sessionLocation = config.get<boolean>('enable', true) &&
config.get<string>('shutdownTimeout', 'immediately') !== 'immediately' ?
positron.LanguageRuntimeSessionLocation.Machine : positron.LanguageRuntimeSessionLocation.Workspace;

const metadata: positron.LanguageRuntimeMetadata = {
runtimeId,
runtimeName,
Expand All @@ -230,7 +238,7 @@ export async function makeMetadata(
fs.readFileSync(
path.join(EXTENSION_ROOT_DIR, 'resources', 'branding', 'r-icon.svg')
).toString('base64'),
sessionLocation: positron.LanguageRuntimeSessionLocation.Workspace,
sessionLocation,
startupBehavior,
extraRuntimeData
};
Expand Down
26 changes: 25 additions & 1 deletion extensions/positron-r/src/runtime-manager.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*---------------------------------------------------------------------------------------------
* Copyright (C) 2024 Posit Software, PBC. All rights reserved.
* Copyright (C) 2024-2025 Posit Software, PBC. All rights reserved.
* Licensed under the Elastic License 2.0. See LICENSE.txt for license information.
*--------------------------------------------------------------------------------------------*/

Expand Down Expand Up @@ -88,6 +88,30 @@ export class RRuntimeManager implements positron.LanguageRuntimeManager {
return Promise.resolve(makeMetadata(inst, positron.LanguageRuntimeStartupBehavior.Immediate));
}

/**
* Validate an existing session for a Jupyter-compatible kernel.
*
* @param sessionId The session ID to validate
* @returns True if the session is valid, false otherwise
*/
async validateSession(sessionId: string): Promise<boolean> {
const config = vscode.workspace.getConfiguration('kernelSupervisor');
if (config.get<boolean>('enable', true)) {
const ext = vscode.extensions.getExtension('positron.positron-supervisor');
if (!ext) {
throw new Error('Positron Supervisor extension not found');
}
if (!ext.isActive) {
await ext.activate();
}
return ext.exports.validateSession(sessionId);
}

// When not using the kernel supervisor, sessions are not
// persisted.
return false;
}

restoreSession(
runtimeMetadata: positron.LanguageRuntimeMetadata,
sessionMetadata: positron.RuntimeSessionMetadata): Thenable<positron.LanguageRuntimeSession> {
Expand Down
8 changes: 8 additions & 0 deletions extensions/positron-reticulate/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -693,6 +693,14 @@ class ReticulateRuntimeMetadata implements positron.LanguageRuntimeMetadata {
path.join(CONTEXT.extensionPath, 'resources', 'branding', 'reticulate.svg'),
{ encoding: 'base64' }
);
// Check the kernel supervisor's configuration; if it's enabled and
// configured to persist sessions, mark the session location as 'machine'
// so that Positron will reattach to the session after Positron is reopened.
const config = vscode.workspace.getConfiguration('kernelSupervisor');
this.sessionLocation = config.get<boolean>('enable', true) &&
config.get<string>('shutdownTimeout', 'immediately') !== 'immediately' ?
positron.LanguageRuntimeSessionLocation.Machine : positron.LanguageRuntimeSessionLocation.Workspace;

}
runtimePath: string = 'Managed by the reticulate package';
runtimeName: string = 'Python (reticulate)';
Expand Down
28 changes: 27 additions & 1 deletion extensions/positron-supervisor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,32 @@
"default": "debug",
"description": "%configuration.logLevel.description%"
},
"kernelSupervisor.shutdownTimeout": {
"scope": "window",
"type": "string",
"enum": [
"immediately",
"when idle",
"4",
"8",
"12",
"24",
"168",
"indefinitely"
],
"enumDescriptions": [
"%configuration.shutdownTimeout.immediately.description%",
"%configuration.shutdownTimeout.whenIdle.description%",
"%configuration.shutdownTimeout.4.description%",
"%configuration.shutdownTimeout.8.description%",
"%configuration.shutdownTimeout.12.description%",
"%configuration.shutdownTimeout.24.description%",
"%configuration.shutdownTimeout.168.description%",
"%configuration.shutdownTimeout.indefinitely.description%"
],
"default": "immediately",
"markdownDescription": "%configuration.shutdownTimeout.description%"
},
"kernelSupervisor.attachOnStartup": {
"scope": "window",
"type": "boolean",
Expand Down Expand Up @@ -115,7 +141,7 @@
},
"positron": {
"binaryDependencies": {
"kallichore": "0.1.26"
"kallichore": "0.1.27"
}
},
"dependencies": {
Expand Down
9 changes: 9 additions & 0 deletions extensions/positron-supervisor/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@
"configuration.logLevel.debug.description": "Debug messages",
"configuration.logLevel.trace.description": "Verbose tracing messages",
"configuration.logLevel.description": "Log level for the kernel supervisor (restart Positron to apply)",
"configuration.shutdownTimeout.description": "When should kernels be shut down after Positron is closed?\n\nTimeouts are in hours and start once the kernel is idle. Restart Positron to apply.",
"configuration.shutdownTimeout.immediately.description": "Shut down kernels immediately when Positron is closed",
"configuration.shutdownTimeout.whenIdle.description": "Wait for kernels to finish any running computations, then shut them down",
"configuration.shutdownTimeout.4.description": "After idle for 4 hours",
"configuration.shutdownTimeout.8.description": "After idle for 8 hours",
"configuration.shutdownTimeout.12.description": "After idle for 12 hours",
"configuration.shutdownTimeout.24.description": "After idle for 1 day",
"configuration.shutdownTimeout.168.description": "After idle for 7 days",
"configuration.shutdownTimeout.indefinitely.description": "Leave kernels running indefinitely",
"configuration.enable.description": "Run Jupyter kernels under the Positron kernel supervisor.",
"configuration.showTerminal.description": "Show the host terminal for the Positron kernel supervisor",
"configuration.connectionTimeout.description": "Timeout in seconds for connecting to the kernel's sockets",
Expand Down
Loading
Loading