Commit 3c072d1
fix: read workspaceSearchPaths scoped per folder in multi-root workspaces (#1448)
In a multi-root workspace, `python-envs.workspaceSearchPaths` set in a
folder's `.vscode/settings.json` was silently ignored.
`getWorkspaceSearchPaths()` called `getConfiguration('python-envs')`
without a scope URI, so `inspect().workspaceFolderValue` was always
`undefined` — VS Code needs a folder URI to resolve per-folder settings.
## Changes
- **`getWorkspaceSearchPaths(scope?: Uri)`** — accepts an optional
folder URI and passes it to `getConfiguration`, allowing VS Code to
return the correct `workspaceFolderValue` per folder.
- **`getAllExtraSearchPaths()`** — replaced the single unscoped call
with a per-folder loop. Each workspace folder's paths are resolved
relative to *that specific folder*, not broadcast against all folders.
```typescript
// Before — one unscoped call; workspaceFolderValue always undefined in multi-root
const workspaceSearchPaths = getWorkspaceSearchPaths();
// relative paths then resolved against every workspace folder
// After — one call per folder, scope-aware
for (const folder of workspaceFolders) {
const paths = getWorkspaceSearchPaths(folder.uri); // scoped ✓
workspaceSearchPathsPerFolder.push({ paths, folder: folder.uri });
}
// each relative path resolved only against the folder it came from
```
- **New unit tests** — two multi-root scenarios: (1) verifies
folder-specific configs are read independently via scoped calls, (2)
verifies a relative path from `folderA` is not resolved against
`folderB`.
> [!WARNING]
>
> <details>
> <summary>Firewall rules blocked me from connecting to one or more
addresses (expand for details)</summary>
>
> #### I tried to connect to the following addresses, but was blocked by
firewall rules:
>
> - `https://api.github.com/graphql`
> - Triggering command: `/usr/bin/gh gh issue list --state open --limit
5 --json number,title,labels` (http block)
>
> If you need me to access, download, or install something from one of
these locations, you can either:
>
> - Configure [Actions setup
steps](https://gh.io/copilot/actions-setup-steps) to set up my
environment, which run before the firewall is enabled
> - Add the appropriate URLs or hosts to the custom allowlist in this
repository's [Copilot coding agent
settings](https://github.com/microsoft/vscode-python-environments/settings/copilot/coding_agent)
(admins only)
>
> </details>
<!-- START COPILOT ORIGINAL PROMPT -->
<details>
<summary>Original prompt</summary>
## Problem
In a multi-root workspace, `python-envs.workspaceSearchPaths` configured
in a folder's `.vscode/settings.json` is silently ignored. This is
because `getWorkspaceSearchPaths()` in
`src/managers/common/nativePythonFinder.ts` calls
`getConfiguration('python-envs')` **without passing a scope URI**. In VS
Code's API, when no scope is provided, `inspect().workspaceFolderValue`
is `undefined` in multi-root workspaces — VS Code doesn't know which
folder to inspect.
Fixes
#1417
## Root Cause
In `src/managers/common/nativePythonFinder.ts`:
1. **`getWorkspaceSearchPaths()`** (around line 900) calls
`getConfiguration('python-envs')` without a scope parameter. In a
multi-root workspace,
`inspect('workspaceSearchPaths').workspaceFolderValue` is always
`undefined` because VS Code needs a folder URI to know which folder's
settings to return.
2. **`getAllExtraSearchPaths()`** (around line 824) calls
`getWorkspaceSearchPaths()` only once (unscoped), then resolves relative
paths against *all* workspace folders. This is incorrect for multi-root:
if folderA configures `./envs` and folderB configures `./venvs`, you
don't want `./envs` resolved against folderB.
## Required Fix
### 1. `getWorkspaceSearchPaths()` — add an optional `scope` parameter
Change the function signature to accept an optional `Uri` scope and pass
it to `getConfiguration`:
```typescript
function getWorkspaceSearchPaths(scope?: Uri): string[] {
try {
const envConfig = getConfiguration('python-envs', scope); // ← pass scope
const inspection = envConfig.inspect<string[]>('workspaceSearchPaths');
// ... rest of function stays the same
```
### 2. `getAllExtraSearchPaths()` — iterate over workspace folders
Replace the current single unscoped call with a per-folder loop. Instead
of:
```typescript
const workspaceSearchPaths = getWorkspaceSearchPaths();
```
Do:
```typescript
const workspaceFolders = getWorkspaceFolders();
const workspaceSearchPathsPerFolder: { paths: string[]; folder?: Uri }[] = [];
if (workspaceFolders && workspaceFolders.length > 0) {
for (const folder of workspaceFolders) {
const paths = getWorkspaceSearchPaths(folder.uri);
workspaceSearchPathsPerFolder.push({ paths, folder: folder.uri });
}
} else {
// No workspace folders — fall back to unscoped
workspaceSearchPathsPerFolder.push({ paths: getWorkspaceSearchPaths() });
}
// Resolve relative paths against the *specific* folder they came from
for (const { paths, folder } of workspaceSearchPathsPerFolder) {
for (const searchPath of paths) {
if (!searchPath || searchPath.trim() === '') continue;
const trimmedPath = searchPath.trim();
if (isAbsolutePath(trimmedPath)) {
searchDirectories.push(trimmedPath);
} else if (folder) {
const resolvedPath = path.resolve(folder.fsPath, trimmedPath);
searchDirectories.push(resolvedPath);
} else {
traceWarn('No workspace folder for relative search path:', trimmedPath);
}
}
}
```
### 3. Update the existing unit tests
The existing tests in
`src/test/managers/common/nativePythonFinder.getAllExtraSearchPaths.unit.test.ts`
mock `getConfiguration` without a scope. The tests that exercise
`workspaceFolderValue` will need to be updated so that the mock returns
the correct `workspaceFolderValue` when called with a specific folder
URI scope. Make sure all existing tests still pass, and add a new test
that verifies multi-root folder-level settings are read correctly (i.e.,
different folders returning different `workspaceSearchPaths`).
The following is the prior conversation context from the user's chat
exploration (may be truncated):
User: Tell me how to repro and debug this.
Assistant:
Let me investigate the issue and the codebase to understand how
`python-envs.workspaceSearchPaths` is handled and why it might be
silently ignored in multi-root workspaces.
Assistant: Now I have a thorough understanding of the code. Here's the
root cause and how to repro/debug:
---
## Root Cause
The bug is in `getWorkspaceSearchPaths()` at [`nativePythonFinder.ts`
lines
900–927](https://github.com/microsoft/vscode-python-environments/blob/d9e20315f1947bae6dc1b753a11e7fc21255970a/src/managers/common/nativePythonFinder.ts#L900-L927).
The function calls `getConfiguration('python-envs')` **without passing a
scope** (i.e., no workspace-folder `Uri`):
```typescript name=src/managers/common/nativePythonFinder.ts url=https://github.com/microsoft/vscode-python-environments/blob/d9e20315f1947bae6dc1b753a11e7fc21255970a/src/managers/common/nativePythonFinder.ts#L900-L927
function getWorkspaceSearchPaths(): string[] {
try {
const envConfig = getConfiguration('python-envs'); // ← no scope!
const inspection = envConfig.inspect<string[]>('workspaceSearchPaths');
// ...
if (inspection?.workspaceFolderValue) ...
</details>
<!-- START COPILOT CODING AGENT SUFFIX -->
*This pull request was created from Copilot chat.*
>
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: edvilme <5952839+edvilme@users.noreply.github.com>
Co-authored-by: Eduardo Villalpando Mello <eduardo.villalpando.mello@gmail.com>1 parent c9b94cb commit 3c072d1
2 files changed
Lines changed: 158 additions & 22 deletions
File tree
- src
- managers/common
- test/managers/common
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1233 | 1233 | | |
1234 | 1234 | | |
1235 | 1235 | | |
1236 | | - | |
1237 | | - | |
1238 | | - | |
1239 | | - | |
1240 | | - | |
1241 | | - | |
1242 | | - | |
| 1236 | + | |
| 1237 | + | |
| 1238 | + | |
| 1239 | + | |
| 1240 | + | |
| 1241 | + | |
| 1242 | + | |
| 1243 | + | |
1243 | 1244 | | |
| 1245 | + | |
| 1246 | + | |
| 1247 | + | |
| 1248 | + | |
| 1249 | + | |
| 1250 | + | |
| 1251 | + | |
| 1252 | + | |
| 1253 | + | |
| 1254 | + | |
| 1255 | + | |
1244 | 1256 | | |
1245 | | - | |
| 1257 | + | |
1246 | 1258 | | |
1247 | | - | |
1248 | | - | |
1249 | | - | |
1250 | | - | |
1251 | | - | |
1252 | | - | |
1253 | | - | |
1254 | | - | |
1255 | | - | |
1256 | | - | |
1257 | | - | |
| 1259 | + | |
| 1260 | + | |
| 1261 | + | |
| 1262 | + | |
| 1263 | + | |
| 1264 | + | |
| 1265 | + | |
1258 | 1266 | | |
1259 | | - | |
| 1267 | + | |
1260 | 1268 | | |
1261 | 1269 | | |
1262 | 1270 | | |
| |||
1298 | 1306 | | |
1299 | 1307 | | |
1300 | 1308 | | |
1301 | | - | |
| 1309 | + | |
1302 | 1310 | | |
1303 | | - | |
| 1311 | + | |
1304 | 1312 | | |
1305 | 1313 | | |
1306 | 1314 | | |
| |||
Lines changed: 128 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
| 2 | + | |
2 | 3 | | |
3 | 4 | | |
4 | 5 | | |
| |||
480 | 481 | | |
481 | 482 | | |
482 | 483 | | |
| 484 | + | |
| 485 | + | |
| 486 | + | |
| 487 | + | |
| 488 | + | |
| 489 | + | |
| 490 | + | |
| 491 | + | |
| 492 | + | |
| 493 | + | |
| 494 | + | |
| 495 | + | |
| 496 | + | |
| 497 | + | |
| 498 | + | |
| 499 | + | |
| 500 | + | |
| 501 | + | |
| 502 | + | |
| 503 | + | |
| 504 | + | |
| 505 | + | |
| 506 | + | |
| 507 | + | |
| 508 | + | |
| 509 | + | |
| 510 | + | |
| 511 | + | |
| 512 | + | |
| 513 | + | |
| 514 | + | |
| 515 | + | |
| 516 | + | |
| 517 | + | |
| 518 | + | |
| 519 | + | |
| 520 | + | |
| 521 | + | |
| 522 | + | |
| 523 | + | |
| 524 | + | |
| 525 | + | |
| 526 | + | |
| 527 | + | |
| 528 | + | |
| 529 | + | |
| 530 | + | |
| 531 | + | |
| 532 | + | |
| 533 | + | |
| 534 | + | |
| 535 | + | |
| 536 | + | |
| 537 | + | |
| 538 | + | |
| 539 | + | |
| 540 | + | |
| 541 | + | |
| 542 | + | |
| 543 | + | |
| 544 | + | |
| 545 | + | |
| 546 | + | |
| 547 | + | |
| 548 | + | |
| 549 | + | |
| 550 | + | |
| 551 | + | |
| 552 | + | |
| 553 | + | |
| 554 | + | |
| 555 | + | |
| 556 | + | |
| 557 | + | |
| 558 | + | |
| 559 | + | |
| 560 | + | |
| 561 | + | |
| 562 | + | |
| 563 | + | |
| 564 | + | |
| 565 | + | |
| 566 | + | |
| 567 | + | |
| 568 | + | |
| 569 | + | |
| 570 | + | |
| 571 | + | |
| 572 | + | |
| 573 | + | |
| 574 | + | |
| 575 | + | |
| 576 | + | |
| 577 | + | |
| 578 | + | |
| 579 | + | |
| 580 | + | |
| 581 | + | |
| 582 | + | |
| 583 | + | |
| 584 | + | |
| 585 | + | |
| 586 | + | |
| 587 | + | |
| 588 | + | |
| 589 | + | |
| 590 | + | |
| 591 | + | |
| 592 | + | |
| 593 | + | |
| 594 | + | |
| 595 | + | |
| 596 | + | |
| 597 | + | |
| 598 | + | |
| 599 | + | |
| 600 | + | |
| 601 | + | |
| 602 | + | |
| 603 | + | |
| 604 | + | |
| 605 | + | |
| 606 | + | |
| 607 | + | |
| 608 | + | |
| 609 | + | |
| 610 | + | |
483 | 611 | | |
484 | 612 | | |
485 | 613 | | |
| |||
0 commit comments