diff --git a/package-lock.json b/package-lock.json index c931516cb2b4..f4d02328f724 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "python", - "version": "2022.16.0", + "version": "2022.16.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "python", - "version": "2022.16.0", + "version": "2022.16.1", "license": "MIT", "dependencies": { "@vscode/extension-telemetry": "^0.6.2", diff --git a/package.json b/package.json index 17819a797727..d8154288522f 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "python", "displayName": "Python", "description": "IntelliSense (Pylance), Linting, Debugging (multi-threaded, remote), Jupyter Notebooks, code formatting, refactoring, unit tests, and more.", - "version": "2022.16.0", + "version": "2022.16.1", "featureFlags": { "usingNewInterpreterStorage": true }, diff --git a/src/client/interpreter/activation/service.ts b/src/client/interpreter/activation/service.ts index baa05656853a..24007581daf5 100644 --- a/src/client/interpreter/activation/service.ts +++ b/src/client/interpreter/activation/service.ts @@ -16,7 +16,7 @@ import { sleep } from '../../common/utils/async'; import { InMemoryCache } from '../../common/utils/cacheUtils'; import { OSType } from '../../common/utils/platform'; import { IEnvironmentVariablesProvider } from '../../common/variables/types'; -import { PythonEnvironment } from '../../pythonEnvironments/info'; +import { EnvironmentType, PythonEnvironment } from '../../pythonEnvironments/info'; import { captureTelemetry, sendTelemetryEvent } from '../../telemetry'; import { EventName } from '../../telemetry/constants'; import { IInterpreterService } from '../contracts'; @@ -30,6 +30,7 @@ import { traceVerbose, traceWarn, } from '../../logging'; +import { Conda } from '../../pythonEnvironments/common/environmentManagers/conda'; const ENVIRONMENT_PREFIX = 'e8b39361-0157-4923-80e1-22d70d46dee6'; const CACHE_DURATION = 10 * 60 * 1000; @@ -169,20 +170,41 @@ export class EnvironmentActivationService implements IEnvironmentActivationServi if (!shellInfo) { return; } - let isPossiblyCondaEnv = false; try { - const activationCommands = await this.helper.getEnvironmentActivationShellCommands( - resource, - shellInfo.shellType, - interpreter, - ); - traceVerbose(`Activation Commands received ${activationCommands} for shell ${shellInfo.shell}`); - if (!activationCommands || !Array.isArray(activationCommands) || activationCommands.length === 0) { - return; + let command: string | undefined; + let [args, parse] = internalScripts.printEnvVariables(); + args.forEach((arg, i) => { + args[i] = arg.toCommandArgumentForPythonExt(); + }); + interpreter = interpreter ?? (await this.interpreterService.getActiveInterpreter(resource)); + if (interpreter?.envType === EnvironmentType.Conda) { + const conda = await Conda.getConda(); + const pythonArgv = await conda?.getRunPythonArgs({ + name: interpreter.envName, + prefix: interpreter.envPath ?? '', + }); + if (pythonArgv) { + // Using environment prefix isn't needed as the marker script already takes care of it. + command = [...pythonArgv, ...args].map((arg) => arg.toCommandArgumentForPythonExt()).join(' '); + } + } + if (!command) { + const activationCommands = await this.helper.getEnvironmentActivationShellCommands( + resource, + shellInfo.shellType, + interpreter, + ); + traceVerbose(`Activation Commands received ${activationCommands} for shell ${shellInfo.shell}`); + if (!activationCommands || !Array.isArray(activationCommands) || activationCommands.length === 0) { + return; + } + // Run the activate command collect the environment from it. + const activationCommand = this.fixActivationCommands(activationCommands).join(' && '); + // In order to make sure we know where the environment output is, + // put in a dummy echo we can look for + command = `${activationCommand} && echo '${ENVIRONMENT_PREFIX}' && python ${args.join(' ')}`; } - isPossiblyCondaEnv = activationCommands.join(' ').toLowerCase().includes('conda'); - // Run the activate command collect the environment from it. - const activationCommand = this.fixActivationCommands(activationCommands).join(' && '); + const processService = await this.processServiceFactory.create(resource); const customEnvVars = await this.envVarsService.getEnvironmentVariables(resource); const hasCustomEnvVars = Object.keys(customEnvVars).length; @@ -194,14 +216,6 @@ export class EnvironmentActivationService implements IEnvironmentActivationServi env[PYTHON_WARNINGS] = 'ignore'; traceVerbose(`${hasCustomEnvVars ? 'Has' : 'No'} Custom Env Vars`); - - // In order to make sure we know where the environment output is, - // put in a dummy echo we can look for - const [args, parse] = internalScripts.printEnvVariables(); - args.forEach((arg, i) => { - args[i] = arg.toCommandArgumentForPythonExt(); - }); - const command = `${activationCommand} && echo '${ENVIRONMENT_PREFIX}' && python ${args.join(' ')}`; traceVerbose(`Activating Environment to capture Environment variables, ${command}`); // Do some wrapping of the call. For two reasons: @@ -219,7 +233,10 @@ export class EnvironmentActivationService implements IEnvironmentActivationServi result = await processService.shellExec(command, { env, shell: shellInfo.shell, - timeout: isPossiblyCondaEnv ? CONDA_ENVIRONMENT_TIMEOUT : ENVIRONMENT_TIMEOUT, + timeout: + interpreter?.envType === EnvironmentType.Conda + ? CONDA_ENVIRONMENT_TIMEOUT + : ENVIRONMENT_TIMEOUT, maxBuffer: 1000 * 1000, throwOnStdErr: false, }); @@ -265,7 +282,7 @@ export class EnvironmentActivationService implements IEnvironmentActivationServi } catch (e) { traceError('getActivatedEnvironmentVariables', e); sendTelemetryEvent(EventName.ACTIVATE_ENV_TO_GET_ENV_VARS_FAILED, undefined, { - isPossiblyCondaEnv, + isPossiblyCondaEnv: interpreter?.envType === EnvironmentType.Conda, terminal: shellInfo.shellType, }); @@ -283,6 +300,9 @@ export class EnvironmentActivationService implements IEnvironmentActivationServi @traceDecoratorError('Failed to parse Environment variables') @traceDecoratorVerbose('parseEnvironmentOutput', TraceOptions.None) protected parseEnvironmentOutput(output: string, parse: (out: string) => NodeJS.ProcessEnv | undefined) { + if (output.indexOf(ENVIRONMENT_PREFIX) === -1) { + return parse(output); + } output = output.substring(output.indexOf(ENVIRONMENT_PREFIX) + ENVIRONMENT_PREFIX.length); const js = output.substring(output.indexOf('{')).trim(); return parse(js); diff --git a/src/test/interpreters/activation/service.unit.test.ts b/src/test/interpreters/activation/service.unit.test.ts index 9e705f247ac9..d50b2b5d5995 100644 --- a/src/test/interpreters/activation/service.unit.test.ts +++ b/src/test/interpreters/activation/service.unit.test.ts @@ -58,7 +58,7 @@ suite('Interpreters Activation - Python Environment Variables', () => { architecture: Architecture.x64, }; - function initSetup() { + function initSetup(interpreter: PythonEnvironment | undefined) { helper = mock(TerminalHelper); platform = mock(PlatformService); processServiceFactory = mock(ProcessServiceFactory); @@ -71,6 +71,7 @@ suite('Interpreters Activation - Python Environment Variables', () => { onDidChangeInterpreter = new EventEmitter(); when(envVarsService.onDidEnvironmentVariablesChange).thenReturn(onDidChangeEnvVariables.event); when(interpreterService.onDidChangeInterpreter).thenReturn(onDidChangeInterpreter.event); + when(interpreterService.getActiveInterpreter(anything())).thenResolve(interpreter); service = new EnvironmentActivationService( instance(helper), instance(platform), @@ -89,7 +90,7 @@ suite('Interpreters Activation - Python Environment Variables', () => { [undefined, Uri.parse('a')].forEach((resource) => [undefined, pythonInterpreter].forEach((interpreter) => { suite(title(resource, interpreter), () => { - setup(initSetup); + setup(() => initSetup(interpreter)); test('Unknown os will return empty variables', async () => { when(platform.osType).thenReturn(OSType.Unknown); const env = await service.getActivatedEnvironmentVariables(resource); @@ -102,7 +103,7 @@ suite('Interpreters Activation - Python Environment Variables', () => { osTypes.forEach((osType) => { suite(osType.name, () => { - setup(initSetup); + setup(() => initSetup(interpreter)); test('getEnvironmentActivationShellCommands will be invoked', async () => { when(platform.osType).thenReturn(osType.value); when(