Skip to content

Commit a425f4c

Browse files
authored
Merge branch 'main' into dependabot/pip/typing-extensions-4.16.0
2 parents 6fa911e + a8c5333 commit a425f4c

11 files changed

Lines changed: 558 additions & 39 deletions

File tree

.github/workflows/build.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ jobs:
130130
runs-on: ubuntu-latest
131131
steps:
132132
- name: Use Python ${{ env.PYTHON_VERSION }}
133-
uses: actions/setup-python@v6
133+
uses: actions/setup-python@v7
134134
with:
135135
python-version: ${{ env.PYTHON_VERSION }}
136136

@@ -184,7 +184,7 @@ jobs:
184184
persist-credentials: false
185185

186186
- name: Use Python ${{ matrix.python }}
187-
uses: actions/setup-python@v6
187+
uses: actions/setup-python@v7
188188
with:
189189
python-version: ${{ matrix.python }}
190190

@@ -255,7 +255,7 @@ jobs:
255255
run: npx @vscode/l10n-dev@latest export ./src
256256

257257
- name: Install Python ${{ matrix.python }}
258-
uses: actions/setup-python@v6
258+
uses: actions/setup-python@v7
259259
with:
260260
python-version: ${{ matrix.python }}
261261

.github/workflows/gen-issue-velocity.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ jobs:
1919
persist-credentials: false
2020

2121
- name: Set up Python
22-
uses: actions/setup-python@v6
22+
uses: actions/setup-python@v7
2323
with:
2424
python-version: '3.x'
2525

.github/workflows/pr-check.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ jobs:
101101
runs-on: ubuntu-latest
102102
steps:
103103
- name: Use Python ${{ env.PYTHON_VERSION }}
104-
uses: actions/setup-python@v6
104+
uses: actions/setup-python@v7
105105
with:
106106
python-version: ${{ env.PYTHON_VERSION }}
107107

@@ -168,7 +168,7 @@ jobs:
168168
persist-credentials: false
169169

170170
- name: Use Python ${{ matrix.python }}
171-
uses: actions/setup-python@v6
171+
uses: actions/setup-python@v7
172172
with:
173173
python-version: ${{ matrix.python }}
174174

@@ -248,7 +248,7 @@ jobs:
248248
run: npx @vscode/l10n-dev@latest export ./src
249249

250250
- name: Use Python ${{ matrix.python }}
251-
uses: actions/setup-python@v6
251+
uses: actions/setup-python@v7
252252
with:
253253
python-version: ${{ matrix.python }}
254254

@@ -521,7 +521,7 @@ jobs:
521521
run: npx @vscode/l10n-dev@latest export ./src
522522

