Skip to content

Commit bb692f3

Browse files
committed
path normalization
1 parent 8f4a11d commit bb692f3

File tree

2 files changed

+85
-3
lines changed

2 files changed

+85
-3
lines changed

src/managers/common/nativePythonFinder.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -710,10 +710,11 @@ export async function getAllExtraSearchPaths(): Promise<string[]> {
710710
}
711711
}
712712

713-
// Remove duplicates and return
713+
// Remove duplicates and normalize to forward slashes for cross-platform glob compatibility
714714
const uniquePaths = Array.from(new Set(searchDirectories));
715-
traceVerbose('Environment search directories:', uniquePaths.length, 'paths');
716-
return uniquePaths;
715+
const normalizedPaths = uniquePaths.map((p) => p.replace(/\\/g, '/'));
716+
traceVerbose('Environment search directories:', normalizedPaths.length, 'paths');
717+
return normalizedPaths;
717718
}
718719

719720
/**

src/test/managers/common/nativePythonFinder.getAllExtraSearchPaths.unit.test.ts

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,4 +462,85 @@ suite('getAllExtraSearchPaths Integration Tests', () => {
462462
assert.ok(result.some((p) => p.includes('workspace') && p.endsWith('.venv')));
463463
});
464464
});
465+
466+
suite('Cross-Platform Path Normalization', () => {
467+
test('Backslashes are converted to forward slashes for glob compatibility', async () => {
468+
// Mock → Windows-style paths with backslashes
469+
pythonConfig.get.withArgs('venvPath').returns('C:\\Users\\test\\envs');
470+
pythonConfig.get.withArgs('venvFolders').returns(['D:\\shared\\venvs']);
471+
envConfig.inspect.withArgs('globalSearchPaths').returns({
472+
globalValue: ['C:\\Python\\environments', 'E:\\projects\\**\\.venv'],
473+
});
474+
envConfig.inspect.withArgs('workspaceSearchPaths').returns({});
475+
476+
// Run
477+
const result = await getAllExtraSearchPaths();
478+
479+
// Assert - All backslashes should be converted to forward slashes
480+
for (const p of result) {
481+
assert.ok(!p.includes('\\'), `Path should not contain backslashes: ${p}`);
482+
}
483+
assert.ok(result.includes('C:/Users/test/envs'));
484+
assert.ok(result.includes('D:/shared/venvs'));
485+
assert.ok(result.includes('C:/Python/environments'));
486+
assert.ok(result.includes('E:/projects/**/.venv'));
487+
});
488+
489+
test('Glob patterns with backslashes are normalized', async () => {
490+
// Mock → Glob pattern with Windows backslashes
491+
pythonConfig.get.withArgs('venvPath').returns(undefined);
492+
pythonConfig.get.withArgs('venvFolders').returns(undefined);
493+
envConfig.inspect.withArgs('globalSearchPaths').returns({
494+
globalValue: ['C:\\workspace\\**\\venv', 'D:\\projects\\*\\.venv'],
495+
});
496+
envConfig.inspect.withArgs('workspaceSearchPaths').returns({});
497+
498+
// Run
499+
const result = await getAllExtraSearchPaths();
500+
501+
// Assert - Glob patterns should use forward slashes
502+
assert.ok(result.includes('C:/workspace/**/venv'));
503+
assert.ok(result.includes('D:/projects/*/.venv'));
504+
});
505+
506+
test('Linux/macOS paths with forward slashes are preserved', async () => {
507+
// Mock → Unix-style paths (already using forward slashes)
508+
pythonConfig.get.withArgs('venvPath').returns('/home/user/envs');
509+
pythonConfig.get.withArgs('venvFolders').returns(['/opt/shared/venvs']);
510+
envConfig.inspect.withArgs('globalSearchPaths').returns({
511+
globalValue: ['/usr/local/python/environments', '/home/user/projects/**/.venv'],
512+
});
513+
envConfig.inspect.withArgs('workspaceSearchPaths').returns({});
514+
515+
// Run
516+
const result = await getAllExtraSearchPaths();
517+
518+
// Assert - Forward slashes should be preserved as-is
519+
assert.ok(result.includes('/home/user/envs'));
520+
assert.ok(result.includes('/opt/shared/venvs'));
521+
assert.ok(result.includes('/usr/local/python/environments'));
522+
assert.ok(result.includes('/home/user/projects/**/.venv'));
523+
// Verify no backslashes were introduced
524+
for (const p of result) {
525+
assert.ok(!p.includes('\\'), `Path should not contain backslashes: ${p}`);
526+
}
527+
});
528+
529+
test('Mixed path separators are normalized to forward slashes', async () => {
530+
// Mock → Paths with mixed separators (edge case)
531+
pythonConfig.get.withArgs('venvPath').returns(undefined);
532+
pythonConfig.get.withArgs('venvFolders').returns(undefined);
533+
envConfig.inspect.withArgs('globalSearchPaths').returns({
534+
globalValue: ['C:/Users\\test/projects\\.venv', '/home/user\\mixed/path'],
535+
});
536+
envConfig.inspect.withArgs('workspaceSearchPaths').returns({});
537+
538+
// Run
539+
const result = await getAllExtraSearchPaths();
540+
541+
// Assert - All backslashes normalized to forward slashes
542+
assert.ok(result.includes('C:/Users/test/projects/.venv'));
543+
assert.ok(result.includes('/home/user/mixed/path'));
544+
});
545+
});
465546
});

0 commit comments

Comments
 (0)