|
| 1 | +## Overview (high-level) |
| 2 | + |
| 3 | +The pyenv manager provides discovery, enumeration, selection, and metadata for Python environments installed and managed by the `pyenv` tool. It plugs into the extension's environment manager abstraction so other parts of the extension (pickers, environment API consumers, telemetry) see a consistent view of pyenv-managed interpreters. |
| 4 | + |
| 5 | +Primary files: |
| 6 | + |
| 7 | +- `src/managers/pyenv/main.ts` — manager registration and lifecycle hook (registration with the manager registry). |
| 8 | +- `src/managers/pyenv/pyenvManager.ts` — the manager implementation (class `PyEnvManager`). Key methods to inspect: |
| 9 | + - `initialize()` — discovers environments at startup. |
| 10 | + - `getEnvironments(scope)` — returns cached environments; supports `all`, `global`, and per-URI lookup. |
| 11 | + - `refresh(context)` — re-runs discovery and emits environment change events. |
| 12 | + - `get(scope)` / `set(scope, environment)` — workspace/global selection helpers. |
| 13 | + - `resolve(context)` — attempt to resolve a pyenv path to a `PythonEnvironment` and add it to the collection. |
| 14 | + - `clearCache()` — clears persisted pyenv selections. |
| 15 | + - `loadEnvMap()` — internal mapping between project folders and resolved environments. |
| 16 | +- `src/managers/pyenv/pyenvUtils.ts` — low-level helpers and command wrappers. Important exported functions and constants you should be aware of: |
| 17 | + - `refreshPyenv(...)` — performs the subprocess calls to `pyenv` and parses results. |
| 18 | + - `resolvePyenvPath(...)` — resolve a path supplied by user/workspace to a canonical interpreter `PythonEnvironment`. |
| 19 | + - `getPyenvForGlobal()`, `getPyenvForWorkspace()` / `setPyenvForWorkspace()` / `setPyenvForGlobal()` — persist and load user selections. |
| 20 | + - `clearPyenvCache()` — remove persisted selections. |
| 21 | + - `PYENV_VERSIONS` — group identifier used on environment entries. |
| 22 | + |
| 23 | +Other integration points: |
| 24 | +- `src/common/pickers/environments.ts` — where environment entries from managers are presented in the UI. |
| 25 | +- `src/api.ts` and `src/extension.ts` — top-level APIs and registration that tie managers into the extension. |
| 26 | + |
| 27 | +## Design goals |
| 28 | + |
| 29 | +- Accurately discover pyenv versions and virtualenvs. |
| 30 | +- Provide stable, human-friendly metadata for each environment (id, displayName, version, type, interpreter path). |
| 31 | +- Cache results to avoid repeated heavy shell calls; run subprocesses asynchronously. |
| 32 | +- Handle shell initialization nuances that affect `pyenv` (shims and shell init scripts). |
| 33 | +- Work across macOS/Linux and Windows (including WSL) where possible. |
| 34 | + |
| 35 | +## How discovery works (conceptual flow) |
| 36 | + |
| 37 | +1. Manager availability check: verify `pyenv` is present (may require sourcing the user's shell configuration). |
| 38 | +2. Invoke low-level utils (e.g., `refreshPyenv`) which run `pyenv` commands to enumerate versions/virtualenvs. |
| 39 | +3. Parse `pyenv` output into `PythonEnvironment` objects that include interpreter path, group, and metadata. |
| 40 | +4. Add to the manager collection and build maps from project folders to environments. |
| 41 | +5. Expose results via the manager interface and emit change events when collection changes. |
| 42 | + |
| 43 | +### Typical metadata produced for each environment |
| 44 | +- envId.id — stable identifier (e.g., `pyenv:3.8.10` or `pyenv:3.8.10:myenv`). |
| 45 | +- environmentPath — a `Uri` referencing the python executable. |
| 46 | +- version — parsed version string (when available). |
| 47 | +- displayName — friendly label shown in UI. |
| 48 | +- group — `PYENV_VERSIONS` (used for grouping in pickers). |
| 49 | + |
| 50 | +## Platform and Windows/WSL considerations |
| 51 | + |
| 52 | +- macOS/Linux: `pyenv` is commonly installed and initialized through shell startup files. The utils layer includes logic to run commands in a shell that sources the user's init files so pyenv shims resolve correctly. |
| 53 | + |
| 54 | +- Windows: `pyenv` is not natively supported on Windows. Many Windows users run `pyenv` inside WSL. For these users: |
| 55 | + - If the extension is running in a Windows host but the project is inside WSL or a remote environment, discovery should be performed in that environment (follow the same pattern used for other managers under remote/WSL scenarios). |
| 56 | + - If `pyenv` is installed inside WSL, ensure subprocess invocations happen in WSL context (the utilities may need to be extended to shell out via `wsl.exe` or use the remote extension plumbing). |
| 57 | + - When running on Windows without WSL, mark pyenv as unavailable. The manager should gracefully no-op and not cause failures. |
| 58 | + |
| 59 | +Be explicit in code when a path or command is intended for a remote/WSL environment; do not assume a single PATH behavior across platforms. |
| 60 | + |
| 61 | +## Sequence diagram (high-level) |
| 62 | + |
| 63 | +Here's a simple sequence diagram showing a typical discovery flow. This can be helpful when adding instrumentation or debugging: |
| 64 | + |
| 65 | +```mermaid |
| 66 | +sequenceDiagram |
| 67 | + participant Manager as PyEnvManager |
| 68 | + participant Utils as pyenvUtils |
| 69 | + participant Shell as Shell/Subprocess |
| 70 | + participant API as PythonEnvironmentApi |
| 71 | +
|
| 72 | + Manager->>Utils: refreshPyenv(nativeFinder, api, self) |
| 73 | + Utils->>Shell: run `pyenv versions --bare` (sourced shell) |
| 74 | + Shell-->>Utils: stdout lines (versions) |
| 75 | + Utils->>Utils: parse lines -> PythonEnvironment[] |
| 76 | + Utils-->>Manager: return environments |
| 77 | + Manager->>API: loadEnvMap() (get/set workspace/global prefs) |
| 78 | + Manager->>Picker: emit onDidChangeEnvironments |
| 79 | + Picker-->>User: show updated environment list |
| 80 | +``` |
| 81 | + |
| 82 | +## Integration & consumers |
| 83 | + |
| 84 | +Who uses the manager and where to update when changing interfaces: |
| 85 | + |
| 86 | +- UI pickers: `src/common/pickers/environments.ts` — verify how environment objects are consumed and displayed. |
| 87 | +- Project-level selection: `src/managers/pyenv/pyenvManager.ts#set` and `loadEnvMap()` manage workspace/global mapping persisted via `pyenvUtils`. |
| 88 | +- Environment resolution used when a folder/URI is provided: `pyenvManager.resolve()` delegates to `resolvePyenvPath()`. |
| 89 | + |
| 90 | +When adding features that alter environment object shapes, update both the manager and the pickers/consumers that rely on those fields. |
| 91 | + |
| 92 | +## Debugging & tracing |
| 93 | + |
| 94 | +- Enable trace logs and capture subprocess invocation details (command, env, cwd) and raw stdout/stderr when troubleshooting discovery/parsing issues. |
| 95 | +- Reproduce failing `pyenv` invocations in a terminal that matches the shell the extension uses (same login/non-login, same PATH and shell init files). |
| 96 | +- When `pyenv` returns unexpected output, capture the exact stdout and stderr and compare against what `pyenvUtils` expects to parse. |
| 97 | + |
| 98 | +## Important implementation notes & gotchas (concise) |
| 99 | + |
| 100 | +- Shell initialization: `pyenv` often requires shell init scripts to be sourced. The utils layer accounts for this; don't remove that logic without verifying discovery still works for typical user setups. |
| 101 | +- Shims vs real binaries: prefer resolving the real interpreter path rather than exposing the shim path to callers where feasible. |
| 102 | +- Virtualenv names and aliases: keep the raw pyenv value in metadata; normalize only for display. |
| 103 | +- Concurrency: avoid firing many simultaneous `pyenv` subprocesses — throttle or serialize as needed. |
| 104 | + |
| 105 | +## File & symbol references (quick jump list) |
| 106 | + |
| 107 | +- Manager class and methods: `src/managers/pyenv/pyenvManager.ts` (class `PyEnvManager`, methods listed earlier). |
| 108 | +- Low-level utils and exported helpers: `src/managers/pyenv/pyenvUtils.ts` (look for `refreshPyenv`, `resolvePyenvPath`, `getPyenvForGlobal`, `getPyenvForWorkspace`, `clearPyenvCache`). |
| 109 | +- Manager registration: `src/managers/pyenv/main.ts`. |
| 110 | +- UI pickers: `src/common/pickers/environments.ts`, `src/common/pickers/managers.ts`. |
| 111 | +- API surfaces: `src/api.ts`, `src/extension.ts`, and `src/common/*` interfaces used by managers. |
0 commit comments