Skip to content

Commit

Permalink
Reticulate support (#4603)
Browse files Browse the repository at this point in the history
Support for creating Python sessions using the reticulate R
package. The Reticulate Python session behaves just like any other
Python session - ie, you can interact with the console, variables pane,
data explorer, etc. The difference is that, since the Reticulate session
is running in the same process as it's parent R session, you can easily
share objects between R and Python sessions.

---------

Signed-off-by: Daniel Falbel <[email protected]>
Co-authored-by: Wasim Lorgat <[email protected]>
  • Loading branch information
dfalbel and seeM authored Sep 27, 2024
1 parent 5c4382e commit b49ae25
Show file tree
Hide file tree
Showing 18 changed files with 10,522 additions and 7 deletions.
1 change: 1 addition & 0 deletions build/gulpfile.extensions.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ const compilations = [
'extensions/positron-proxy/tsconfig.json',
'extensions/positron-viewer/tsconfig.json',
'extensions/positron-zed/tsconfig.json',
'extensions/positron-reticulate/tsconfig.json',
'extensions/jupyter-adapter/tsconfig.json',
// --- End Positron ---
'extensions/configuration-editing/tsconfig.json',
Expand Down
1 change: 1 addition & 0 deletions build/npm/dirs.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const dirs = [
'extensions/positron-notebook-controllers',
'extensions/positron-notebooks',
'extensions/positron-r',
'extensions/positron-reticulate',
'extensions/positron-rstudio-keymap',
'extensions/positron-run-app',
'extensions/positron-python',
Expand Down
18 changes: 15 additions & 3 deletions extensions/jupyter-adapter/src/JupyterKernel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,6 @@ export class JupyterKernel extends EventEmitter implements vscode.Disposable {
// Save the exit code for error reporting if we know it
if (closedTerminal.exitStatus && closedTerminal.exitStatus.code) {
this._exitCode = closedTerminal.exitStatus.code;

// The kernel's status is now exited
this.setStatus(positron.RuntimeState.Exited);
}
Expand Down Expand Up @@ -311,7 +310,7 @@ export class JupyterKernel extends EventEmitter implements vscode.Disposable {
* @param session The Jupyter session information for the kernel running in
* the terminal
*/
private async connectToSession(session: JupyterSession) {
public async connectToSession(session: JupyterSession) {

// Establish a log channel for the kernel we're connecting to, if we
// don't already have one (we will if we're restarting)
Expand Down Expand Up @@ -680,6 +679,20 @@ export class JupyterKernel extends EventEmitter implements vscode.Disposable {
// and establishes available ports and sockets for the kernel to connect
// to.
const session = await createJupyterSession();

if (this._spec.startKernel) {
// If this is provided, it means we are starting the kernel with a different method
// instead of using a terminal. For instance, it could be a kernel started in a
// different format.
await this._spec.startKernel(session, this);
} else {
// This is the defualt path for starting a kernel as a vscode terminal
// using the given `argv` and `env` from the kernel spec.
await this.startTerminal(session);
}
}

async startTerminal(session: JupyterSession) {
const connnectionFile = session.state.connectionFile;
const logFile = session.state.logFile;
const profileFile = session.state.profileFile;
Expand Down Expand Up @@ -1032,7 +1045,6 @@ export class JupyterKernel extends EventEmitter implements vscode.Disposable {
* have disconnected, we consider the kernel to have exited.
*/
private onSocketDisconnected() {

// Check to see whether all the sockets are disconnected
for (const socket of this._allSockets) {
if (socket.isConnected()) {
Expand Down
32 changes: 32 additions & 0 deletions extensions/jupyter-adapter/src/jupyter-adapter.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,32 @@ import * as vscode from 'vscode';
// eslint-disable-next-line import/no-unresolved
import * as positron from 'positron';

export interface JupyterSessionState {
/** The Jupyter session identifier; sent as part of every message */
sessionId: string;

/** The log file the kernel is writing to */
logFile: string;

/** The profile file the kernel is writing to */
profileFile?: string;

/** The connection file specifying the ZeroMQ ports, signing keys, etc. */
connectionFile: string;

/** The ID of the kernel's process, or 0 if the process is not running */
processId: number;
}

export interface JupyterSession {
readonly state: JupyterSessionState;
}

export interface JupyterKernel {
connectToSession(session: JupyterSession): Promise<void>;
log(msg: string): void;
}

/**
* This set of type definitions defines the interfaces used by the Positron
* Jupyter Adapter extension.
Expand All @@ -34,6 +60,12 @@ export interface JupyterKernelSpec {

/** Environment variables to set when starting the kernel */
env?: NodeJS.ProcessEnv;

/** Function that starts the kernel given a JupyterSession object.
* This is used to start the kernel if it's provided. In this case `argv`
* is ignored.
*/
startKernel?: (session: JupyterSession, kernel: JupyterKernel) => Promise<void>;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import os
import sys

from positron_ipykernel.positron_ipkernel import PositronIPKernelApp
from positron_ipykernel.positron_ipkernel import PositronIPKernelApp, PositronIPyKernel
from positron_ipykernel.positron_jedilsp import POSITRON
from positron_ipykernel.session_mode import SessionMode

Expand Down Expand Up @@ -123,7 +123,6 @@ def parse_args() -> argparse.Namespace:
# command-line arguments in unexpected ways (e.g. logfile instructs it to log executed code).
app.initialize(argv=[])
assert app.kernel is not None, "Kernel was not initialized"

# Disable the banner if running in quiet mode.
if args.quiet:
app.kernel.shell.banner1 = ""
Expand All @@ -149,5 +148,11 @@ def parse_args() -> argparse.Namespace:
finally:
loop.close()

# When the app is gone, it should be safe to clear singleton instances.
# This allows re-starting the ipykernel in the same process, using different
# connection strings, etc.
PositronIPyKernel.clear_instance()
PositronIPKernelApp.clear_instance()

logger.info(f"Exiting process with status {exit_status}")
sys.exit(exit_status)
13 changes: 13 additions & 0 deletions extensions/positron-python/src/client/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
import { Uri, Event } from 'vscode';
import { BaseLanguageClient, LanguageClientOptions } from 'vscode-languageclient';
import { LanguageClient } from 'vscode-languageclient/node';
// --- Start Positron ---
import { PythonRuntimeSession } from './positron/session';
// --- End Positron ---
import { PYLANCE_NAME } from './activation/node/languageClientFactory';
import { ILanguageServerOutputChannel } from './activation/types';
import { PythonExtension } from './api/types';
Expand Down Expand Up @@ -155,5 +158,15 @@ export function buildApi(
(api as any).serviceContainer = serviceContainer;
(api as any).serviceManager = serviceManager;
}
// --- Start Positron ---
(api as any).positron = {
createPythonRuntimeSession:
// Types should actually be:
// (runtimeMetadata: LanguageRuntimeMetadata, sessionMetadata: RuntimeSessionMetadata, spec: JupyterKernelSpec)
// but we can't import them here.
(runtimeMetadata: any, sessionMetadata: any, spec: any) =>
new PythonRuntimeSession(runtimeMetadata, sessionMetadata, serviceContainer, spec),
};
// --- End Positron ---
return api;
}
2 changes: 1 addition & 1 deletion extensions/positron-r/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -635,7 +635,7 @@
},
"positron": {
"binaryDependencies": {
"ark": "0.1.139"
"ark": "0.1.140"
},
"minimumRVersion": "4.2.0",
"minimumRenvVersion": "1.0.7"
Expand Down
20 changes: 20 additions & 0 deletions extensions/positron-reticulate/extension.webpack.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*---------------------------------------------------------------------------------------------
* Copyright (C) 2024 Posit Software, PBC. All rights reserved.
* Licensed under the Elastic License 2.0. See LICENSE.txt for license information.
*--------------------------------------------------------------------------------------------*/

//@ts-check

'use strict';

const withDefaults = require('../shared.webpack.config');

module.exports = withDefaults({
context: __dirname,
entry: {
extension: './src/extension.ts',
},
node: {
__dirname: false
}
});
61 changes: 61 additions & 0 deletions extensions/positron-reticulate/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
{
"name": "positron-reticulate",
"displayName": "%displayName%",
"description": "%description%",
"version": "0.0.1",
"publisher": "vscode",
"engines": {
"vscode": "^1.65.0"
},
"activationEvents": [
"onStartupFinished"
],
"main": "./out/extension.js",
"scripts": {
"vscode:prepublish": "yarn run compile",
"pretest": "yarn run compile && yarn run lint",
"lint": "eslint src --ext ts"
},
"contributes": {
"languageRuntimes": [
{
"languageId": "reticulate"
}
],
"configuration": {
"type": "object",
"title": "%configurationTitle%",
"properties": {
"positron.reticulate.enabled": {
"type": "boolean",
"default": false,
"description": "%enabledDescription%"
}
}
}
},
"extensionDependencies": [
"ms-python.python",
"vscode.positron-r",
"vscode.jupyter-adapter"
],
"devDependencies": {
"@types/glob": "^7.2.0",
"@types/mocha": "^9.1.0",
"@types/node": "14.x",
"@typescript-eslint/eslint-plugin": "^5.12.1",
"@typescript-eslint/parser": "^5.12.1",
"@vscode/test-electron": "^2.1.2",
"inversify": "^6.0.1",
"eslint": "^8.9.0",
"glob": "^7.2.0",
"mocha": "^9.2.1",
"ts-node": "^10.9.1",
"typescript": "^4.5.5",
"vsce": "^2.11.0"
},
"repository": {
"type": "git",
"url": "https://github.com/posit-dev/positron"
}
}
6 changes: 6 additions & 0 deletions extensions/positron-reticulate/package.nls.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"displayName": "Positron Reticulate",
"description": "Provides reticulate support for positron",
"configurationTitle": "Reticulate settings",
"enabledDescription": "Enables reticulate console sessions"
}
7,316 changes: 7,316 additions & 0 deletions extensions/positron-reticulate/resources/branding/reticulate.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit b49ae25

Please sign in to comment.