Skip to content

Commit 6a6a4b0

Browse files
committed
support listing virtual env interpreters
1 parent 178a24b commit 6a6a4b0

1 file changed

Lines changed: 83 additions & 20 deletions

File tree

‎src/client/providers/setInterpreterProvider.ts‎

Lines changed: 83 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"use strict";
22
import * as child_process from 'child_process';
33
import * as path from "path";
4+
import * as fs from "fs";
45
import * as vscode from "vscode";
56
import * as settings from "./../common/configSettings";
67
import * as utils from "./../common/utils";
@@ -9,13 +10,22 @@ let ncp = require("copy-paste");
910
// where to find the Python binary within a conda env
1011
const CONDA_RELATIVE_PY_PATH = utils.IS_WINDOWS ? ['python'] : ['bin', 'python']
1112
const REPLACE_PYTHONPATH_REGEXP = /("python\.pythonPath"\s*:\s*)"(.*)"/g;
13+
const CHECK_PYTHON_INTERPRETER_REGEXP = utils.IS_WINDOWS ? /^python(\d+(.\d+)?)?\.exe$/ : /^python(\d+(.\d+)?)?$/;
1214

1315
interface PythonPathSuggestion {
1416
label: string, // myenvname
1517
path: string, // /full/path/to/bin/python
1618
type: string // conda
1719
}
1820

21+
interface PythonPathQuickPickItem extends vscode.QuickPickItem {
22+
path: string
23+
}
24+
25+
function isPythonInterpreter(filePath: string): boolean {
26+
return CHECK_PYTHON_INTERPRETER_REGEXP.test(filePath);
27+
}
28+
1929
function getSearchPaths(): string[] {
2030
if (utils.IS_WINDOWS) {
2131
return [
@@ -35,16 +45,9 @@ function getSearchPaths(): string[] {
3545
'C:\\Program Files\\Python 2.7',
3646
'C:\\Program Files\\Python 3.4',
3747
'C:\\Program Files\\Python 3.5'
38-
].map(p => path.join(p, 'python.exe'));
48+
];
3949
} else {
40-
const pyPaths = [];
41-
const pyExecutables = ['python', 'python2.6', 'python2.7', 'python3.3', 'python3.4', 'python3.5'];
42-
['/usr/local/bin', '/usr/bin', '/bin', '/usr/sbin', '/sbin'].forEach(p => {
43-
pyExecutables.forEach(pyExecName => {
44-
pyPaths.push(path.join(p, pyExecName));
45-
});
46-
});
47-
return pyPaths;
50+
return ['/usr/local/bin', '/usr/bin', '/bin', '/usr/sbin', '/sbin'];
4851
}
4952
}
5053

@@ -72,14 +75,66 @@ export function activateSetInterpreterProvider() {
7275
vscode.commands.registerCommand("python.setInterpreter", setInterpreter);
7376
}
7477

