Skip to content

Commit b3f8667

Browse files
fix: scope uv run setting lookup
Agent-Logs-Url: https://github.com/microsoft/vscode-python-environments/sessions/5f628821-9c7b-48a6-9f5f-9fccb9a33bf6 Co-authored-by: eleanorjboyd <26030610+eleanorjboyd@users.noreply.github.com>
1 parent f517bd9 commit b3f8667

3 files changed

Lines changed: 51 additions & 4 deletions

File tree

src/features/execution/runAsTask.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export async function runAsTask(
3535

3636
const args = environment.execInfo?.activatedRun?.args ?? environment.execInfo?.run.args ?? [];
3737
const allArgs = [...args, ...options.args];
38-
const useUv = await shouldUseUv(undefined, environment.environmentPath.fsPath);
38+
const useUv = await shouldUseUv(undefined, environment.environmentPath.fsPath, options.project?.uri);
3939

4040
if (useUv) {
4141
allArgs.unshift('--python', executable);

src/managers/builtin/helpers.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { CancellationError, CancellationToken, LogOutputChannel } from 'vscode';
1+
import { CancellationError, CancellationToken, ConfigurationScope, LogOutputChannel } from 'vscode';
22
import { spawnProcess } from '../../common/childProcess.apis';
33
import { EventNames } from '../../common/telemetry/constants';
44
import { sendTelemetryEvent } from '../../common/telemetry/sender';
@@ -40,7 +40,7 @@ export async function isUvInstalled(log?: LogOutputChannel): Promise<boolean> {
4040
* @param envPath - Optional environment path to check against UV environments list
4141
* @returns True if uv should be used, false otherwise. For UV environments, returns true if uv is installed. For other environments, checks the 'python-envs.alwaysUseUv' setting and uv availability.
4242
*/
43-
export async function shouldUseUv(log?: LogOutputChannel, envPath?: string): Promise<boolean> {
43+
export async function shouldUseUv(log?: LogOutputChannel, envPath?: string, scope?: ConfigurationScope): Promise<boolean> {
4444
if (envPath) {
4545
// always use uv if the given environment is stored as a uv env
4646
const uvEnvs = await getUvEnvironments();
@@ -50,7 +50,7 @@ export async function shouldUseUv(log?: LogOutputChannel, envPath?: string): Pro
5050
}
5151

5252
// For other environments, check the user setting
53-
const config = getConfiguration('python-envs');
53+
const config = getConfiguration('python-envs', scope);
5454
const alwaysUseUv = config.get<boolean>('alwaysUseUv', true);
5555

5656
if (alwaysUseUv) {

src/test/features/execution/runAsTask.unit.test.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,48 @@ suite('runAsTask Tests', () => {
171171
);
172172
});
173173

174+
test('should quote uv executable when needed', async () => {
175+
const environment: PythonEnvironment = {
176+
envId: { id: 'test-env', managerId: 'test-manager' },
177+
name: 'Test Environment',
178+
displayName: 'Test Environment',
179+
displayPath: '/path/to/env',
180+
version: '3.9.0',
181+
environmentPath: Uri.file('/path/to/env'),
182+
execInfo: {
183+
run: {
184+
executable: '/path/to/python',
185+
args: [],
186+
},
187+
},
188+
sysPrefix: '/path/to/env',
189+
};
190+
191+
const options: PythonTaskExecutionOptions = {
192+
name: 'Quoted UV Task',
193+
args: ['script.py'],
194+
};
195+
196+
const mockTaskExecution = {} as TaskExecution;
197+
198+
mockGetWorkspaceFolder.returns(undefined);
199+
mockShouldUseUv.withArgs(undefined, environment.environmentPath.fsPath, undefined).resolves(true);
200+
mockQuoteStringIfNecessary.withArgs('uv').returns('"uv"');
201+
mockExecuteTask.resolves(mockTaskExecution);
202+
203+
await runAsTask(environment, options);
204+
205+
const taskArg = mockExecuteTask.firstCall.args[0] as Task;
206+
const execution = taskArg.execution as ShellExecution;
207+
208+
assert.strictEqual(execution.command, '"uv"', 'Should quote the uv executable when required');
209+
assert.deepStrictEqual(
210+
execution.args,
211+
['run', '--python', '/path/to/python', 'script.py'],
212+
'Should preserve uv arguments when quoting the executable',
213+
);
214+
});
215+
174216
test('should create and execute task with regular run configuration when no activatedRun', async () => {
175217
// Mock - Environment without activatedRun
176218
const environment: PythonEnvironment = {
@@ -197,6 +239,7 @@ suite('runAsTask Tests', () => {
197239
const mockTaskExecution = {} as TaskExecution;
198240

199241
mockGetWorkspaceFolder.withArgs(undefined).returns(undefined);
242+
mockShouldUseUv.withArgs(undefined, environment.environmentPath.fsPath, undefined).resolves(false);
200243
mockQuoteStringIfNecessary.withArgs('/path/to/python').returns('/path/to/python');
201244
mockExecuteTask.resolves(mockTaskExecution);
202245

@@ -214,6 +257,10 @@ suite('runAsTask Tests', () => {
214257
mockTraceInfo.calledWith(sinon.match(/Running as task: \/path\/to\/python --default-arg test\.py/)),
215258
'Should log execution with run args',
216259
);
260+
const taskArg = mockExecuteTask.firstCall.args[0] as Task;
261+
const execution = taskArg.execution as ShellExecution;
262+
assert.strictEqual(execution.command, '/path/to/python', 'Should keep the python executable when uv is off');
263+
assert.deepStrictEqual(execution.args, ['--default-arg', 'test.py'], 'Should keep the non-uv arguments');
217264
});
218265

219266
test('should handle custom reveal option', async () => {

0 commit comments

Comments
 (0)