Skip to content

Public onDidChangePythonProjects API event never fires (runtime project add/remove not reported to consumers) #1599

@rchiodo

Description

@rchiodo

Summary

The public PythonEnvironmentApi.onDidChangePythonProjects event never fires. API consumers that subscribe to it to react to projects being added/removed at runtime never receive a notification. The internal project list is updated correctly (so a consumer that re-queries getPythonProjects() later — e.g. after a restart — does see the change), but no live event is emitted.

Where

src/features/pythonApi.tsPythonEnvironmentApiImpl:

The emitter is declared and exposed, but _onDidChangePythonProjects.fire(...) is never called anywhere in the class:

private readonly _onDidChangePythonProjects = new EventEmitter<DidChangePythonProjectsEventArgs>();
// ...
onDidChangePythonProjects: Event<DidChangePythonProjectsEventArgs> = this._onDidChangePythonProjects.event;

The constructor only forwards environment events — it pushes the emitter onto disposables (so it gets disposed) and subscribes to envManagers.onDidChangeActiveEnvironment_onDidChangeEnvironment and envVarManager.onDidChangeEnvironmentVariables_onDidChangeEnvironmentVariables. There is no subscription to projectManager.onDidChangeProjects.

Meanwhile the project manager does track and broadcast project changes internally (src/features/projectManager.ts, PythonProjectManagerImpl):

  • add(), remove(), updateProjects(), and loadProjects() all call this._onDidChangeProjects.fire(...).
  • It exposes public readonly onDidChangeProjects = this._onDidChangeProjects.event;.

And the API's addPythonProject() / removePythonProject() delegate to projectManager.add/remove, which updates the internal map and fires the internal onDidChangeProjects — but the public onDidChangePythonProjects is never fired.

Impact

Any API consumer relying on onDidChangePythonProjects to stay in sync with the active project set silently misses runtime changes (e.g. a new per-folder environment/project added via the UI). Workarounds require a full re-query or a restart of the consumer.

Concretely, this affects Pylance's per-folder interpreter support: when a user adds an environment/project for a subfolder, Pylance doesn't pick up the new (virtual) workspace folder until it is restarted, because Pylance subscribes to onDidChangePythonProjects and the event never fires.

Steps to reproduce

  1. Have an extension acquire the Python Environments API and subscribe to onDidChangePythonProjects.
  2. Add a project/environment for a folder at runtime (UI or addPythonProject).
  3. Observe: getPythonProjects() now includes the new project, but the onDidChangePythonProjects callback never fired.

Expected

onDidChangePythonProjects should fire whenever the set of python projects changes (added/removed), consistent with its documented contract ("Event raised when python projects are added or removed").

Suggested fix

Forward the project manager's change event to the public API emitter in the PythonEnvironmentApiImpl constructor, e.g.:

this.projectManager.onDidChangeProjects((/* projects */) => {
    this._onDidChangePythonProjects.fire(/* DidChangePythonProjectsEventArgs: added / removed */);
});

The project manager currently fires the full project array, so producing the added/removed shape of DidChangePythonProjectsEventArgs will require either tracking the previous set in the API layer, or updating the manager to emit the delta.

Environment

  • Repo: microsoft/vscode-python-environments (observed on main)
  • Relevant files: src/features/pythonApi.ts, src/features/projectManager.ts

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions