-
Notifications
You must be signed in to change notification settings - Fork 41
Reducing Boilerplate for Third-Party Environment Managers #1438
Description
Summary
We want to make it easy for the community to build great Python tool extensions on top of vscode-python-environments. Whether you're integrating Hatch, Pixi, or your own tool, we want the process to be straightforward. You should be able to focus on what makes your tool unique rather than reimplementing generic plumbing.
Right now, third-party extension authors building environment managers must reimplement hundreds of lines of generic logic that isn't specific to their tool. This discussion proposes ways to reduce that burden so authors can focus on discovery and environment resolution while reusing battle-tested defaults for everything else.
The Problem
The EnvironmentManager interface is what your extension implements to teach VS Code about your tool's Python environments. It requires implementing methods covering environment tracking, shell activation, persistent state, event wiring, and environment resolution. In practice, most of these implementations are nearly identical across venv-based tools.
Looking at real-world examples like pixi-code's envManager.ts, only a handful of functions are actually Pixi-specific:
| What's tool-specific | What's generic boilerplate |
|---|---|
refreshPixi() — discover environments |
get() / set() — active env tracking with persistent state |
resolvePixiProjectPaths() — find project paths |
onDidChangeEnvironment / onDidChangeEnvironments event wiring |
| Constructor args (name, icon, tooltip) | getEnvironments() — scope-based filtering (all, global, Uri) |
Activation via pixi shell / pixi run (tool-specific) |
clearCache() — persistent state cleanup |
| Environment diffing (old vs. new) for change events | |
Persistent state helpers (getWorkspacePersistentState reimplemented locally) |
The same pattern was observed by @flying-sheep when attempting to build hatch-code (see #378):
"I'd like to only override a few things, I don't want to reimplement everything."
"almost no line in the file I linked is pixi specific except for the constructor args, and two functions:
refreshPixiandresolvePixiProjectPaths"
This high implementation cost discourages third-party extension development and creates a maintenance burden, every extension author ends up having to independently update their copy.
Proposed Solution
We envision a two-phase approach, starting with low-risk utility exports and building toward a higher-level abstraction.
Phase 1: Export Internal Utility Functions (Near-Term, Lower Risk)
We already have well-tested internal utility functions that every third-party manager ends up reimplementing. Exporting a curated set of these through the public API would immediately reduce boilerplate — but we want to hear which parts of the implementation cost you the most time before deciding what to expose.
Phase 2: Higher-Level Abstraction (Larger Effort, Needs Design)
Beyond individual utilities, we're exploring whether we can provide a higher-level abstraction that handles most of the EnvironmentManager boilerplate for you. The idea is that you'd only need to provide your tool-specific logic (e.g., "here's how to discover my environments") and get everything else — get/set tracking, event wiring, scope filtering, persistent state, cache management — for free.
There are several possible shapes this could take:
- A base class you extend with just a
discoverEnvironments()override - A factory function that returns a complete
EnvironmentManager - A declarative registration where you provide a discovery function and the extension handles everything else
We'd like community input on which approach would work best for your use case.
We're happy to help extension authors work through integration challenges along the way. If you're building something and get stuck, feel free to open an issue, comment below or upvote this discussion item.