Skip to content

Commit 292c702

Browse files
Add env manager pipenv (#749)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
1 parent e8f35f2 commit 292c702

6 files changed

Lines changed: 625 additions & 28 deletions

File tree

docs/pipenv-integration.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Pipenv Environment Manager — Implementation Plan
2+
3+
Summary
4+
- This doc lists the tasks required to get pipenv integrated as an EnvironmentManager
5+
6+
Files & methods to implement
7+
1. Registration/activation (src/managers/pipenv/main.ts)
8+
- Implement `registerPipenvFeatures(nativeFinder: NativePythonFinder, disposables: Disposable[])`.
9+
- Use the same registration pattern as `pyenv` and `poetry` (get Python API, detect pipenv, instantiate manager, `api.registerEnvironmentManager(mgr)`, push disposables).
10+
11+
2. Utilities (src/managers/pipenv/pipenvUtils.ts)
12+
- `getPipenv(native?: NativePythonFinder)` — locate the `pipenv` binary (persisted override, env vars, which, or `nativeFinder` fallback).
13+
- `refreshPipenv(hardRefresh, nativeFinder, api, manager)` — discover pipenv environments (workspace Pipfiles, native finder info, or scanning `WORKON_HOME`).
14+
- `resolvePipenvPath(fsPath, nativeFinder, api, manager)` — resolve a path or Uri to a PythonEnvironment.
15+
- `nativeToPythonEnv(nativeInfo, api, manager, pipenvPath)` — convert native discovery info into a `PythonEnvironment`:
16+
- set `execInfo.run.executable` (use `pipenv --py` when possible) and `execInfo.shellActivation` (see activation strategy below), `sysPrefix`, display metadata.
17+
18+
3. Manager implementation (src/managers/pipenv/pipenvManager.ts)
19+
- Implement class like `PoetryManager`/`PyEnvManager`:
20+
- fields: `collection: PythonEnvironment[]`, `fsPathToEnv: Map<string, PythonEnvironment>`, `globalEnv`.
21+
- event emitters: `_onDidChangeEnvironments`, `_onDidChangeEnvironment` and public events.
22+
- constructor(nativeFinder, api) and metadata properties (`name`, `displayName`, `preferredPackageManagerId`, `tooltip`).
23+
- lifecycle methods: `initialize()`, `getEnvironments()`, `refresh()`, `get()`, `set()`, `resolve()`, `clearCache()`.
24+
- helpers: `loadEnvMap()`, `fromEnvMap(uri)`, `findEnvironmentByPath(fsPath)`.
25+
- Use `api.createPythonEnvironmentItem()` to create `PythonEnvironment` items.
26+
27+
4. Exec info & activation behavior
28+
- Resolve a Python executable using `pipenv --py` when possible and set `execInfo.run.executable`.
29+
- Activation options:
30+
- Provide `shellActivation` mapping with `'unknown'` fallback. For example `{ executable: 'pipenv', args: ['shell'] }` for activation.
31+
- Provide `activatedRun` or `run` that uses resolved python (`/path/to/venv/bin/python`) or fallback to `pipenv run python` (e.g., run.executable = 'pipenv', args = ['run', 'python']).
32+
- Set `shellDeactivation` to `exit`/`deactivate` where appropriate.
33+
34+
5. Workspace mapping & persistence
35+
- Implement per-workspace persistent selection (get/set persisted environment for workspace & global), similar to `pyenv` and `poetry` utils.
36+
- Implement logic in `loadEnvMap()` to pick project-specific envs (Pipfile location), global fallback, and mapping to projects via `api.getPythonProjects()`.
37+
38+
6. Package-manager (required)
39+
- Implement a dedicated Pipenv `PackageManager` and register it via `api.registerPackageManager(...)`.
40+
- Use package manager id: `ms-python.python:pipenv`.
41+
- Implement install/uninstall by invoking `pipenv install`/`pipenv uninstall` and firing package-change events.
42+
43+
7. Tests
44+
- Add unit tests (mocking `NativePythonFinder` and `getPythonApi`) for detection, discovery, `resolve()` and mapping.
45+
- Add integration tests that run `pipenv --py`/`pipenv --venv` behavior using a test fixture if desired.
46+
47+
8. Localization & assets
48+
- Add localized strings (e.g., `PipenvStrings`) for messages and progress titles.
49+
- Add icon(s) if required and reference via `iconPath`.
50+
51+
9. Documentation
52+
- Update README/docs to include Pipenv support and configuration/setting notes.
53+
54+
10. CI & linting
55+
- Run tests and fix TypeScript compile/lint issues (unused args, correct imports). Ensure `main.ts` registration uses `api.registerEnvironmentManager` like other managers.
56+
57+
Minimal viable implementation (priority)
58+
1. Fix `main.ts` to implement `registerPipenvFeatures(...)` and register the manager (so the manager is known to the extension).
59+
2. Implement `getPipenv()` (detect pipenv binary) and `nativeToPythonEnv()` (at minimum obtain python path using `pipenv --py` and return a valid `PythonEnvironment` via `api.createPythonEnvironmentItem`).
60+
3. Implement manager skeleton (constructor, event emitters, `initialize()`, `getEnvironments()` and `resolve()` that uses utils above) and wire registration.
61+
4. Add a simple integration test and run the extension in dev to validate detection.
62+
63+
Questions / decisions (resolved)
64+
- preferredPackageManagerId: create a distinct `pipenv` package manager id: `ms-python.python:pipenv`.
65+
66+
- Activation approach: use `pipenv shell` for terminal activation (interactive terminals) and `pipenv run` as the fallback for non-interactive runs / `activatedRun`.
67+
68+
- Scope of discovery: discover both global pipenv virtualenvs and workspace-local pipenv environments (projects with Pipfile).
69+
70+
- Create/quickCreate: implement `create()` using `pipenv install` to create environments and install requested packages as part of quick-create.
71+
72+
- Windows/PowerShell specifics: keep `shellActivation` mapping with `'unknown'` fallback for now; revisit if issues surface.
73+
74+
- Tests: (deferred).

src/extension.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ import {
7676
} from './managers/common/nativePythonFinder';
7777
import { IDisposable } from './managers/common/types';
7878
import { registerCondaFeatures } from './managers/conda/main';
79+
import { registerPipenvFeatures } from './managers/pipenv/main';
7980
import { registerPoetryFeatures } from './managers/poetry/main';
8081
import { registerPyenvFeatures } from './managers/pyenv/main';
8182

@@ -562,6 +563,7 @@ export async function activate(context: ExtensionContext): Promise<PythonEnviron
562563
registerSystemPythonFeatures(nativeFinder, context.subscriptions, outputChannel, sysMgr),
563564
registerCondaFeatures(nativeFinder, context.subscriptions, outputChannel),
564565
registerPyenvFeatures(nativeFinder, context.subscriptions),
566+
registerPipenvFeatures(nativeFinder, context.subscriptions),
565567
registerPoetryFeatures(nativeFinder, context.subscriptions, outputChannel),
566568
shellStartupVarsMgr.initialize(),
567569
]);

src/managers/pipenv/main.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { traceInfo } from '../../common/logging';
44
import { getPythonApi } from '../../features/pythonApi';
55
import { NativePythonFinder } from '../common/nativePythonFinder';
66
import { PipenvManager } from './pipenvManager';
7+
import { PipenvPackageManager } from './pipenvPackageManager';
78
import { getPipenv } from './pipenvUtils';
89

910
export async function registerPipenvFeatures(
@@ -17,7 +18,14 @@ export async function registerPipenvFeatures(
1718

1819
if (pipenv) {
1920
const mgr = new PipenvManager(nativeFinder, api);
20-
disposables.push(mgr, api.registerEnvironmentManager(mgr));
21+
const packageManager = new PipenvPackageManager(api);
22+
23+
disposables.push(
24+
mgr,
25+
packageManager,
26+
api.registerEnvironmentManager(mgr),
27+
api.registerPackageManager(packageManager)
28+
);
2129
} else {
2230
traceInfo('Pipenv not found, turning off pipenv features.');
2331
}

0 commit comments

Comments
 (0)