You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Handle Conda environments without Python in EnvExt adapter (#26112)
## Summary
- skip Conda environment placeholders that do not contain a Python
interpreter
- isolate EnvExt conversion failures per item so one unsupported
environment cannot discard later valid environments in the same update
batch
- harden active-interpreter conversion and use consistent executable
identity when removing environments from the compatibility collection
- add focused coverage for discovery, active-environment, removal, and
refresh flows
Fixesmicrosoft/vscode-python-environments#1681
Related to microsoft/vscode-python-environments#1671
## Context
The Python Environments extension can discover and manage Conda
environments that do not currently have Python installed. It
intentionally exposes those entries to its Environment Manager UI as
placeholders:
```text
name: empty-env
version: no-python
run executable: conda
```
That is useful in the management UI, but the entry is not a selectable
Python interpreter.
Users reported that PET and Environment Manager showed every Conda
environment while both `Python: Select Interpreter` and Jupyter's
Python-environment kernel picker showed only System Python and the
current project environment. Both users confirmed that Python was
delegating discovery to the Python Environments extension, ruling out
the legacy discovery path.
One user then performed a reversible A/B test:
1. with a no-Python Conda environment under `miniconda3/envs`, Python
and Jupyter omitted the other valid Conda environments
2. moving that unchanged directory outside `envs` and reloading restored
both pickers
3. moving it back and reloading reproduced the failure
## Root cause
`src/client/envExt/envExtApi.ts` translates Python Environments API
objects into the legacy `PythonEnvInfo` format consumed by Python and
Jupyter.
The adapter previously parsed every version unconditionally:
```ts
parseVersion(pythonEnv.version)
```
`parseVersion('no-python')` throws `invalid version no-python`.
Environment-list changes arrive as a batch and were processed by an
unguarded `forEach`. When the no-Python placeholder appeared before
valid Conda environments, its conversion exception aborted the callback,
so every valid item later in that batch was lost:
```text
no-python placeholder -> throws
valid Conda A -> never processed
valid Conda B -> never processed
```
System environments arrive through a separate valid batch, and
individually resolved/current environments can use separate paths,
explaining the restricted list users observed.
Refresh did not self-heal because each Conda refresh sent another batch
containing the same placeholder and failed at the same conversion point.
## Fix
### Safe version conversion
A shared EnvExt conversion helper now:
- treats the expected `no-python` sentinel as a non-interpreter and
skips it at debug log level
- catches and logs unexpected malformed version strings instead of
throwing into callers
Both discovery-list conversion and active-interpreter compatibility
conversion use this boundary.
### Per-item fault isolation
Every environment-list change is processed independently. A malformed
runtime item is logged and skipped without preventing later valid items
from reaching Python/Jupyter.
Active-environment events are emitted only when every supplied side can
be converted, avoiding synthetic partial clear/set transitions while
preserving normal valid set, clear, and change behavior.
### Consistent removal identity
Adds are keyed by the Python executable path. Removals now derive the
same executable identity from the source environment. Previously Conda
removals used the environment prefix, so they generally could not remove
an entry stored by interpreter executable.
## Behavior and compatibility
| Scenario | Result |
| --- | --- |
| Conda environment without Python | remains visible/manageable in
Environment Manager; omitted from Python/Jupyter interpreter lists |
| Valid Conda, venv, system, Poetry, Pipenv, Pyenv environments | same
conversion and picker behavior as before |
| No-Python item before valid items in a batch | placeholder skipped;
all valid later items retained |
| Unexpected malformed provider item | logged and isolated; later items
continue |
| Valid active environment set/change/clear | unchanged |
| Invalid active environment | ignored instead of throwing |
| Conda refresh remove/add batch | old executable entries removed and
valid refreshed entries restored |
The valid hot path adds only a helper call and the existing version
parse. There are no new filesystem operations, environment discovery
calls, or background tasks.
## Tests
New adapter coverage includes:
- `[no-python, valid, valid]` batch handling
- malformed version in the middle of a batch
- structurally malformed event followed by a valid item
- no-Python and partially invalid active-environment events
- valid active set/change/clear events
- Conda removal by executable identity
- refresh batches containing removals, a no-Python placeholder, and
valid additions
- the actual `triggerRefresh()` path
- active-interpreter resolution for no-Python and malformed versions
Validation performed:
- `gulp compile`
- full repository ESLint
- focused EnvExt tests: **14 passing**
- 24 downstream EnvExt/interpreter-selector/interpreter/environment-API
test files: **345 passing, 1 expected pending**
- full unit suite: **5,178 passing, 34 pending**; the remaining 10
failures are pre-existing/unrelated Windows PATH-casing assertions and
native PET timeouts in untouched suites
The complete diff also went through two code-review passes and a
design-focused review; no production issues remained after addressing
malformed-event and active set/clear coverage.
## Follow-up
The EnvExt adapter currently relies on change events rather than
hydrating an authoritative initial snapshot. That is a separate
resilience concern; this PR intentionally targets the user-confirmed
`no-python` batch-abort failure.
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2f537bc5-b389-4ee2-aac2-e2b83be47f5c
0 commit comments