Skip to content

Commit 46531e5

Browse files
committed
fix #353
1 parent 1d7381a commit 46531e5

1 file changed

Lines changed: 64 additions & 2 deletions

File tree

src/client/common/configSettings.ts

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ import * as vscode from 'vscode';
44
import { SystemVariables } from './systemVariables';
55
import { EventEmitter } from 'events';
66
import * as path from 'path';
7+
import * as child_process from 'child_process';
8+
9+
export const IS_WINDOWS = /^win/.test(process.platform);
710

811
export interface IPythonSettings {
912
pythonPath: string;
@@ -117,7 +120,7 @@ export class PythonSettings extends EventEmitter implements IPythonSettings {
117120
this.disposables.push(vscode.workspace.onDidChangeConfiguration(() => {
118121
this.initializeSettings();
119122
}));
120-
123+
121124
this.initializeSettings();
122125
}
123126
public static getInstance(): PythonSettings {
@@ -282,7 +285,23 @@ export class PythonSettings extends EventEmitter implements IPythonSettings {
282285
this.emit('change');
283286
}
284287

285-
public pythonPath: string;
288+
private _pythonPath: string;
289+
public get pythonPath(): string {
290+
return this._pythonPath;
291+
}
292+
public set pythonPath(value: string) {
293+
if (this._pythonPath === value) {
294+
return;
295+
}
296+
// Add support for specifying just the directory where the python executable will be located
297+
// E.g. virtual directory name
298+
try {
299+
this._pythonPath = getPythonExecutable(value);
300+
}
301+
catch (ex) {
302+
this._pythonPath = value;
303+
}
304+
}
286305
public jediPath: string;
287306
public devOptions: string[];
288307
public linting: ILintingSettings;
@@ -301,4 +320,47 @@ function getAbsolutePath(pathToCheck: string, rootDir: string): string {
301320
return pathToCheck;
302321
}
303322
return path.isAbsolute(pathToCheck) ? pathToCheck : path.resolve(rootDir, pathToCheck);
323+
}
324+
325+
function getPythonExecutable(pythonPath: string): string {
326+
// If only 'python'
327+
if (pythonPath === 'python' ||
328+
pythonPath.indexOf(path.sep) === -1 ||
329+
path.basename(pythonPath) === path.dirname(pythonPath)) {
330+
return pythonPath;
331+
}
332+
333+
if (isValidPythonPath(pythonPath)) {
334+
return pythonPath;
335+
}
336+
337+
// Suffix with 'python' for linux and 'osx', and 'python.exe' for 'windows'
338+
if (IS_WINDOWS) {
339+
if (isValidPythonPath(path.join(pythonPath, 'python.exe'))) {
340+
return path.join(pythonPath, 'python.exe');
341+
}
342+
if (isValidPythonPath(path.join(pythonPath, 'scripts', 'python.exe'))) {
343+
return path.join(pythonPath, 'scripts', 'python.exe');
344+
}
345+
}
346+
else {
347+
if (isValidPythonPath(path.join(pythonPath, 'python'))) {
348+
return path.join(pythonPath, 'python');
349+
}
350+
if (isValidPythonPath(path.join(pythonPath, 'bin', 'python'))) {
351+
return path.join(pythonPath, 'bin', 'python');
352+
}
353+
}
354+
355+
return pythonPath;
356+
}
357+
358+
function isValidPythonPath(pythonPath): boolean {
359+
try {
360+
let output = child_process.execFileSync(pythonPath, ['-c', 'print(1234)'], { encoding: 'utf8' });
361+
return output.startsWith('1234');
362+
}
363+
catch (ex) {
364+
return false;
365+
}
304366
}

0 commit comments

Comments
 (0)