Skip to content

Commit 9603427

Browse files
committed
support poetry terminal activation required for versions > 2.0.0
1 parent b8d1a91 commit 9603427

4 files changed

Lines changed: 149 additions & 161 deletions

File tree

src/managers/builtin/venvUtils.ts

Lines changed: 3 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,15 @@
11
import * as fsapi from 'fs-extra';
22
import * as os from 'os';
33
import * as path from 'path';
4-
import { l10n, LogOutputChannel, ProgressLocation, QuickPickItem, QuickPickItemKind, ThemeIcon, Uri } from 'vscode';
5-
import {
6-
EnvironmentManager,
7-
PythonCommandRunConfiguration,
8-
PythonEnvironment,
9-
PythonEnvironmentApi,
10-
PythonEnvironmentInfo,
11-
} from '../../api';
4+
import { l10n, LogOutputChannel, ProgressLocation, QuickPickItem, QuickPickItemKind, Uri } from 'vscode';
5+
import { EnvironmentManager, PythonEnvironment, PythonEnvironmentApi } from '../../api';
126
import { ENVS_EXTENSION_ID } from '../../common/constants';
137
import { Common, VenvManagerStrings } from '../../common/localize';
148
import { traceInfo } from '../../common/logging';
159
import { getWorkspacePersistentState } from '../../common/persistentState';
1610
import { pickEnvironmentFrom } from '../../common/pickers/environments';
1711
import { EventNames } from '../../common/telemetry/constants';
1812
import { sendTelemetryEvent } from '../../common/telemetry/sender';
19-
import { isWindows } from '../../common/utils/platformUtils';
2013
import {
2114
showErrorMessage,
2215
showInputBox,
@@ -26,14 +19,13 @@ import {
2619
withProgress,
2720
} from '../../common/window.apis';
2821
import { getConfiguration } from '../../common/workspace.apis';
29-
import { ShellConstants } from '../../features/common/shellConstants';
3022
import {
3123
isNativeEnvInfo,
3224
NativeEnvInfo,
3325
NativePythonEnvironmentKind,
3426
NativePythonFinder,
3527
} from '../common/nativePythonFinder';
36-
import { pathForGitBash, shortVersion, sortEnvironments } from '../common/utils';
28+
import { getPythonInfo, sortEnvironments } from '../common/utils';
3729
import { isUvInstalled, runPython, runUV } from './helpers';
3830
import { getProjectInstallable, getWorkspacePackagesToInstall, PipPackages } from './pipUtils';
3931
import { resolveSystemPythonEnvironmentPath } from './utils';
@@ -106,122 +98,7 @@ export async function setVenvForGlobal(envPath: string | undefined): Promise<voi
10698
await state.set(VENV_GLOBAL_KEY, envPath);
10799
}
108100

