From a08f1123debde1c01c28772e90e8c8bc4fa84a9c Mon Sep 17 00:00:00 2001 From: eleanorjboyd <26030610+eleanorjboyd@users.noreply.github.com> Date: Thu, 8 Jan 2026 08:57:32 -0800 Subject: [PATCH 1/2] Refactor: Replace direct child_process.spawn calls with spawnProcess for consistent environment handling and add PYTHONUTF8=1 for subprocesses --- src/common/childProcess.apis.ts | 7 ++++++- src/managers/common/nativePythonFinder.ts | 4 ++-- src/managers/conda/condaUtils.ts | 4 ++-- src/managers/poetry/poetryPackageManager.ts | 4 ++-- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/common/childProcess.apis.ts b/src/common/childProcess.apis.ts index 8ae2f26b..7e7d1f4e 100644 --- a/src/common/childProcess.apis.ts +++ b/src/common/childProcess.apis.ts @@ -24,5 +24,10 @@ export function spawnProcess( args: string[], options?: cp.SpawnOptions, ): cp.ChildProcess | cp.ChildProcessWithoutNullStreams { - return cp.spawn(command, args, options ?? {}); + // Set PYTHONUTF8=1; user-provided PYTHONUTF8 values take precedence. + const env = { + PYTHONUTF8: '1', + ...(options?.env ?? process.env), + }; + return cp.spawn(command, args, { ...options, env }); } diff --git a/src/managers/common/nativePythonFinder.ts b/src/managers/common/nativePythonFinder.ts index 2708d8fe..20fb4e56 100644 --- a/src/managers/common/nativePythonFinder.ts +++ b/src/managers/common/nativePythonFinder.ts @@ -1,10 +1,10 @@ -import * as ch from 'child_process'; import * as fs from 'fs-extra'; import * as path from 'path'; import { PassThrough } from 'stream'; import { Disposable, ExtensionContext, LogOutputChannel, Uri } from 'vscode'; import * as rpc from 'vscode-jsonrpc/node'; import { PythonProjectApi } from '../../api'; +import { spawnProcess } from '../../common/childProcess.apis'; import { ENVS_EXTENSION_ID, PYTHON_EXTENSION_ID } from '../../common/constants'; import { getExtension } from '../../common/extension.apis'; import { traceError, traceLog, traceVerbose, traceWarn } from '../../common/logging'; @@ -213,7 +213,7 @@ class NativePythonFinderImpl implements NativePythonFinder { const writable = new PassThrough(); const disposables: Disposable[] = []; try { - const proc = ch.spawn(this.toolPath, ['server'], { env: process.env }); + const proc = spawnProcess(this.toolPath, ['server'], { env: process.env }); proc.stdout.pipe(readable, { end: false }); proc.stderr.on('data', (data) => this.outputChannel.error(`[pet] ${data.toString()}`)); writable.pipe(proc.stdin, { end: false }); diff --git a/src/managers/conda/condaUtils.ts b/src/managers/conda/condaUtils.ts index 04d7ccbb..3b1f45ea 100644 --- a/src/managers/conda/condaUtils.ts +++ b/src/managers/conda/condaUtils.ts @@ -1,4 +1,3 @@ -import * as ch from 'child_process'; import * as fse from 'fs-extra'; import * as os from 'os'; import * as path from 'path'; @@ -25,6 +24,7 @@ import { PythonEnvironmentInfo, PythonProject, } from '../../api'; +import { spawnProcess } from '../../common/childProcess.apis'; import { ENVS_EXTENSION_ID, EXTENSION_ROOT_DIR } from '../../common/constants'; import { showErrorMessageWithLogs } from '../../common/errors/utils'; import { Common, CondaStrings, PackageManagement, Pickers } from '../../common/localize'; @@ -206,7 +206,7 @@ async function _runConda( const quotedConda = quoteStringIfNecessary(conda); const timer = new StopWatch(); deferred.promise.finally(() => traceInfo(`Ran conda in ${timer.elapsedTime}: ${quotedConda} ${args.join(' ')}`)); - const proc = ch.spawn(quotedConda, args, { shell: true }); + const proc = spawnProcess(quotedConda, args, { shell: true }); token?.onCancellationRequested(() => { proc.kill(); diff --git a/src/managers/poetry/poetryPackageManager.ts b/src/managers/poetry/poetryPackageManager.ts index d3379e9d..0134cb3e 100644 --- a/src/managers/poetry/poetryPackageManager.ts +++ b/src/managers/poetry/poetryPackageManager.ts @@ -1,4 +1,3 @@ -import * as ch from 'child_process'; import * as fsapi from 'fs-extra'; import * as path from 'path'; import { @@ -22,6 +21,7 @@ import { PythonEnvironment, PythonEnvironmentApi, } from '../../api'; +import { spawnProcess } from '../../common/childProcess.apis'; import { showErrorMessage, showInputBox, withProgress } from '../../common/window.apis'; import { PoetryManager } from './poetryManager'; import { getPoetry } from './poetryUtils'; @@ -271,7 +271,7 @@ export async function runPoetry( log?.info(`Running: ${poetry} ${args.join(' ')}`); return new Promise((resolve, reject) => { - const proc = ch.spawn(poetry, args, { cwd }); + const proc = spawnProcess(poetry, args, { cwd }); token?.onCancellationRequested(() => { proc.kill(); reject(new CancellationError()); From cae9d2ebbee2e1bbf21b1f5c2f515f5c25e62e8a Mon Sep 17 00:00:00 2001 From: eleanorjboyd <26030610+eleanorjboyd@users.noreply.github.com> Date: Thu, 8 Jan 2026 09:00:57 -0800 Subject: [PATCH 2/2] Fix: Update spawnProcess call to use 'pipe' for stdio to improve output handling --- src/managers/common/nativePythonFinder.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/managers/common/nativePythonFinder.ts b/src/managers/common/nativePythonFinder.ts index 20fb4e56..f544de70 100644 --- a/src/managers/common/nativePythonFinder.ts +++ b/src/managers/common/nativePythonFinder.ts @@ -213,7 +213,7 @@ class NativePythonFinderImpl implements NativePythonFinder { const writable = new PassThrough(); const disposables: Disposable[] = []; try { - const proc = spawnProcess(this.toolPath, ['server'], { env: process.env }); + const proc = spawnProcess(this.toolPath, ['server'], { env: process.env, stdio: 'pipe' }); proc.stdout.pipe(readable, { end: false }); proc.stderr.on('data', (data) => this.outputChannel.error(`[pet] ${data.toString()}`)); writable.pipe(proc.stdin, { end: false });