Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion src/features/envManagers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1086,6 +1086,14 @@ export class PythonEnvironmentManagers implements EnvironmentManagers {
context: Uri | undefined,
project: PythonProject | undefined,
): InternalEnvironmentManager | undefined {
const cachedEnv = this._activeSelection.get(this.getProjectSelectionKey(project));
if (context === undefined && cachedEnv) {
const cachedManager = this._environmentManagers.get(cachedEnv.envId.managerId);
if (cachedManager) {
return cachedManager;
}
}

const defaultEnvManagerId = getDefaultEnvManagerSetting(this.pm, context);
if (defaultEnvManagerId !== undefined) {
const settingsManager = this._environmentManagers.get(defaultEnvManagerId);
Expand All @@ -1094,7 +1102,6 @@ export class PythonEnvironmentManagers implements EnvironmentManagers {
}
}

const cachedEnv = this._activeSelection.get(this.getProjectSelectionKey(project));
if (cachedEnv) {
const cachedManager = this._environmentManagers.get(cachedEnv.envId.managerId);
if (cachedManager) {
Expand Down
102 changes: 102 additions & 0 deletions src/test/features/envManagers.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,108 @@ suite('PythonEnvironmentManagers - getEnvironment', () => {
});
});

suite('PythonEnvironmentManagers - cross-manager global selection', () => {
let sandbox: sinon.SinonSandbox;
let envManagers: PythonEnvironmentManagers;
let mockProjectManager: sinon.SinonStubbedInstance<PythonProjectManager>;

const selectedSystemEnvironment: PythonEnvironment = {
envId: { id: 'selected-system', managerId: 'ms-python.python:system' },
name: 'Selected system Python',
displayName: 'Selected system Python',
version: '3.13.0',
displayPath: '/usr/bin/python3.13',
environmentPath: Uri.file('/usr/bin/python3.13'),
sysPrefix: '/usr',
execInfo: { run: { executable: '/usr/bin/python3.13' } },
};

const configuredVenvEnvironment: PythonEnvironment = {
envId: { id: 'configured-venv', managerId: 'ms-python.python:venv' },
name: 'Configured venv',
displayName: 'Configured venv',
version: '3.12.0',
displayPath: '/workspace/.venv/bin/python',
environmentPath: Uri.file('/workspace/.venv/bin/python'),
sysPrefix: '/workspace/.venv',
execInfo: { run: { executable: '/workspace/.venv/bin/python' } },
};

setup(() => {
sandbox = sinon.createSandbox();
sandbox.stub(frameUtils, 'getCallingExtension').returns('ms-python.python');

sandbox.stub(workspaceApis, 'getConfiguration').returns({
get: (key: string, defaultValue?: unknown) => {
if (key === 'defaultEnvManager') {
return 'ms-python.python:venv';
}
if (key === 'pythonProjects') {
return [];
}
return defaultValue;
},
has: () => false,
inspect: () => undefined,
update: () => Promise.resolve(),
} as any);

mockProjectManager = {
getProjects: sandbox.stub().returns([]),
get: sandbox.stub().returns(undefined),
} as unknown as sinon.SinonStubbedInstance<PythonProjectManager>;

envManagers = new PythonEnvironmentManagers(mockProjectManager as unknown as PythonProjectManager);
});

teardown(() => {
sandbox.restore();
});

function registerFakeManager(
managerId: string,
getStub: sinon.SinonStub,
setStub: sinon.SinonStub = sandbox.stub().resolves(),
): string {
const fakeManager = {
name: managerId.split(':')[1],
displayName: managerId,
preferredPackageManagerId: 'ms-python.python:pip',
get: getStub,
set: setStub,
resolve: sandbox.stub().resolves(undefined),
refresh: sandbox.stub().resolves(),
getEnvironments: sandbox.stub().resolves([]),
onDidChangeEnvironments: sandbox.stub().returns({ dispose: () => {} }),
onDidChangeEnvironment: sandbox.stub().returns({ dispose: () => {} }),
};
envManagers.registerEnvironmentManager(fakeManager as any, { extensionId: 'ms-python.python' });
return managerId;
}

test('routes and reads a global selection through its selected manager', async () => {
const configuredVenvGet = sandbox.stub().resolves(configuredVenvEnvironment);
registerFakeManager('ms-python.python:venv', configuredVenvGet);

const selectedSystemGet = sandbox.stub().resolves(selectedSystemEnvironment);
registerFakeManager('ms-python.python:system', selectedSystemGet);

await envManagers.setEnvironments('global', selectedSystemEnvironment, false);

assert.strictEqual(
envManagers.getEnvironmentManager(undefined)?.id,
'ms-python.python:system',
'A global selection must route through the selected environment manager',
);
assert.strictEqual(
(await envManagers.getEnvironment(undefined))?.envId.id,
selectedSystemEnvironment.envId.id,
'Global readback must return the selected non-venv environment',
);
assert.ok(configuredVenvGet.notCalled, 'The configured venv manager must not serve the selected global readback');
});
});

suite('PythonEnvironmentManagers - refreshEnvironment', () => {
let sandbox: sinon.SinonSandbox;
let envManagers: PythonEnvironmentManagers;
Expand Down