Skip to content

Defer positron-reticulate activation until needed#13086

Open
juliasilge wants to merge 2 commits intomainfrom
feat/reticulate-lazier-activation
Open

Defer positron-reticulate activation until needed#13086
juliasilge wants to merge 2 commits intomainfrom
feat/reticulate-lazier-activation

Conversation

@juliasilge
Copy link
Copy Markdown
Contributor

@juliasilge juliasilge commented Apr 18, 2026

Addresses #12478

After #12645, positron-reticulate activation no longer blocks time-to-first-console, but the extension was still being activated during the runtime-discovery phase on every startup, regardless of whether the user ever used reticulate. This PR defers that activation to onStartupFinished and drops the extensionDependencies chain that was forcing ms-python.python and positron.positron-r to be pulled in by reticulate's activation path.

The extension still activates on onStartupFinished so the client handler for positron.reticulate comms, the config-change listener, and the runtime manager registration are all in place before the user can realistically trigger reticulate::repl_python(). For persisted machine-level sessions, restoreWorkspaceSessions activates reticulate by extension id, which doesn't require a languageRuntimes contribution.

Runtime startup marks (before vs. after)

From just a single comparison on a local dev build opening the same workspace with R and Python runtimes available:

Mark main this PR
runtimeStartup/extensionPreActivate/positron.positron-reticulate present absent
runtimeStartup/extensionPostActivate/positron.positron-reticulate present absent
reticulate activation event (from Show Running Extensions) onLanguageRuntime:reticulate onStartupFinished
reticulate Load + Call + Finish (from Show Running Extensions) ~75ms (shared with two other extensions) ~3ms

The removal of reticulate from the runtime-discovery activation set is the structural change this PR guarantees. The firstRuntimeReady and runtimeStartupPhase/complete marks also landed earlier on the branch FWIW.

Release Notes

New Features

  • N/A

Bug Fixes

QA Notes

@:reticulate

  1. Open Command Palette -> Developer: Show Running Extensions on a fresh Positron window with an R interpreter available. Confirm positron.positron-reticulate shows onStartupFinished (not onLanguageRuntime:reticulate), with a small activation time, and By: positron.positron-reticulate.
  2. In the R console, run reticulate::repl_python() and confirm a Python (reticulate) session starts as before.
  3. Restart the reticulate session via its console control; confirm it restarts cleanly.
  4. Set kernelSupervisor.shutdownTimeout to something other than immediately, start a reticulate session, reload Positron, and confirm the session is restored (exercises the restore path that now relies on activateById).
  5. Set positron.reticulate.enabled = "always" in a workspace with R available, reload, and confirm the reticulate runtime registers.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 18, 2026

E2E Tests 🚀
This PR will run tests tagged with: @:critical @:reticulate

readme  valid tags

"lint": "eslint src --ext ts"
},
"contributes": {
"languageRuntimes": [
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was the reason the extension was eagerly activated during discoverAllRuntimes, but the languageId: "reticulate" anchor was a no-op (the actual runtime metadata registers under languageId: 'python').

}
}
},
"extensionDependencies": [
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These forced the Python and R extensions to fully activate before reticulate's own activate() could even start, but that's not necessary.

}

