diff --git a/news/1 Enhancements/18479.md b/news/1 Enhancements/18479.md new file mode 100644 index 000000000000..c28a2f051112 --- /dev/null +++ b/news/1 Enhancements/18479.md @@ -0,0 +1 @@ +Use `conda run` for conda environments for running python files and installing modules. diff --git a/src/client/common/process/pythonEnvironment.ts b/src/client/common/process/pythonEnvironment.ts index e162eb6da5a1..dfa74cbff922 100644 --- a/src/client/common/process/pythonEnvironment.ts +++ b/src/client/common/process/pythonEnvironment.ts @@ -149,9 +149,13 @@ export async function createCondaEnv( // These are used to generate the deps. procs: IProcessService, fs: IFileSystem, + executeAsAProcess?: boolean, ): Promise { const conda = await Conda.getConda(); - const pythonArgv = await conda?.getRunPythonArgs({ name: condaInfo.name, prefix: condaInfo.path }); + const pythonArgv = await conda?.getRunPythonArgs( + { name: condaInfo.name, prefix: condaInfo.path }, + executeAsAProcess, + ); if (!pythonArgv) { return undefined; } diff --git a/src/client/common/process/pythonExecutionFactory.ts b/src/client/common/process/pythonExecutionFactory.ts index 3bd167da0926..e5c8ea6aabcc 100644 --- a/src/client/common/process/pythonExecutionFactory.ts +++ b/src/client/common/process/pythonExecutionFactory.ts @@ -77,7 +77,11 @@ export class PythonExecutionFactory implements IPythonExecutionFactory { } const processService: IProcessService = await this.processServiceFactory.create(options.resource); - const condaExecutionService = await this.createCondaExecutionService(pythonPath, processService); + const condaExecutionService = await this.createCondaExecutionService( + pythonPath, + processService, + options.executeAsAProcess, + ); if (condaExecutionService) { return condaExecutionService; } @@ -125,13 +129,20 @@ export class PythonExecutionFactory implements IPythonExecutionFactory { public async createCondaExecutionService( pythonPath: string, processService: IProcessService, + executeAsAProcess?: boolean, ): Promise { const condaLocatorService = this.serviceContainer.get(IComponentAdapter); const [condaEnvironment] = await Promise.all([condaLocatorService.getCondaEnvironment(pythonPath)]); if (!condaEnvironment) { return undefined; } - const env = await createCondaEnv(condaEnvironment, pythonPath, processService, this.fileSystem); + const env = await createCondaEnv( + condaEnvironment, + pythonPath, + processService, + this.fileSystem, + executeAsAProcess, + ); if (!env) { return undefined; } diff --git a/src/client/common/process/types.ts b/src/client/common/process/types.ts index a5884ddd75fa..226341f4ae78 100644 --- a/src/client/common/process/types.ts +++ b/src/client/common/process/types.ts @@ -65,6 +65,10 @@ export const IPythonExecutionFactory = Symbol('IPythonExecutionFactory'); export type ExecutionFactoryCreationOptions = { resource?: Uri; pythonPath?: string; + /** + * Whether to execute using `NodeJS.child_process` API, considered `true` as default. + */ + executeAsAProcess?: boolean; }; export type ExecutionFactoryCreateWithEnvironmentOptions = { resource?: Uri; diff --git a/src/client/pythonEnvironments/common/environmentManagers/conda.ts b/src/client/pythonEnvironments/common/environmentManagers/conda.ts index 332dee3f626f..abc440fc524d 100644 --- a/src/client/pythonEnvironments/common/environmentManagers/conda.ts +++ b/src/client/pythonEnvironments/common/environmentManagers/conda.ts @@ -405,7 +405,7 @@ export class Conda { return envList.find((e) => isParentPath(executable, e.prefix)); } - public async getRunPythonArgs(env: CondaEnvInfo): Promise { + public async getRunPythonArgs(env: CondaEnvInfo, executeAsAProcess = true): Promise { const condaVersion = await this.getCondaVersion(); if (condaVersion && lt(condaVersion, CONDA_RUN_VERSION)) { return undefined; @@ -416,7 +416,11 @@ export class Conda { } else { args.push('-p', env.prefix); } - return [this.command, 'run', ...args, '--no-capture-output', '--live-stream', 'python', OUTPUT_MARKER_SCRIPT]; + const pythonArgs = [this.command, 'run', ...args, '--no-capture-output', '--live-stream', 'python']; + if (executeAsAProcess) { + pythonArgs.push(OUTPUT_MARKER_SCRIPT); + } + return pythonArgs; } /** diff --git a/src/client/terminals/codeExecution/djangoShellCodeExecution.ts b/src/client/terminals/codeExecution/djangoShellCodeExecution.ts index 61f4cef7758f..649848daf426 100644 --- a/src/client/terminals/codeExecution/djangoShellCodeExecution.ts +++ b/src/client/terminals/codeExecution/djangoShellCodeExecution.ts @@ -9,6 +9,7 @@ import { Disposable, Uri } from 'vscode'; import { ICommandManager, IDocumentManager, IWorkspaceService } from '../../common/application/types'; import '../../common/extensions'; import { IFileSystem, IPlatformService } from '../../common/platform/types'; +import { IPythonExecutionFactory } from '../../common/process/types'; import { ITerminalServiceFactory } from '../../common/terminal/types'; import { IConfigurationService, IDisposableRegistry } from '../../common/types'; import { copyPythonExecInfo, PythonExecInfo } from '../../pythonEnvironments/exec'; @@ -26,8 +27,16 @@ export class DjangoShellCodeExecutionProvider extends TerminalCodeExecutionProvi @inject(ICommandManager) commandManager: ICommandManager, @inject(IFileSystem) fileSystem: IFileSystem, @inject(IDisposableRegistry) disposableRegistry: Disposable[], + @inject(IPythonExecutionFactory) pythonExecutionFactory: IPythonExecutionFactory, ) { - super(terminalServiceFactory, configurationService, workspace, disposableRegistry, platformService); + super( + terminalServiceFactory, + configurationService, + workspace, + disposableRegistry, + platformService, + pythonExecutionFactory, + ); this.terminalTitle = 'Django Shell'; disposableRegistry.push(new DjangoContextInitializer(documentManager, workspace, fileSystem, commandManager)); } diff --git a/src/client/terminals/codeExecution/repl.ts b/src/client/terminals/codeExecution/repl.ts index fe5f5860bb28..69226c23dedf 100644 --- a/src/client/terminals/codeExecution/repl.ts +++ b/src/client/terminals/codeExecution/repl.ts @@ -7,6 +7,7 @@ import { inject, injectable } from 'inversify'; import { Disposable } from 'vscode'; import { IWorkspaceService } from '../../common/application/types'; import { IPlatformService } from '../../common/platform/types'; +import { IPythonExecutionFactory } from '../../common/process/types'; import { ITerminalServiceFactory } from '../../common/terminal/types'; import { IConfigurationService, IDisposableRegistry } from '../../common/types'; import { TerminalCodeExecutionProvider } from './terminalCodeExecution'; @@ -19,8 +20,16 @@ export class ReplProvider extends TerminalCodeExecutionProvider { @inject(IWorkspaceService) workspace: IWorkspaceService, @inject(IDisposableRegistry) disposableRegistry: Disposable[], @inject(IPlatformService) platformService: IPlatformService, + @inject(IPythonExecutionFactory) pythonExecutionFactory: IPythonExecutionFactory, ) { - super(terminalServiceFactory, configurationService, workspace, disposableRegistry, platformService); + super( + terminalServiceFactory, + configurationService, + workspace, + disposableRegistry, + platformService, + pythonExecutionFactory, + ); this.terminalTitle = 'REPL'; } } diff --git a/src/client/terminals/codeExecution/terminalCodeExecution.ts b/src/client/terminals/codeExecution/terminalCodeExecution.ts index 88eff107f791..a1d9e01812ff 100644 --- a/src/client/terminals/codeExecution/terminalCodeExecution.ts +++ b/src/client/terminals/codeExecution/terminalCodeExecution.ts @@ -9,6 +9,7 @@ import { Disposable, Uri } from 'vscode'; import { IWorkspaceService } from '../../common/application/types'; import '../../common/extensions'; import { IPlatformService } from '../../common/platform/types'; +import { IPythonExecutionFactory } from '../../common/process/types'; import { ITerminalService, ITerminalServiceFactory } from '../../common/terminal/types'; import { IConfigurationService, IDisposableRegistry } from '../../common/types'; import { buildPythonExecInfo, PythonExecInfo } from '../../pythonEnvironments/exec'; @@ -26,6 +27,7 @@ export class TerminalCodeExecutionProvider implements ICodeExecutionService { @inject(IWorkspaceService) protected readonly workspace: IWorkspaceService, @inject(IDisposableRegistry) protected readonly disposables: Disposable[], @inject(IPlatformService) protected readonly platformService: IPlatformService, + @inject(IPythonExecutionFactory) private readonly pythonExecutionFactory: IPythonExecutionFactory, ) {} public async executeFile(file: Uri) { @@ -61,11 +63,12 @@ export class TerminalCodeExecutionProvider implements ICodeExecutionService { public async getExecutableInfo(resource?: Uri, args: string[] = []): Promise { const pythonSettings = this.configurationService.getSettings(resource); - const command = this.platformService.isWindows - ? pythonSettings.pythonPath.replace(/\\/g, '/') - : pythonSettings.pythonPath; + const executionFactory = await this.pythonExecutionFactory.create({ resource, executeAsAProcess: false }); + const execInfo = executionFactory.getExecutionInfo(); + const pythonArgs = execInfo.args; + const command = this.platformService.isWindows ? execInfo.command.replace(/\\/g, '/') : execInfo.command; const launchArgs = pythonSettings.terminal.launchArgs; - return buildPythonExecInfo(command, [...launchArgs, ...args]); + return buildPythonExecInfo(command, [...pythonArgs, ...launchArgs, ...args]); } // Overridden in subclasses, see djangoShellCodeExecution.ts diff --git a/src/test/terminals/codeExecution/djangoShellCodeExect.unit.test.ts b/src/test/terminals/codeExecution/djangoShellCodeExect.unit.test.ts index c0f5e50e0e1f..c6a106db03ea 100644 --- a/src/test/terminals/codeExecution/djangoShellCodeExect.unit.test.ts +++ b/src/test/terminals/codeExecution/djangoShellCodeExect.unit.test.ts @@ -57,6 +57,7 @@ suite('Terminal - Django Shell Code Execution', () => { commandManager.object, fileSystem.object, disposables, + pythonExecutionFactory.object, ); terminalFactory.setup((f) => f.getTerminalService(TypeMoq.It.isAny())).returns(() => terminalService.object); diff --git a/src/test/terminals/codeExecution/terminalCodeExec.unit.test.ts b/src/test/terminals/codeExecution/terminalCodeExec.unit.test.ts index 4a02c75efc81..a9d2fa35c0ae 100644 --- a/src/test/terminals/codeExecution/terminalCodeExec.unit.test.ts +++ b/src/test/terminals/codeExecution/terminalCodeExec.unit.test.ts @@ -59,6 +59,7 @@ suite('Terminal - Code Execution', () => { terminalFactory = TypeMoq.Mock.ofType(); terminalSettings = TypeMoq.Mock.ofType(); terminalService = TypeMoq.Mock.ofType(); + pythonExecutionFactory = TypeMoq.Mock.ofType(); const configService = TypeMoq.Mock.ofType(); workspace = TypeMoq.Mock.ofType(); platform = TypeMoq.Mock.ofType(); @@ -66,7 +67,6 @@ suite('Terminal - Code Execution', () => { documentManager = TypeMoq.Mock.ofType(); commandManager = TypeMoq.Mock.ofType(); fileSystem = TypeMoq.Mock.ofType(); - pythonExecutionFactory = TypeMoq.Mock.ofType(); settings = TypeMoq.Mock.ofType(); settings.setup((s) => s.terminal).returns(() => terminalSettings.object); configService.setup((c) => c.getSettings(TypeMoq.It.isAny())).returns(() => settings.object); @@ -79,6 +79,7 @@ suite('Terminal - Code Execution', () => { workspace.object, disposables, platform.object, + pythonExecutionFactory.object, ); break; } @@ -89,6 +90,7 @@ suite('Terminal - Code Execution', () => { workspace.object, disposables, platform.object, + pythonExecutionFactory.object, ); expectedTerminalTitle = 'REPL'; break; @@ -111,6 +113,7 @@ suite('Terminal - Code Execution', () => { commandManager.object, fileSystem.object, disposables, + pythonExecutionFactory.object, ); expectedTerminalTitle = 'Django Shell'; break;