78+
function lookForInterpretersInPath(pathToCheck: string): Promise<string[]> {
79+
return new Promise<string[]>(resolve => {
80+
// Now look for Interpreters in this directory
81+
fs.readdir(pathToCheck, (err, subDirs) => {
82+
if (err) {
83+
return resolve([]);
84+
}
85+
const interpreters = subDirs
86+
.filter(subDir => CHECK_PYTHON_INTERPRETER_REGEXP.test(subDir))
87+
.map(subDir => path.join(pathToCheck, subDir));
88+
resolve(interpreters);
89+
});
90+
});
91+
}
92+
function lookForInterpretersInVirtualEnvs(pathToCheck: string): Promise<PythonPathSuggestion[]> {
93+
return new Promise<PythonPathSuggestion[]>(resolve => {
94+
// Now look for Interpreters in this directory
95+
fs.readdir(pathToCheck, (err, subDirs) => {
96+
if (err) {
97+
return resolve([]);
98+
}
99+
const envsInterpreters = [];
100+
const promises = subDirs.map(subDir => {
101+
subDir = path.join(pathToCheck, subDir);
102+
const interpreterFolder = utils.IS_WINDOWS ? path.join(subDir, 'scripts') : path.join(subDir, 'bin');
103+
return lookForInterpretersInPath(interpreterFolder);
104+
});
105+
Promise.all<string[]>(promises).then(pathsWithInterpreters => {
106+
pathsWithInterpreters.forEach(interpreters => {
107+
interpreters.map(interpter => {
108+
envsInterpreters.push({
109+
label: path.basename(interpter), path: interpter, type: ''
110+
});
111+
})
112+
});
113+
114+
resolve(envsInterpreters);
115+
})
116+
});
117+
});
118+
}
75119
function suggestionsFromKnownPaths(): Promise<PythonPathSuggestion[]> {
76120
return new Promise(resolve => {
77-
const validPaths = getSearchPaths().map(p => utils.validatePath(p));
78-
Promise.all<string>(validPaths).then(paths => {
79-
const suggestions = paths.filter(p => p.length > 0).map(p => {
80-
return <PythonPathSuggestion>{
81-
label: path.basename(p), path: p, type: ''
121+
const validPaths = getSearchPaths().map(p => {
122+
return utils.validatePath(p).then(validatedPath => {
123+
if (validatedPath.length === 0) {
124+
return Promise.resolve<string[]>([]);
82125
}
126+
127+
return lookForInterpretersInPath(validatedPath);
128+
});
129+
});
130+
Promise.all<string[]>(validPaths).then(listOfInterpreters => {
131+
const suggestions: PythonPathSuggestion[] = [];
132+
listOfInterpreters.forEach(interpreters => {
133+
interpreters.filter(interpter => interpter.length > 0).map(interpter => {
134+
suggestions.push({
135+
label: path.basename(interpter), path: interpter, type: ''
136+
});
137+
});
83138
});
84139
resolve(suggestions);
85140
});
@@ -116,23 +171,31 @@ function suggestionsFromConda(): Promise<PythonPathSuggestion[]> {
116171
});
117172
}
118173

119-
function suggestionToQuickPickItem(suggestion: PythonPathSuggestion): vscode.QuickPickItem {
174+
175+
function suggestionToQuickPickItem(suggestion: PythonPathSuggestion): PythonPathQuickPickItem {
176+
let detail = suggestion.path;
177+
if (suggestion.path.startsWith(vscode.workspace.rootPath)) {
178+
detail = path.relative(vscode.workspace.rootPath, suggestion.path);
179+
}
180+
detail = utils.IS_WINDOWS ? detail.replace(/\\/g, "/") : detail;
120181
return {
121182
label: suggestion.label,
122183
description: suggestion.type,
123-
detail: utils.IS_WINDOWS ? suggestion.path.replace(/\\/g, "/") : suggestion.path
184+
detail: detail,
185+
path: utils.IS_WINDOWS ? suggestion.path.replace(/\\/g, "/") : suggestion.path
124186
}
125187
}
126188

127-
function suggestPythonPaths(): Promise<vscode.QuickPickItem[]> {
189+
function suggestPythonPaths(): Promise<PythonPathQuickPickItem[]> {
128190
// For now we only interrogate conda for suggestions.
129191
const condaSuggestions = suggestionsFromConda();
130192
const knownPathSuggestions = suggestionsFromKnownPaths();
193+
const virtualEnvSuggestions = lookForInterpretersInVirtualEnvs(vscode.workspace.rootPath);
131194

132195
// Here we could also look for virtualenvs/default install locations...
133196

134-
return Promise.all<PythonPathSuggestion[]>([condaSuggestions, knownPathSuggestions]).then(suggestions => {
135-
const quickPicks: vscode.QuickPickItem[] = [];
197+
return Promise.all<PythonPathSuggestion[]>([condaSuggestions, knownPathSuggestions, virtualEnvSuggestions]).then(suggestions => {
198+
const quickPicks: PythonPathQuickPickItem[] = [];
136199
suggestions.forEach(list => {
137200
quickPicks.push(...list.map(suggestionToQuickPickItem));
138201
});
@@ -201,7 +264,7 @@ function presentQuickPickOfSuggestedPythonPaths() {
201264
vscode.window.showQuickPick(suggestions, quickPickOptions).then(
202265
value => {
203266
if (value !== undefined) {
204-
setPythonPath(value.detail);
267+
setPythonPath(value.path);
205268
}
206269
});
207270
});

0 commit comments

Comments
 (0)