109-
function getName(binPath: string): string {
110-
const dir1 = path.dirname(binPath);
111-
if (dir1.endsWith('bin') || dir1.endsWith('Scripts') || dir1.endsWith('scripts')) {
112-
return path.basename(path.dirname(dir1));
113-
}
114-
return path.basename(dir1);
115-
}
116-
117-
async function getPythonInfo(env: NativeEnvInfo): Promise<PythonEnvironmentInfo> {
118-
if (env.executable && env.version && env.prefix) {
119-
const venvName = env.name ?? getName(env.executable);
120-
const sv = shortVersion(env.version);
121-
const name = `${venvName} (${sv})`;
122-
123-
const binDir = path.dirname(env.executable);
124-
125-
const shellActivation: Map<string, PythonCommandRunConfiguration[]> = new Map();
126-
const shellDeactivation: Map<string, PythonCommandRunConfiguration[]> = new Map();
127-
128-
if (isWindows()) {
129-
shellActivation.set('unknown', [{ executable: path.join(binDir, `activate`) }]);
130-
shellDeactivation.set('unknown', [{ executable: path.join(binDir, `deactivate`) }]);
131-
} else {
132-
shellActivation.set('unknown', [{ executable: 'source', args: [path.join(binDir, `activate`)] }]);
133-
shellDeactivation.set('unknown', [{ executable: 'deactivate' }]);
134-
}
135-
136-
shellActivation.set(ShellConstants.SH, [{ executable: 'source', args: [path.join(binDir, `activate`)] }]);
137-
shellDeactivation.set(ShellConstants.SH, [{ executable: 'deactivate' }]);
138-
139-
shellActivation.set(ShellConstants.BASH, [{ executable: 'source', args: [path.join(binDir, `activate`)] }]);
140-
shellDeactivation.set(ShellConstants.BASH, [{ executable: 'deactivate' }]);
141-
142-
shellActivation.set(ShellConstants.GITBASH, [
143-
{ executable: 'source', args: [pathForGitBash(path.join(binDir, `activate`))] },
144-
]);
145-
shellDeactivation.set(ShellConstants.GITBASH, [{ executable: 'deactivate' }]);
146-
147-
shellActivation.set(ShellConstants.ZSH, [{ executable: 'source', args: [path.join(binDir, `activate`)] }]);
148-
shellDeactivation.set(ShellConstants.ZSH, [{ executable: 'deactivate' }]);
149-
150-
shellActivation.set(ShellConstants.KSH, [{ executable: '.', args: [path.join(binDir, `activate`)] }]);
151-
shellDeactivation.set(ShellConstants.KSH, [{ executable: 'deactivate' }]);
152-
153-
if (await fsapi.pathExists(path.join(binDir, 'Activate.ps1'))) {
154-
shellActivation.set(ShellConstants.PWSH, [{ executable: '&', args: [path.join(binDir, `Activate.ps1`)] }]);
155-
shellDeactivation.set(ShellConstants.PWSH, [{ executable: 'deactivate' }]);
156-
} else if (await fsapi.pathExists(path.join(binDir, 'activate.ps1'))) {
157-
shellActivation.set(ShellConstants.PWSH, [{ executable: '&', args: [path.join(binDir, `activate.ps1`)] }]);
158-
shellDeactivation.set(ShellConstants.PWSH, [{ executable: 'deactivate' }]);
159-
}
160101

161-
if (await fsapi.pathExists(path.join(binDir, 'activate.bat'))) {
162-
shellActivation.set(ShellConstants.CMD, [{ executable: path.join(binDir, `activate.bat`) }]);
163-
shellDeactivation.set(ShellConstants.CMD, [{ executable: path.join(binDir, `deactivate.bat`) }]);
164-
}
165-
166-
if (await fsapi.pathExists(path.join(binDir, 'activate.csh'))) {
167-
shellActivation.set(ShellConstants.CSH, [
168-
{ executable: 'source', args: [path.join(binDir, `activate.csh`)] },
169-
]);
170-
shellDeactivation.set(ShellConstants.CSH, [{ executable: 'deactivate' }]);
171-
172-
shellActivation.set(ShellConstants.FISH, [
173-
{ executable: 'source', args: [path.join(binDir, `activate.csh`)] },
174-
]);
175-
shellDeactivation.set(ShellConstants.FISH, [{ executable: 'deactivate' }]);
176-
}
177-
178-
if (await fsapi.pathExists(path.join(binDir, 'activate.fish'))) {
179-
shellActivation.set(ShellConstants.FISH, [
180-
{ executable: 'source', args: [path.join(binDir, `activate.fish`)] },
181-
]);
182-
shellDeactivation.set(ShellConstants.FISH, [{ executable: 'deactivate' }]);
183-
}
184-
185-
if (await fsapi.pathExists(path.join(binDir, 'activate.xsh'))) {
186-
shellActivation.set(ShellConstants.XONSH, [
187-
{ executable: 'source', args: [path.join(binDir, `activate.xsh`)] },
188-
]);
189-
shellDeactivation.set(ShellConstants.XONSH, [{ executable: 'deactivate' }]);
190-
}
191-
192-
if (await fsapi.pathExists(path.join(binDir, 'activate.nu'))) {
193-
shellActivation.set(ShellConstants.NU, [
194-
{ executable: 'overlay', args: ['use', path.join(binDir, 'activate.nu')] },
195-
]);
196-
shellDeactivation.set(ShellConstants.NU, [{ executable: 'overlay', args: ['hide', 'activate'] }]);
197-
}
198-
199-
return {
200-
name: name,
201-
displayName: name,
202-
shortDisplayName: `${sv} (${venvName})`,
203-
displayPath: env.executable,
204-
version: env.version,
205-
description: undefined,
206-
tooltip: env.executable,
207-
environmentPath: Uri.file(env.executable),
208-
iconPath: new ThemeIcon('python'),
209-
sysPrefix: env.prefix,
210-
execInfo: {
211-
run: {
212-
executable: env.executable,
213-
},
214-
activatedRun: {
215-
executable: env.executable,
216-
},
217-
shellActivation,
218-
shellDeactivation,
219-
},
220-
};
221-
} else {
222-
throw new Error(`Invalid python info: ${JSON.stringify(env)}`);
223-
}
224-
}
225102