523523
- name: Use Python ${{ env.PYTHON_VERSION }}
524-
uses: actions/setup-python@v6
524+
uses: actions/setup-python@v7
525525
with:
526526
python-version: ${{ env.PYTHON_VERSION }}
527527
cache: 'pip'

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1593,7 +1593,7 @@
15931593
{
15941594
"name": "configure_python_environment",
15951595
"displayName": "Configure Python Environment",
1596-
"modelDescription": "This tool configures a Python environment in the given workspace. ALWAYS Use this tool to set up the user's chosen environment and ALWAYS call this tool before using any other Python related tools or running any Python command in the terminal. IMPORTANT: This tool is only for Python environments (venv, virtualenv, conda, pipenv, poetry, pyenv, pixi, or any other Python environment manager). Do not use this tool for npm packages, system packages, Ruby gems, or any other non-Python dependencies.",
1596+
"modelDescription": "This tool configures a Python environment in the given workspace. ALWAYS Use this tool to set up the user's chosen environment and ALWAYS call this tool before using any other Python related tools or running any Python command in the terminal. If you already know which Python interpreter to use (e.g. from a previous tool call or user message), pass it as 'pythonPath' to skip interactive prompts and configure the environment automatically. IMPORTANT: This tool is only for Python environments (venv, virtualenv, conda, pipenv, poetry, pyenv, pixi, or any other Python environment manager). Do not use this tool for npm packages, system packages, Ruby gems, or any other non-Python dependencies.",
15971597
"userDescription": "%python.languageModelTools.configure_python_environment.userDescription%",
15981598
"toolReferenceName": "configurePythonEnvironment",
15991599
"tags": [
@@ -1609,6 +1609,10 @@
16091609
"resourcePath": {
16101610
"type": "string",
16111611
"description": "The path to the Python file or workspace for which a Python Environment needs to be configured."
1612+
},
1613+
"pythonPath": {
1614+
"type": "string",
1615+
"description": "Optional absolute path to a Python interpreter to use. When provided, the environment is configured automatically without any interactive prompts. Use this to avoid blocking the session on user input."
16121616
}
16131617
},
16141618
"required": []

src/client/chat/configurePythonEnvTool.ts

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,28 @@ import {
2323
IResourceReference,
2424
isCancellationError,
2525
raceCancellationError,
26+
setEnvironmentDirectlyByPath,
2627
} from './utils';
2728
import { ITerminalHelper } from '../common/terminal/types';
2829
import { IRecommendedEnvironmentService } from '../interpreter/configuration/types';
2930
import { CreateVirtualEnvTool } from './createVirtualEnvTool';
3031
import { ISelectPythonEnvToolArguments, SelectPythonEnvTool } from './selectEnvTool';
3132
import { BaseTool } from './baseTool';
33+
import { traceVerbose } from '../logging';
34+
import { ErrorWithTelemetrySafeReason } from '../common/errors/errorUtils';
3235

33-
export class ConfigurePythonEnvTool extends BaseTool<IResourceReference>
34-
implements LanguageModelTool<IResourceReference> {
36+
export interface IConfigurePythonEnvToolArguments extends IResourceReference {
37+
/**
38+
* Optional path to a Python interpreter. When provided, the tool sets this
39+
* interpreter directly without any user interaction (no Quick Pick, no
40+
* create-venv prompt). This is the recommended way for Copilot to call
41+
* the tool in autopilot / bypass-approvals mode.
42+
*/
43+
pythonPath?: string;
44+
}
45+
46+
export class ConfigurePythonEnvTool extends BaseTool<IConfigurePythonEnvToolArguments>
47+
implements LanguageModelTool<IConfigurePythonEnvToolArguments> {
3548
private readonly terminalExecutionService: TerminalCodeExecutionProvider;
3649
private readonly terminalHelper: ITerminalHelper;
3750
private readonly recommendedEnvService: IRecommendedEnvironmentService;
@@ -53,7 +66,7 @@ export class ConfigurePythonEnvTool extends BaseTool<IResourceReference>
5366
}
5467

5568
async invokeImpl(
56-
options: LanguageModelToolInvocationOptions<IResourceReference>,
69+
options: LanguageModelToolInvocationOptions<IConfigurePythonEnvToolArguments>,
5770
resource: Uri | undefined,
5871
token: CancellationToken,
5972
): Promise<LanguageModelToolResult> {
@@ -63,6 +76,11 @@ export class ConfigurePythonEnvTool extends BaseTool<IResourceReference>
6376
return notebookResponse;
6477
}
6578

79+
// Fast path: if the caller provided a pythonPath, set it directly without any UI.
80+
if (options.input.pythonPath) {
81+
return this.setEnvironmentDirectly(options.input.pythonPath, resource, token);
82+
}
83+
6684
const workspaceSpecificEnv = await raceCancellationError(
6785
this.hasAlreadyGotAWorkspaceSpecificEnvironment(resource),
6886
token,
@@ -107,8 +125,37 @@ export class ConfigurePythonEnvTool extends BaseTool<IResourceReference>
107125
}
108126
}
109127

128+
/**
129+
* Sets the given interpreter path directly without user interaction, then
130+
* resolves and returns the environment details.
131+
*/
132+
private async setEnvironmentDirectly(
133+
pythonPath: string,
134+
resource: Uri | undefined,
135+
token: CancellationToken,
136+
): Promise<LanguageModelToolResult> {
137+
traceVerbose(`${ConfigurePythonEnvTool.toolName}: setting environment directly from pythonPath: ${pythonPath}`);
138+
const result = await setEnvironmentDirectlyByPath(pythonPath, this.api, resource, token);
139+
if (result) {
140+
this.extraTelemetryProperties.resolveOutcome = 'providedEnv';
141+
this.extraTelemetryProperties.envType = getEnvTypeForTelemetry(result);
142+
return getEnvDetailsForResponse(
143+
result,
144+
this.api,
145+
this.terminalExecutionService,
146+
this.terminalHelper,
147+
resource,
148+
token,
149+
);
150+
}
151+
throw new ErrorWithTelemetrySafeReason(
152+
`No environment found for the provided pythonPath '${pythonPath}'.`,
153+
'noEnvFound',
154+
);
155+
}
156+
110157
async prepareInvocationImpl(
111-
_options: LanguageModelToolInvocationPrepareOptions<IResourceReference>,
158+
_options: LanguageModelToolInvocationPrepareOptions<IConfigurePythonEnvToolArguments>,
112159
_resource: Uri | undefined,
113160
_token: CancellationToken,
114161
): Promise<PreparedToolInvocation> {

src/client/chat/createVirtualEnvTool.ts

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ import { hideEnvCreation } from '../pythonEnvironments/creation/provider/hideEnv
4545
import { BaseTool } from './baseTool';
4646

4747
interface ICreateVirtualEnvToolParams extends IResourceReference {
48-
packageList?: string[]; // Added only becausewe have ability to create a virtual env with list of packages same tool within the in Python Env extension.
48+
packageList?: string[]; // Added only because we have the ability to create a virtual env with a list of packages using the same tool within the Python Env extension.
4949
}
5050

5151
export class CreateVirtualEnvTool extends BaseTool<ICreateVirtualEnvToolParams>
@@ -92,12 +92,17 @@ export class CreateVirtualEnvTool extends BaseTool<ICreateVirtualEnvToolParams>
9292

9393
let createdEnvPath: string | undefined = undefined;
9494
if (useEnvExtension()) {
95-
const result: PythonEnvironment | undefined = await commands.executeCommand('python-envs.createAny', {
96-
quickCreate: true,
97-
additionalPackages: options.input.packageList || [],
98-
uri: workspaceFolder.uri,
99-
selectEnvironment: true,
100-
});
95+
const result: PythonEnvironment | undefined = await raceCancellationError(
96+
Promise.resolve(
97+
commands.executeCommand<PythonEnvironment | undefined>('python-envs.createAny', {
98+
quickCreate: true,
99+
additionalPackages: options.input.packageList || [],
100+
uri: workspaceFolder.uri,
101+
selectEnvironment: true,
102+
}),
103+
),
104+
token,
105+
);
101106
createdEnvPath = result?.environmentPath.fsPath;
102107
} else {
103108
const created = await raceCancellationError(
@@ -116,19 +121,19 @@ export class CreateVirtualEnvTool extends BaseTool<ICreateVirtualEnvToolParams>
116121

117122
// Wait a few secs to ensure the env is selected as the active environment..
118123
// If this doesn't work, then something went wrong.
119-
await raceTimeout(5_000, interpreterChanged);
124+
await raceCancellationError(raceTimeout(5_000, interpreterChanged), token);
120125

121126
const stopWatch = new StopWatch();
122127
let env: ResolvedEnvironment | undefined;
123-
while (stopWatch.elapsedTime < 5_000 || !env) {
124-
env = await this.api.resolveEnvironment(createdEnvPath);
128+
while (stopWatch.elapsedTime < 5_000 && !env) {
129+
env = await raceCancellationError(this.api.resolveEnvironment(createdEnvPath), token);
125130
if (env) {
126131
break;
127132
} else {
128133
traceVerbose(
129134
`${CreateVirtualEnvTool.toolName} tool invoked, env created but not yet resolved, waiting...`,
130135
);
131-
await sleep(200);
136+
await raceCancellationError(sleep(200), token);
132137
}
133138
}
134139
if (!env) {

src/client/chat/selectEnvTool.ts

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
getEnvDetailsForResponse,
2626
getToolResponseIfNotebook,
2727
IResourceReference,
28+
raceCancellationError,
2829
} from './utils';
2930
import { ITerminalHelper } from '../common/terminal/types';
3031
import { raceTimeout } from '../common/utils/async';
@@ -67,20 +68,26 @@ export class SelectPythonEnvTool extends BaseTool<ISelectPythonEnvToolArguments>
6768
let selected: boolean | undefined = false;
6869
const hasVenvOrCondaEnvInWorkspaceFolder = doesWorkspaceHaveVenvOrCondaEnv(resource, this.api);
6970
if (options.input.reason === 'cancelled' || hasVenvOrCondaEnvInWorkspaceFolder) {
70-
const result = (await Promise.resolve(
71-
commands.executeCommand(Commands.Set_Interpreter, {
72-
hideCreateVenv: false,
73-
showBackButton: false,
74-
}),
75-
)) as SelectEnvironmentResult | undefined;
71+
const result = await raceCancellationError(
72+
Promise.resolve(
73+
commands.executeCommand(Commands.Set_Interpreter, {
74+
hideCreateVenv: false,
75+
showBackButton: false,
76+
}),
77+
) as Promise<SelectEnvironmentResult | undefined>,
78+
token,
79+
);
7680
if (result?.path) {
7781
traceVerbose(`User selected a Python environment ${result.path} in Select Python Tool.`);
7882
selected = true;
7983
} else {
8084
traceWarn(`User did not select a Python environment in Select Python Tool.`);
8185
}
8286
} else {
83-
selected = await showCreateAndSelectEnvironmentQuickPick(resource, this.serviceContainer);
87+
selected = await raceCancellationError(
88+
showCreateAndSelectEnvironmentQuickPick(resource, this.serviceContainer, token),
89+
token,
90+
);
8491
if (selected) {
8592
traceVerbose(`User selected a Python environment ${selected} in Select Python Tool(2).`);
8693
} else {
@@ -152,6 +159,7 @@ export class SelectPythonEnvTool extends BaseTool<ISelectPythonEnvToolArguments>
152159
async function showCreateAndSelectEnvironmentQuickPick(
153160
uri: Uri | undefined,
154161
serviceContainer: IServiceContainer,
162+
token: CancellationToken,
155163
): Promise<boolean | undefined> {
156164
const createLabel = `${Octicons.Add} ${InterpreterQuickPickList.create.label}`;
157165
const selectLabel = l10n.t('Select an existing Python Environment');
@@ -161,11 +169,15 @@ async function showCreateAndSelectEnvironmentQuickPick(
161169
{ label: selectLabel },
162170
];
163171

164-
const selectedItem = await showQuickPick(items, {
165-
placeHolder: l10n.t('Configure a Python Environment'),
166-
matchOnDescription: true,
167-
ignoreFocusOut: true,
168-
});
172+
const selectedItem = await showQuickPick(
173+
items,
174+
{
175+
placeHolder: l10n.t('Configure a Python Environment'),
176+
matchOnDescription: true,
177+
ignoreFocusOut: true,
178+
},
179+
token,
180+
);
169181

170182
if (selectedItem && !Array.isArray(selectedItem) && selectedItem.label === createLabel) {
171183
const disposables = new DisposableStore();
@@ -187,7 +199,7 @@ async function showCreateAndSelectEnvironmentQuickPick(
187199
);
188200

189201
if (created?.action === 'Back') {
190-
return showCreateAndSelectEnvironmentQuickPick(uri, serviceContainer);
202+
return showCreateAndSelectEnvironmentQuickPick(uri, serviceContainer, token);
191203
}
192204
if (created?.action === 'Cancel') {
193205
return undefined;
@@ -206,7 +218,7 @@ async function showCreateAndSelectEnvironmentQuickPick(
206218
commands.executeCommand(Commands.Set_Interpreter, { hideCreateVenv: true, showBackButton: true }),
207219
)) as SelectEnvironmentResult | undefined;
208220
if (result?.action === 'Back') {
209-
return showCreateAndSelectEnvironmentQuickPick(uri, serviceContainer);
221+
return showCreateAndSelectEnvironmentQuickPick(uri, serviceContainer, token);
210222
}
211223
if (result?.action === 'Cancel') {
212224
return undefined;

0 commit comments

Comments
 (0)