createPythonRuntimeSession(runtimeMetadata: positron.LanguageRuntimeMetadata, sessionMetadata: positron.RuntimeSessionMetadata, kernelSpec?: JupyterKernelSpec): positron.LanguageRuntimeSession {
async createPythonRuntimeSession(runtimeMetadata: positron.LanguageRuntimeMetadata, sessionMetadata: positron.RuntimeSessionMetadata, kernelSpec?: JupyterKernelSpec): Promise<positron.LanguageRuntimeSession> {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're making this async and explicitly await-ing api.activate() when the Python extension isn't yet active (needed now because, without extensionDependencies, api.exports is only populated post-activation).

this.onDidUpdateResourceUsage = this._resourceUsageEmitter.event;
}

private async initializePythonSession(runtimeMetadata: positron.LanguageRuntimeMetadata): Promise<void> {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is new, to split out the async work out of the ReticulateRuntimeSession constructor. The create and restore factories now await session.initializePythonSession(metadata) after construction. The restart() path's createPythonRuntimeSession call is also now awaited.

@juliasilge juliasilge marked this pull request as ready for review April 19, 2026 20:30
@juliasilge juliasilge requested a review from jmcphers April 19, 2026 20:31
@jmcphers
Copy link
Copy Markdown
Collaborator

With these changes, I do see the reticulate startup time drop to 3ms.

But reviewing the startup diagnostics, with a simple workspace containing a single session with an affiliated version of R, I still see positron-reticulate getting invoked during discovery:

Workspace-Affiliated Runtimes

Name Language Version Source Has Session Session ID(s)
R 4.5.0 r 4.5.0 System Yes r-99fa1650

Active Sessions

Runtime Name Mode State Start Reason Created
R 4.5.0 R 4.5.0 console idle Affiliated R runtime for workspace 3:40:39 PM

Time to First Runtime Ready

  • First runtime ready: 3573ms after runtime startup began

Startup Phase Progression

  • Current Phase: Complete
Phase Timestamp (ms) Duration (ms)
initializing 1776897638337 -
awaitingTrust 1776897638337 0
starting 1776897638651 314
discovering 1776897641920 3269
complete 1776897646378 4458

Positron Runtime Performance Marks

Name Timestamp Delta Total
code/positron/runtimeStartupBegin 1776897638337 0 0
code/positron/runtimeStartupPhase/initializing 1776897638337 0 0
code/positron/runtimeStartupPhase/awaitingTrust 1776897638337 0 0
code/positron/runtimeStartupPhase/starting 1776897638651 314 314
code/positron/runtimeStartup/extensionPreActivate/positron.positron-r 1776897638682 31 345
code/positron/positron.positron-supervisor/initializing 1776897639166 484 829
code/positron/positron.positron-supervisor/terminalOpened 1776897639170 4 833
code/positron/runtimeStartup/extensionPostActivate/positron.positron-r 1776897639281 111 944
code/positron/positron.positron-supervisor/started 1776897639283 2 946
code/positron/runtimeSessionWillStart/r-99fa1650 1776897639381 98 1044
code/positron/positron.positron-supervisor/ready 1776897641396 2015 3059
code/positron/runtimeSessionStart/r-99fa1650 1776897641910 514 3573
code/positron/firstRuntimeReady 1776897641910 0 3573
code/positron/runtimeStartup/extensionPreActivate/positron.positron-javascript 1776897641911 1 3574
code/positron/runtimeStartup/extensionPreActivate/ms-python.python 1776897641911 0 3574
code/positron/runtimeStartup/extensionPreActivate/positron.positron-reticulate 1776897641911 0 3574
code/positron/runtimeStartup/extensionPreActivate/positron.positron-zed 1776897641911 0 3574
code/positron/runtimeStartup/extensionPostActivate/ms-python.python 1776897641916 5 3579
code/positron/runtimeStartup/extensionPostActivate/positron.positron-reticulate 1776897641917 1 3580
code/positron/runtimeStartup/extensionPostActivate/positron.positron-zed 1776897641917 0 3580
code/positron/runtimeStartup/extensionPostActivate/positron.positron-javascript 1776897641920 3 3583
code/positron/runtimeStartupPhase/discovering 1776897641920 0 3583
code/positron/runtimeStartupPhase/complete 1776897646378 4458 8041

@juliasilge
Copy link
Copy Markdown
Contributor Author

Gosh, that is very surprising, @jmcphers! 😱

I definitely do not see that locally for myself. Here's a fresh startup diagnostic I just captured from the current state of this branch (after the merge from main I just did), with the same kind of simple workspace with an affiliated R session plus a Python session that starts when a .py file is opened:

code/positron/runtimeStartup/extensionPreActivate/ms-python.python              | delta 4  | total 7318
code/positron/runtimeStartup/extensionPreActivate/positron.positron-javascript  | delta 0  | total 7318
code/positron/runtimeStartup/extensionPreActivate/positron.positron-zed         | delta 0  | total 7318
code/positron/runtimeStartup/extensionPostActivate/ms-python.python             | delta 40 | total 7358
code/positron/runtimeStartup/extensionPostActivate/positron.positron-zed        | delta 3  | total 7361
code/positron/runtimeStartup/extensionPostActivate/positron.positron-javascript | delta 97 | total 7458
code/positron/runtimeStartupPhase/discovering                                   | delta 0  | total 7458

positron.positron-reticulate is no longer present in the pre/post activate list at all. It's fully out of the discovery activation path, not just fast.

The extensionPreActivate / extensionPostActivate marks are emitted in activateExtension, which is only called from activateExtensionsForLanguages, iterating over _languagePacks. And _languagePacks is populated exclusively from the languageRuntimes extension-point contribution in languageRuntimeExtPoint.setHandler.

This PR removes that contribution from reticulate's manifest (see extensions/positron-reticulate/package.json where there is no more languageRuntimes block), so reticulate shouldn't end up in _languagePacks and the marks shouldn't fire.

My best guess is a stale extension-description cache on your side is still listing the old languageRuntimes: [{languageId: "reticulate"}] entry. That would put reticulate back in _languagePacks and produce exactly the pattern you saw (mark fires, but activation itself is ~1ms because onStartupFinished already ran).

Would you mind pulling the merge, doing a full rebuild, and re-running diagnostics to see if reticulate is still showing up for you? If it is, there's another path into _languagePacks I haven't spotted and obviously we'd want to track it down before landing this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants