Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
129 changes: 3 additions & 126 deletions src/managers/builtin/venvUtils.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,15 @@
import * as fsapi from 'fs-extra';
import * as os from 'os';
import * as path from 'path';
import { l10n, LogOutputChannel, ProgressLocation, QuickPickItem, QuickPickItemKind, ThemeIcon, Uri } from 'vscode';
import {
EnvironmentManager,
PythonCommandRunConfiguration,
PythonEnvironment,
PythonEnvironmentApi,
PythonEnvironmentInfo,
} from '../../api';
import { l10n, LogOutputChannel, ProgressLocation, QuickPickItem, QuickPickItemKind, Uri } from 'vscode';
import { EnvironmentManager, PythonEnvironment, PythonEnvironmentApi } from '../../api';
import { ENVS_EXTENSION_ID } from '../../common/constants';
import { Common, VenvManagerStrings } from '../../common/localize';
import { traceInfo } from '../../common/logging';
import { getWorkspacePersistentState } from '../../common/persistentState';
import { pickEnvironmentFrom } from '../../common/pickers/environments';
import { EventNames } from '../../common/telemetry/constants';
import { sendTelemetryEvent } from '../../common/telemetry/sender';
import { isWindows } from '../../common/utils/platformUtils';
import {
showErrorMessage,
showInputBox,
Expand All @@ -26,14 +19,13 @@ import {
withProgress,
} from '../../common/window.apis';
import { getConfiguration } from '../../common/workspace.apis';
import { ShellConstants } from '../../features/common/shellConstants';
import {
isNativeEnvInfo,
NativeEnvInfo,
NativePythonEnvironmentKind,
NativePythonFinder,
} from '../common/nativePythonFinder';
import { pathForGitBash, shortVersion, sortEnvironments } from '../common/utils';
import { getPythonInfo, sortEnvironments } from '../common/utils';
import { isUvInstalled, runPython, runUV } from './helpers';
import { getProjectInstallable, getWorkspacePackagesToInstall, PipPackages } from './pipUtils';
import { resolveSystemPythonEnvironmentPath } from './utils';
Expand Down Expand Up @@ -106,122 +98,7 @@ export async function setVenvForGlobal(envPath: string | undefined): Promise<voi
await state.set(VENV_GLOBAL_KEY, envPath);
}

function getName(binPath: string): string {
const dir1 = path.dirname(binPath);
if (dir1.endsWith('bin') || dir1.endsWith('Scripts') || dir1.endsWith('scripts')) {
return path.basename(path.dirname(dir1));
}
return path.basename(dir1);
}

async function getPythonInfo(env: NativeEnvInfo): Promise<PythonEnvironmentInfo> {
if (env.executable && env.version && env.prefix) {
const venvName = env.name ?? getName(env.executable);
const sv = shortVersion(env.version);
const name = `${venvName} (${sv})`;

const binDir = path.dirname(env.executable);

const shellActivation: Map<string, PythonCommandRunConfiguration[]> = new Map();
const shellDeactivation: Map<string, PythonCommandRunConfiguration[]> = new Map();

if (isWindows()) {
shellActivation.set('unknown', [{ executable: path.join(binDir, `activate`) }]);
shellDeactivation.set('unknown', [{ executable: path.join(binDir, `deactivate`) }]);
} else {
shellActivation.set('unknown', [{ executable: 'source', args: [path.join(binDir, `activate`)] }]);
shellDeactivation.set('unknown', [{ executable: 'deactivate' }]);
}

shellActivation.set(ShellConstants.SH, [{ executable: 'source', args: [path.join(binDir, `activate`)] }]);
shellDeactivation.set(ShellConstants.SH, [{ executable: 'deactivate' }]);

shellActivation.set(ShellConstants.BASH, [{ executable: 'source', args: [path.join(binDir, `activate`)] }]);
shellDeactivation.set(ShellConstants.BASH, [{ executable: 'deactivate' }]);

shellActivation.set(ShellConstants.GITBASH, [
{ executable: 'source', args: [pathForGitBash(path.join(binDir, `activate`))] },
]);
shellDeactivation.set(ShellConstants.GITBASH, [{ executable: 'deactivate' }]);

shellActivation.set(ShellConstants.ZSH, [{ executable: 'source', args: [path.join(binDir, `activate`)] }]);
shellDeactivation.set(ShellConstants.ZSH, [{ executable: 'deactivate' }]);

shellActivation.set(ShellConstants.KSH, [{ executable: '.', args: [path.join(binDir, `activate`)] }]);
shellDeactivation.set(ShellConstants.KSH, [{ executable: 'deactivate' }]);

if (await fsapi.pathExists(path.join(binDir, 'Activate.ps1'))) {
shellActivation.set(ShellConstants.PWSH, [{ executable: '&', args: [path.join(binDir, `Activate.ps1`)] }]);
shellDeactivation.set(ShellConstants.PWSH, [{ executable: 'deactivate' }]);
} else if (await fsapi.pathExists(path.join(binDir, 'activate.ps1'))) {
shellActivation.set(ShellConstants.PWSH, [{ executable: '&', args: [path.join(binDir, `activate.ps1`)] }]);
shellDeactivation.set(ShellConstants.PWSH, [{ executable: 'deactivate' }]);
}

if (await fsapi.pathExists(path.join(binDir, 'activate.bat'))) {
shellActivation.set(ShellConstants.CMD, [{ executable: path.join(binDir, `activate.bat`) }]);
shellDeactivation.set(ShellConstants.CMD, [{ executable: path.join(binDir, `deactivate.bat`) }]);
}

if (await fsapi.pathExists(path.join(binDir, 'activate.csh'))) {
shellActivation.set(ShellConstants.CSH, [
{ executable: 'source', args: [path.join(binDir, `activate.csh`)] },
]);
shellDeactivation.set(ShellConstants.CSH, [{ executable: 'deactivate' }]);

shellActivation.set(ShellConstants.FISH, [
{ executable: 'source', args: [path.join(binDir, `activate.csh`)] },
]);
shellDeactivation.set(ShellConstants.FISH, [{ executable: 'deactivate' }]);
}

if (await fsapi.pathExists(path.join(binDir, 'activate.fish'))) {
shellActivation.set(ShellConstants.FISH, [
{ executable: 'source', args: [path.join(binDir, `activate.fish`)] },
]);
shellDeactivation.set(ShellConstants.FISH, [{ executable: 'deactivate' }]);
}

if (await fsapi.pathExists(path.join(binDir, 'activate.xsh'))) {
shellActivation.set(ShellConstants.XONSH, [
{ executable: 'source', args: [path.join(binDir, `activate.xsh`)] },
]);
shellDeactivation.set(ShellConstants.XONSH, [{ executable: 'deactivate' }]);
}

if (await fsapi.pathExists(path.join(binDir, 'activate.nu'))) {
shellActivation.set(ShellConstants.NU, [
{ executable: 'overlay', args: ['use', path.join(binDir, 'activate.nu')] },
]);
shellDeactivation.set(ShellConstants.NU, [{ executable: 'overlay', args: ['hide', 'activate'] }]);
}

return {
name: name,
displayName: name,
shortDisplayName: `${sv} (${venvName})`,
displayPath: env.executable,
version: env.version,
description: undefined,
tooltip: env.executable,
environmentPath: Uri.file(env.executable),
iconPath: new ThemeIcon('python'),
sysPrefix: env.prefix,
execInfo: {
run: {
executable: env.executable,
},
activatedRun: {
executable: env.executable,
},
shellActivation,
shellDeactivation,
},
};
} else {
throw new Error(`Invalid python info: ${JSON.stringify(env)}`);
}
}

export async function findVirtualEnvironments(
hardRefresh: boolean,
Expand Down
116 changes: 115 additions & 1 deletion src/managers/common/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { PythonEnvironment } from '../../api';
import * as fs from 'fs-extra';
import path from 'path';
import { ThemeIcon, Uri } from 'vscode';
import { PythonCommandRunConfiguration, PythonEnvironment, PythonEnvironmentInfo } from '../../api';
import { isWindows } from '../../common/utils/platformUtils';
import { ShellConstants } from '../../features/common/shellConstants';
import { NativeEnvInfo } from './nativePythonFinder';
import { Installable } from './types';

export function noop() {
Expand Down Expand Up @@ -112,3 +117,112 @@ export function compareVersions(version1: string, version2: string): number {

return 0;
}

export async function getPythonInfo(env: NativeEnvInfo): Promise<PythonEnvironmentInfo> {
if (env.executable && env.version && env.prefix) {
const venvName = env.name;
const sv = shortVersion(env.version);
const name = `${venvName} (${sv})`;

const binDir = path.dirname(env.executable);

const shellActivation: Map<string, PythonCommandRunConfiguration[]> = new Map();
const shellDeactivation: Map<string, PythonCommandRunConfiguration[]> = new Map();

if (isWindows()) {
shellActivation.set('unknown', [{ executable: path.join(binDir, `activate`) }]);
shellDeactivation.set('unknown', [{ executable: path.join(binDir, `deactivate`) }]);
} else {
shellActivation.set('unknown', [{ executable: 'source', args: [path.join(binDir, `activate`)] }]);
shellDeactivation.set('unknown', [{ executable: 'deactivate' }]);
}

shellActivation.set(ShellConstants.SH, [{ executable: 'source', args: [path.join(binDir, `activate`)] }]);
shellDeactivation.set(ShellConstants.SH, [{ executable: 'deactivate' }]);

shellActivation.set(ShellConstants.BASH, [{ executable: 'source', args: [path.join(binDir, `activate`)] }]);
shellDeactivation.set(ShellConstants.BASH, [{ executable: 'deactivate' }]);

shellActivation.set(ShellConstants.GITBASH, [
{ executable: 'source', args: [pathForGitBash(path.join(binDir, `activate`))] },
]);
shellDeactivation.set(ShellConstants.GITBASH, [{ executable: 'deactivate' }]);

shellActivation.set(ShellConstants.ZSH, [{ executable: 'source', args: [path.join(binDir, `activate`)] }]);
shellDeactivation.set(ShellConstants.ZSH, [{ executable: 'deactivate' }]);

shellActivation.set(ShellConstants.KSH, [{ executable: '.', args: [path.join(binDir, `activate`)] }]);
shellDeactivation.set(ShellConstants.KSH, [{ executable: 'deactivate' }]);

if (await fs.pathExists(path.join(binDir, 'Activate.ps1'))) {
shellActivation.set(ShellConstants.PWSH, [{ executable: '&', args: [path.join(binDir, `Activate.ps1`)] }]);
shellDeactivation.set(ShellConstants.PWSH, [{ executable: 'deactivate' }]);
} else if (await fs.pathExists(path.join(binDir, 'activate.ps1'))) {
shellActivation.set(ShellConstants.PWSH, [{ executable: '&', args: [path.join(binDir, `activate.ps1`)] }]);
shellDeactivation.set(ShellConstants.PWSH, [{ executable: 'deactivate' }]);
}

if (await fs.pathExists(path.join(binDir, 'activate.bat'))) {
shellActivation.set(ShellConstants.CMD, [{ executable: path.join(binDir, `activate.bat`) }]);
shellDeactivation.set(ShellConstants.CMD, [{ executable: path.join(binDir, `deactivate.bat`) }]);
}

if (await fs.pathExists(path.join(binDir, 'activate.csh'))) {
shellActivation.set(ShellConstants.CSH, [
{ executable: 'source', args: [path.join(binDir, `activate.csh`)] },
]);
shellDeactivation.set(ShellConstants.CSH, [{ executable: 'deactivate' }]);

shellActivation.set(ShellConstants.FISH, [
{ executable: 'source', args: [path.join(binDir, `activate.csh`)] },
]);
shellDeactivation.set(ShellConstants.FISH, [{ executable: 'deactivate' }]);
}

if (await fs.pathExists(path.join(binDir, 'activate.fish'))) {
shellActivation.set(ShellConstants.FISH, [
{ executable: 'source', args: [path.join(binDir, `activate.fish`)] },
]);
shellDeactivation.set(ShellConstants.FISH, [{ executable: 'deactivate' }]);
}

if (await fs.pathExists(path.join(binDir, 'activate.xsh'))) {
shellActivation.set(ShellConstants.XONSH, [
{ executable: 'source', args: [path.join(binDir, `activate.xsh`)] },
]);
shellDeactivation.set(ShellConstants.XONSH, [{ executable: 'deactivate' }]);
}

if (await fs.pathExists(path.join(binDir, 'activate.nu'))) {
shellActivation.set(ShellConstants.NU, [
{ executable: 'overlay', args: ['use', path.join(binDir, 'activate.nu')] },
]);
shellDeactivation.set(ShellConstants.NU, [{ executable: 'overlay', args: ['hide', 'activate'] }]);
}

return {
name: name,
displayName: name,
shortDisplayName: `${sv} (${venvName})`,
displayPath: env.executable,
version: env.version,
description: undefined,
tooltip: env.executable,
environmentPath: Uri.file(env.executable),
iconPath: new ThemeIcon('python'),
sysPrefix: env.prefix,
execInfo: {
run: {
executable: env.executable,
},
activatedRun: {
executable: env.executable,
},
shellActivation,
shellDeactivation,
},
};
} else {
throw new Error(`Invalid python info: ${JSON.stringify(env)}`);
}
}
18 changes: 7 additions & 11 deletions src/managers/poetry/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { NativePythonFinder } from '../common/nativePythonFinder';
import { compareVersions } from '../common/utils';
import { PoetryManager } from './poetryManager';
import { PoetryPackageManager } from './poetryPackageManager';
import { getPoetry, getPoetryVersion, isPoetryShellPluginInstalled } from './poetryUtils';
import { getPoetry, getPoetryVersion } from './poetryUtils';

export async function registerPoetryFeatures(
nativeFinder: NativePythonFinder,
Expand All @@ -18,23 +18,19 @@ export async function registerPoetryFeatures(

try {
const poetryPath = await getPoetry(nativeFinder);
let shellSupported = true;
if (poetryPath) {
const version = await getPoetryVersion(poetryPath);
if (!version) {
showErrorMessage(l10n.t('Poetry version could not be determined.'));
return;
}
traceInfo(
'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.',
);
if (version && compareVersions(version, '2.0.0') >= 0) {
shellSupported = await isPoetryShellPluginInstalled(poetryPath);
if (!shellSupported) {
showErrorMessage(
l10n.t(
'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)',
),
);
return;
}
traceInfo('Note: The current version of Poetry is 2.0.0 or higher.');
} else {
traceInfo('Note: The current version of Poetry is lower than 2.0.0.');
}

const envManager = new PoetryManager(nativeFinder, api);
Expand Down
Loading