226103
export async function findVirtualEnvironments(
227104
hardRefresh: boolean,

src/managers/common/utils.ts

Lines changed: 115 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1-
import { PythonEnvironment } from '../../api';
1+
import * as fs from 'fs-extra';
2+
import path from 'path';
3+
import { ThemeIcon, Uri } from 'vscode';
4+
import { PythonCommandRunConfiguration, PythonEnvironment, PythonEnvironmentInfo } from '../../api';
25
import { isWindows } from '../../common/utils/platformUtils';
6+
import { ShellConstants } from '../../features/common/shellConstants';
7+
import { NativeEnvInfo } from './nativePythonFinder';
38
import { Installable } from './types';
49

510
export function noop() {
@@ -112,3 +117,112 @@ export function compareVersions(version1: string, version2: string): number {
112117

113118
return 0;
114119
}
120+
121+
export async function getPythonInfo(env: NativeEnvInfo): Promise<PythonEnvironmentInfo> {
122+
if (env.executable && env.version && env.prefix) {
123+
const venvName = env.name;
124+
const sv = shortVersion(env.version);
125+
const name = `${venvName} (${sv})`;
126+
127+
const binDir = path.dirname(env.executable);
128+
129+
const shellActivation: Map<string, PythonCommandRunConfiguration[]> = new Map();
130+
const shellDeactivation: Map<string, PythonCommandRunConfiguration[]> = new Map();
131+
132+
if (isWindows()) {
133+
shellActivation.set('unknown', [{ executable: path.join(binDir, `activate`) }]);
134+
shellDeactivation.set('unknown', [{ executable: path.join(binDir, `deactivate`) }]);
135+
} else {
136+
shellActivation.set('unknown', [{ executable: 'source', args: [path.join(binDir, `activate`)] }]);
137+
shellDeactivation.set('unknown', [{ executable: 'deactivate' }]);
138+
}
139+
140+
shellActivation.set(ShellConstants.SH, [{ executable: 'source', args: [path.join(binDir, `activate`)] }]);
141+
shellDeactivation.set(ShellConstants.SH, [{ executable: 'deactivate' }]);
142+
143+
shellActivation.set(ShellConstants.BASH, [{ executable: 'source', args: [path.join(binDir, `activate`)] }]);
144+
shellDeactivation.set(ShellConstants.BASH, [{ executable: 'deactivate' }]);
145+
146+
shellActivation.set(ShellConstants.GITBASH, [
147+
{ executable: 'source', args: [pathForGitBash(path.join(binDir, `activate`))] },
148+
]);
149+
shellDeactivation.set(ShellConstants.GITBASH, [{ executable: 'deactivate' }]);
150+
151+
shellActivation.set(ShellConstants.ZSH, [{ executable: 'source', args: [path.join(binDir, `activate`)] }]);
152+
shellDeactivation.set(ShellConstants.ZSH, [{ executable: 'deactivate' }]);
153+
154+
shellActivation.set(ShellConstants.KSH, [{ executable: '.', args: [path.join(binDir, `activate`)] }]);
155+
shellDeactivation.set(ShellConstants.KSH, [{ executable: 'deactivate' }]);
156+
157+
if (await fs.pathExists(path.join(binDir, 'Activate.ps1'))) {
158+
shellActivation.set(ShellConstants.PWSH, [{ executable: '&', args: [path.join(binDir, `Activate.ps1`)] }]);
159+
shellDeactivation.set(ShellConstants.PWSH, [{ executable: 'deactivate' }]);
160+
} else if (await fs.pathExists(path.join(binDir, 'activate.ps1'))) {
161+
shellActivation.set(ShellConstants.PWSH, [{ executable: '&', args: [path.join(binDir, `activate.ps1`)] }]);
162+
shellDeactivation.set(ShellConstants.PWSH, [{ executable: 'deactivate' }]);
163+
}
164+
165+
if (await fs.pathExists(path.join(binDir, 'activate.bat'))) {
166+
shellActivation.set(ShellConstants.CMD, [{ executable: path.join(binDir, `activate.bat`) }]);
167+
shellDeactivation.set(ShellConstants.CMD, [{ executable: path.join(binDir, `deactivate.bat`) }]);
168+
}
169+
170+
if (await fs.pathExists(path.join(binDir, 'activate.csh'))) {
171+
shellActivation.set(ShellConstants.CSH, [
172+
{ executable: 'source', args: [path.join(binDir, `activate.csh`)] },
173+
]);
174+
shellDeactivation.set(ShellConstants.CSH, [{ executable: 'deactivate' }]);
175+
176+
shellActivation.set(ShellConstants.FISH, [
177+
{ executable: 'source', args: [path.join(binDir, `activate.csh`)] },
178+
]);
179+
shellDeactivation.set(ShellConstants.FISH, [{ executable: 'deactivate' }]);
180+
}
181+
182+
if (await fs.pathExists(path.join(binDir, 'activate.fish'))) {
183+
shellActivation.set(ShellConstants.FISH, [
184+
{ executable: 'source', args: [path.join(binDir, `activate.fish`)] },
185+
]);
186+
shellDeactivation.set(ShellConstants.FISH, [{ executable: 'deactivate' }]);
187+
}
188+
189+
if (await fs.pathExists(path.join(binDir, 'activate.xsh'))) {
190+
shellActivation.set(ShellConstants.XONSH, [
191+
{ executable: 'source', args: [path.join(binDir, `activate.xsh`)] },
192+
]);
193+
shellDeactivation.set(ShellConstants.XONSH, [{ executable: 'deactivate' }]);
194+
}
195+
196+
if (await fs.pathExists(path.join(binDir, 'activate.nu'))) {
197+
shellActivation.set(ShellConstants.NU, [
198+
{ executable: 'overlay', args: ['use', path.join(binDir, 'activate.nu')] },
199+
]);
200+
shellDeactivation.set(ShellConstants.NU, [{ executable: 'overlay', args: ['hide', 'activate'] }]);
201+
}
202+
203+
return {
204+
name: name,
205+
displayName: name,
206+
shortDisplayName: `${sv} (${venvName})`,
207+
displayPath: env.executable,
208+
version: env.version,
209+
description: undefined,
210+
tooltip: env.executable,
211+
environmentPath: Uri.file(env.executable),
212+
iconPath: new ThemeIcon('python'),
213+
sysPrefix: env.prefix,
214+
execInfo: {
215+
run: {
216+
executable: env.executable,
217+
},
218+
activatedRun: {
219+
executable: env.executable,
220+
},
221+
shellActivation,
222+
shellDeactivation,
223+
},
224+
};
225+
} else {
226+
throw new Error(`Invalid python info: ${JSON.stringify(env)}`);
227+
}
228+
}

src/managers/poetry/main.ts

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { NativePythonFinder } from '../common/nativePythonFinder';
77
import { compareVersions } from '../common/utils';
88
import { PoetryManager } from './poetryManager';
99
import { PoetryPackageManager } from './poetryPackageManager';
10-
import { getPoetry, getPoetryVersion, isPoetryShellPluginInstalled } from './poetryUtils';
10+
import { getPoetry, getPoetryVersion } from './poetryUtils';
1111

1212
export async function registerPoetryFeatures(
1313
nativeFinder: NativePythonFinder,
@@ -18,23 +18,19 @@ export async function registerPoetryFeatures(
1818

1919
try {
2020
const poetryPath = await getPoetry(nativeFinder);
21-
let shellSupported = true;
2221
if (poetryPath) {
2322
const version = await getPoetryVersion(poetryPath);
2423
if (!version) {
2524
showErrorMessage(l10n.t('Poetry version could not be determined.'));
2625
return;
2726
}
27+
traceInfo(
28+
'The `shell` command is not available by default in Poetry versions 2.0.0 and above. Therefore all shell activation will be handled by calling `source <path-to-poetry-executable>`. If you face any problems with shell activation, please file an issue at https://github.com/microsoft/vscode-python-environments/issues to help us improve this implementation.',
29+
);
2830
if (version && compareVersions(version, '2.0.0') >= 0) {
29-
shellSupported = await isPoetryShellPluginInstalled(poetryPath);
30-
if (!shellSupported) {
31-
showErrorMessage(
32-
l10n.t(
33-
'Poetry 2.0.0+ detected. The `shell` command is not available by default. Please install the shell plugin to enable shell activation. See [here](https://python-poetry.org/docs/managing-environments/#activating-the-environment), shell [plugin](https://github.com/python-poetry/poetry-plugin-shell)',
34-
),
35-
);
36-
return;
37-
}
31+
traceInfo('Note: The current version of Poetry is 2.0.0 or higher.');
32+
} else {
33+
traceInfo('Note: The current version of Poetry is lower than 2.0.0.');
3834
}
3935

4036
const envManager = new PoetryManager(nativeFinder, api);

0 commit comments

Comments
 (0)