Skip to content

Commit 3d5ded6

Browse files
committed
impelment MVP of searchPaths
1 parent 43492da commit 3d5ded6

File tree

2 files changed

+32
-113
lines changed

2 files changed

+32
-113
lines changed

package.nls.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"python-envs.terminal.autoActivationType.off": "No automatic activation of environments.",
1313
"python-envs.terminal.useEnvFile.description": "Controls whether environment variables from .env files and python.envFile setting are injected into terminals.",
1414
"python-env.globalSearchPaths.description": "Global search paths for Python environments. Absolute directory paths that are searched at the user level.",
15-
"python-env.workspaceSearchPaths.description": "Workspace search paths for Python environments. Can be relative directory paths or regex patterns searched within the workspace.",
15+
"python-env.workspaceSearchPaths.description": "Workspace search paths for Python environments. Can be absolute paths or relative directory paths searched within the workspace.",
1616
"python-envs.terminal.revertStartupScriptChanges.title": "Revert Shell Startup Script Changes",
1717
"python-envs.reportIssue.title": "Report Issue",
1818
"python-envs.setEnvManager.title": "Set Environment Manager",

src/managers/common/nativePythonFinder.ts

Lines changed: 31 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -329,8 +329,6 @@ class NativePythonFinderImpl implements NativePythonFinder {
329329
// Get all extra search paths including legacy settings and new searchPaths
330330
const extraSearchPaths = await getAllExtraSearchPaths();
331331

332-
traceLog('Final environment directories:', extraSearchPaths);
333-
334332
const options: ConfigurationOptions = {
335333
workspaceDirectories: this.api.getPythonProjects().map((item) => item.uri.fsPath),
336334
environmentDirectories: extraSearchPaths,
@@ -384,49 +382,6 @@ function getPythonSettingAndUntildify<T>(name: string, scope?: Uri): T | undefin
384382
return value;
385383
}
386384

387-
/**
388-
* Checks if a search path is a regex pattern.
389-
* A path is considered a regex pattern if it contains regex special characters
390-
* but is not a Windows path (which can contain backslashes).
391-
* @param searchPath The search path to check
392-
* @returns true if the path is a regex pattern, false otherwise
393-
*/
394-
function isRegexSearchPattern(searchPath: string): boolean {
395-
// Check if it's a regex pattern (contains regex special characters)
396-
// Note: Windows paths contain backslashes, so we need to be more careful
397-
const regexChars = /[*?[\]{}()^$+|\\]/;
398-
const hasBackslash = searchPath.includes('\\');
399-
const isWindowsPath = hasBackslash && (searchPath.match(/^[A-Za-z]:\\/) || searchPath.match(/^\\\\[^\\]+\\/));
400-
return regexChars.test(searchPath) && !isWindowsPath;
401-
}
402-
403-
/**
404-
* Extracts the environment directory from a Python executable path.
405-
* This follows the pattern: executable -> bin -> env -> search directory
406-
* @param executablePath Path to Python executable
407-
* @returns The environment directory path, or undefined if not found
408-
*/
409-
function extractEnvironmentDirectory(executablePath: string): string | undefined {
410-
try {
411-
// TODO: This logic may need to be adjusted for Windows paths (esp with Conda as doesn't use Scripts folder?)
412-
const environmentDir = path.dirname(path.dirname(path.dirname(executablePath)));
413-
if (environmentDir && environmentDir !== path.dirname(environmentDir)) {
414-
traceLog('Extracted environment directory:', environmentDir, 'from executable:', executablePath);
415-
return environmentDir;
416-
} else {
417-
traceLog(
418-
'Warning: identified executable python at',
419-
executablePath,
420-
'not configured in correct folder structure, skipping',
421-
);
422-
return undefined;
423-
}
424-
} catch (error) {
425-
traceLog('Error extracting environment directory from:', executablePath, 'Error:', error);
426-
return undefined;
427-
}
428-
}
429-
430385
/**
431386
* Gets all extra environment search paths from various configuration sources.
432387
* Combines legacy python settings (with migration), globalSearchPaths, and workspaceSearchPaths.
@@ -443,74 +398,37 @@ async function getAllExtraSearchPaths(): Promise<string[]> {
443398
const customVenvDirs = getCustomVirtualEnvDirsLegacy();
444399
searchDirectories.push(...customVenvDirs);
445400
traceLog('Added legacy custom venv directories (not covered by globalSearchPaths):', customVenvDirs);
446-
} else {
447-
traceLog('Skipping legacy custom venv directories - they are covered by globalSearchPaths');
448401
}
449402

450-
// Get globalSearchPaths (absolute paths, no regex)
403+
// Get globalSearchPaths
451404
const globalSearchPaths = getGlobalSearchPaths();
452-
traceLog('Retrieved globalSearchPaths:', globalSearchPaths);
453-
for (const globalPath of globalSearchPaths) {
454-
try {
455-
if (!globalPath || globalPath.trim() === '') {
456-
continue;
457-
}
458-
const trimmedPath = globalPath.trim();
459-
traceLog('Processing global search path:', trimmedPath);
460-
// Simply add the trimmed global path
461-
searchDirectories.push(trimmedPath);
462-
} catch (error) {
463-
traceLog('Error processing global search path:', globalPath, 'Error:', error);
464-
}
465-
}
405+
searchDirectories.push(...globalSearchPaths);
466406

467-
// Get workspaceSearchPaths (can include regex patterns)
407+
// Get workspaceSearchPaths
468408
const workspaceSearchPaths = getWorkspaceSearchPaths();
469-
traceLog('Retrieved workspaceSearchPaths:', workspaceSearchPaths);
470-
for (const searchPath of workspaceSearchPaths) {
471-
try {
472-
if (!searchPath || searchPath.trim() === '') {
473-
continue;
474-
}
475409

476-
const trimmedPath = searchPath.trim();
477-
const isRegexPattern = isRegexSearchPattern(trimmedPath);
478-
479-
if (isRegexPattern) {
480-
// Search for Python executables using the regex pattern
481-
// Look for common Python executable names within the pattern
482-
const pythonExecutablePatterns = isWindows()
483-
? [`${trimmedPath}/**/python.exe`, `${trimmedPath}/**/python3.exe`]
484-
: [`${trimmedPath}/**/python`, `${trimmedPath}/**/python3`];
410+
// Resolve relative paths against workspace folders
411+
for (const searchPath of workspaceSearchPaths) {
412+
if (!searchPath || searchPath.trim() === '') {
413+
continue;
414+
}
485415

486-
traceLog('Searching for Python executables with patterns:', pythonExecutablePatterns);
487-
for (const pattern of pythonExecutablePatterns) {
488-
try {
489-
const foundFiles = await workspace.findFiles(pattern, null);
490-
traceLog(
491-
'Python executable search found',
492-
foundFiles.length,
493-
'files matching pattern:',
494-
pattern,
495-
);
416+
const trimmedPath = searchPath.trim();
496417

497-
for (const file of foundFiles) {
498-
// given the executable path, extract and save the environment directory
499-
const environmentDir = extractEnvironmentDirectory(file.fsPath);
500-
if (environmentDir) {
501-
searchDirectories.push(environmentDir);
502-
}
503-
}
504-
} catch (error) {
505-
traceLog('Error searching for Python executables with pattern:', pattern, 'Error:', error);
506-
}
418+
if (path.isAbsolute(trimmedPath)) {
419+
// Absolute path - use as is
420+
searchDirectories.push(trimmedPath);
421+
} else {
422+
// Relative path - resolve against all workspace folders
423+
const workspaceFolders = workspace.workspaceFolders;
424+
if (workspaceFolders) {
425+
for (const workspaceFolder of workspaceFolders) {
426+
const resolvedPath = path.resolve(workspaceFolder.uri.fsPath, trimmedPath);
427+
searchDirectories.push(resolvedPath);
507428
}
508429
} else {
509-
// If it's not a regex, treat it as a normal directory path and just add it
510-
searchDirectories.push(trimmedPath);
430+
traceLog('Warning: No workspace folders found for relative path:', trimmedPath);
511431
}
512-
} catch (error) {
513-
traceLog('Error processing workspace search path:', searchPath, 'Error:', error);
514432
}
515433
}
516434

@@ -535,7 +453,6 @@ function getGlobalSearchPaths(): string[] {
535453
const inspection = envConfig.inspect<string[]>('globalSearchPaths');
536454

537455
const globalPaths = inspection?.globalValue || [];
538-
traceLog('Retrieved globalSearchPaths:', globalPaths);
539456
return untildifyArray(globalPaths);
540457
} catch (error) {
541458
traceLog('Error getting globalSearchPaths:', error);
@@ -544,27 +461,29 @@ function getGlobalSearchPaths(): string[] {
544461
}
545462

546463
/**
547-
* Gets workspaceSearchPaths setting with workspace precedence.
548-
* Gets the most specific workspace-level setting available.
464+
* Gets the most specific workspace-level setting available for workspaceSearchPaths.
549465
*/
550466
function getWorkspaceSearchPaths(): string[] {
551467
try {
552468
const envConfig = getConfiguration('python-env');
553469
const inspection = envConfig.inspect<string[]>('workspaceSearchPaths');
554470

471+
if (inspection?.globalValue) {
472+
traceLog(
473+
'Error: python-env.workspaceSearchPaths is set at the user/global level, but this setting can only be set at the workspace or workspace folder level.',
474+
);
475+
}
476+
555477
// For workspace settings, prefer workspaceFolder > workspace
556478
if (inspection?.workspaceFolderValue) {
557-
traceLog('Using workspaceFolder level workspaceSearchPaths setting');
558479
return inspection.workspaceFolderValue;
559480
}
560481

561482
if (inspection?.workspaceValue) {
562-
traceLog('Using workspace level workspaceSearchPaths setting');
563483
return inspection.workspaceValue;
564484
}
565485

566486
// Default empty array (don't use global value for workspace settings)
567-
traceLog('No workspaceSearchPaths setting found at workspace level, using empty array');
568487
return [];
569488
} catch (error) {
570489
traceLog('Error getting workspaceSearchPaths:', error);
@@ -607,12 +526,9 @@ async function handleLegacyPythonSettingsMigration(): Promise<boolean> {
607526

608527
if (globalLegacyPaths.length === 0) {
609528
// No legacy settings exist, so they're "covered" (nothing to worry about)
610-
traceLog('No legacy python settings found');
611529
return true;
612530
}
613531

614-
traceLog('Found legacy python settings - global paths:', globalLegacyPaths);
615-
616532
// Check if legacy paths are already in globalSearchPaths
617533
const globalSearchPathsInspection = envConfig.inspect<string[]>('globalSearchPaths');
618534
const currentGlobalSearchPaths = globalSearchPathsInspection?.globalValue || [];
@@ -630,7 +546,10 @@ async function handleLegacyPythonSettingsMigration(): Promise<boolean> {
630546
// Need to migrate - add legacy paths to globalSearchPaths
631547
const combinedGlobalPaths = Array.from(new Set([...currentGlobalSearchPaths, ...globalLegacyPaths]));
632548
await envConfig.update('globalSearchPaths', combinedGlobalPaths, true); // true = global/user level
633-
traceLog('Migrated legacy global python settings to globalSearchPaths. Combined paths:', combinedGlobalPaths);
549+
traceLog(
550+
'Migrated legacy global python settings to globalSearchPaths. globalSearchPaths setting is now:',
551+
combinedGlobalPaths,
552+
);
634553

635554
// Show notification to user about migration
636555
if (!migrationNotificationShown) {

0 commit comments

Comments
 (0)