Skip to content
This repository was archived by the owner on Dec 23, 2021. It is now read-only.

Properly resolve Python path if not set #338

Merged
merged 6 commits into from
Apr 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,6 @@ export const CONSTANTS = {
"error.noProgramFoundDebug",
"Cannot find a program to debug."
),
NO_PIP: localize(
"error.noPip",
"We found that you don't have Pip installed on your computer, please install it and try again."
),
NO_PYTHON_PATH: localize(
"error.noPythonPath",
"We found that you don't have Python 3 installed on your computer, please install the latest version, add it to your PATH and try again."
Expand Down Expand Up @@ -532,7 +528,7 @@ export const STATUS_BAR_PRIORITY = {
};

export const VERSIONS = {
MIN_PY_VERSION: "3.7.0",
MIN_PY_VERSION: "Python 3.7.0",
};

export const HELPER_FILES = {
Expand All @@ -546,6 +542,7 @@ export const HELPER_FILES = {

export const GLOBAL_ENV_VARS = {
PYTHON: "python",
PYTHON3: "python3",
};
export const LANGUAGE_VARS = {
PYTHON: { ID: "python", FILE_ENDS: ".py" },
Expand Down
2 changes: 1 addition & 1 deletion src/install_dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import pathlib
import os

os.chdir(pathlib.Path(__file__).parent.parent.absolute())
os.chdir(str(pathlib.Path(__file__).parent.parent.absolute()))
subprocess.check_call(
[sys.executable, "-m", "pip", "install", "-r", "./out/requirements.txt"]
)
200 changes: 135 additions & 65 deletions src/service/setupService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,11 @@ export class SetupService {
context: vscode.ExtensionContext,
needsResponse: boolean = false
) => {
const originalpythonExecutablePath = await this.getCurrentPythonExecutablePath();
let pythonExecutablePath = originalpythonExecutablePath;
const originalPythonExecutablePath = await this.getCurrentPythonExecutablePath();
if (originalPythonExecutablePath === "") {
return;
}
let pythonExecutablePath = originalPythonExecutablePath;
const pythonExecutableName: string =
os.platform() === "win32"
? HELPER_FILES.PYTHON_EXE
Expand Down Expand Up @@ -71,7 +74,7 @@ export class SetupService {
} else {
pythonExecutablePath = await this.promptInstallVenv(
context,
originalpythonExecutablePath,
originalPythonExecutablePath,
pythonExecutableName
);
this.telemetryAI.trackFeatureUsage(
Expand All @@ -92,7 +95,7 @@ export class SetupService {
TelemetryEventName.SETUP_HAS_VENV
);
}
if (pythonExecutablePath === originalpythonExecutablePath) {
if (pythonExecutablePath === originalPythonExecutablePath) {
// going with original interpreter, either because
// already in venv or error in creating custom venv
if (checkConfig(CONFIG.SHOW_DEPENDENCY_INSTALL)) {
Expand Down Expand Up @@ -159,40 +162,151 @@ export class SetupService {
return pythonExecutablePath;
};

public getCurrentPythonExecutablePath = async () => {
let originalpythonExecutablePath = "";

public getCurrentPythonExecutablePath = async (
isTryingPython3: boolean = false
) => {
let originalPythonExecutablePath = "";
const systemPythonVar = isTryingPython3
? GLOBAL_ENV_VARS.PYTHON3
: GLOBAL_ENV_VARS.PYTHON;
// try to get name from interpreter
try {
originalpythonExecutablePath = getConfig(CONFIG.PYTHON_PATH);
originalPythonExecutablePath = getConfig(CONFIG.PYTHON_PATH);
} catch (err) {
originalpythonExecutablePath = GLOBAL_ENV_VARS.PYTHON;
originalPythonExecutablePath = systemPythonVar;
}

if (
originalpythonExecutablePath === GLOBAL_ENV_VARS.PYTHON ||
originalpythonExecutablePath === ""
originalPythonExecutablePath === GLOBAL_ENV_VARS.PYTHON3 ||
originalPythonExecutablePath === GLOBAL_ENV_VARS.PYTHON ||
originalPythonExecutablePath === ""
) {
// catching any instance where the python path needs to be resolved
// from an system variable
this.telemetryAI.trackFeatureUsage(
TelemetryEventName.SETUP_AUTO_RESOLVE_PYTHON_PATH
);
try {
const { stdout } = await this.executePythonCommand(
GLOBAL_ENV_VARS.PYTHON,
systemPythonVar,
`-c "import sys; print(sys.executable)"`
);
originalpythonExecutablePath = stdout.trim();
originalPythonExecutablePath = stdout.trim();
} catch (err) {
this.telemetryAI.trackFeatureUsage(
TelemetryEventName.SETUP_NO_PYTHON_PATH
);
if (isTryingPython3) {
// if trying python3 failed, that means that BOTH
// python and python3 failed as system variables
// so that means that there is no python
vscode.window
.showErrorMessage(
CONSTANTS.ERROR.NO_PYTHON_PATH,
DialogResponses.INSTALL_PYTHON
)
.then((selection: vscode.MessageItem | undefined) => {
if (selection === DialogResponses.INSTALL_PYTHON) {
const okAction = () => {
this.telemetryAI.trackFeatureUsage(
TelemetryEventName.SETUP_DOWNLOAD_PYTHON
);
open(CONSTANTS.LINKS.DOWNLOAD_PYTHON);
};
showPrivacyModal(
okAction,
CONSTANTS.INFO.THIRD_PARTY_WEBSITE_PYTHON
);
}
});
// no python installed, cannot get path
return "";
} else {
// "python" didn't resolve to anything, trying "python3"
return this.getCurrentPythonExecutablePath(true);
}
}
if (
!(await this.validatePythonVersion(
originalPythonExecutablePath
))
) {
this.telemetryAI.trackFeatureUsage(
TelemetryEventName.SETUP_INVALID_PYTHON_VER
);
if (isTryingPython3) {
// if we're trying python3, it means we already tried python and it
// all doesn't seem to work, but it got this far, so it means that
// their system python3 version is still not above 3.7, but they
// don't have a path selected.
vscode.window
.showInformationMessage(
CONSTANTS.ERROR.INVALID_PYTHON_PATH,
DialogResponses.INSTALL_PYTHON
)
.then(
(installChoice: vscode.MessageItem | undefined) => {
if (
installChoice ===
DialogResponses.INSTALL_PYTHON
) {
const okAction = () => {
this.telemetryAI.trackFeatureUsage(
TelemetryEventName.SETUP_DOWNLOAD_PYTHON
);
open(CONSTANTS.LINKS.DOWNLOAD_PYTHON);
};
showPrivacyModal(
okAction,
CONSTANTS.INFO
.THIRD_PARTY_WEBSITE_PYTHON
);
}
}
);
return "";
} else {
// otherwise, we ran the "python" system variable
// and we can try python3
return this.getCurrentPythonExecutablePath(true);
}
}
} else {
// should only be applicable if the user defined their own path

// fix path to be absolute
if (!path.isAbsolute(originalPythonExecutablePath)) {
originalPythonExecutablePath = path.join(
vscode.workspace.rootPath,
originalPythonExecutablePath
);
}

if (!fs.existsSync(originalPythonExecutablePath)) {
await vscode.window.showErrorMessage(
CONSTANTS.ERROR.BAD_PYTHON_PATH
);
this.telemetryAI.trackFeatureUsage(
TelemetryEventName.SETUP_INVALID_PYTHON_INTERPRETER_PATH
);
return "";
}

if (
!(await this.validatePythonVersion(
originalPythonExecutablePath
))
) {
this.telemetryAI.trackFeatureUsage(
TelemetryEventName.SETUP_INVALID_PYTHON_VER
);
vscode.window
.showErrorMessage(
CONSTANTS.ERROR.NO_PYTHON_PATH,
.showInformationMessage(
CONSTANTS.ERROR.INVALID_PYTHON_PATH,
DialogResponses.INSTALL_PYTHON
)
.then((selection: vscode.MessageItem | undefined) => {
if (selection === DialogResponses.INSTALL_PYTHON) {
.then((installChoice: vscode.MessageItem | undefined) => {
if (installChoice === DialogResponses.INSTALL_PYTHON) {
const okAction = () => {
this.telemetryAI.trackFeatureUsage(
TelemetryEventName.SETUP_DOWNLOAD_PYTHON
Expand All @@ -205,49 +319,21 @@ export class SetupService {
);
}
});
// no python installed, cannot get path
return "";
}
}
// fix path to be absolute
if (!path.isAbsolute(originalpythonExecutablePath)) {
originalpythonExecutablePath = path.join(
vscode.workspace.rootPath,
originalpythonExecutablePath
);
}

if (!fs.existsSync(originalpythonExecutablePath)) {
await vscode.window.showErrorMessage(
CONSTANTS.ERROR.BAD_PYTHON_PATH
);
this.telemetryAI.trackFeatureUsage(
TelemetryEventName.SETUP_INVALID_PYTHON_INTERPRETER_PATH
);
return "";
}

if (!(await this.validatePythonVersion(originalpythonExecutablePath))) {
this.telemetryAI.trackFeatureUsage(
TelemetryEventName.SETUP_INVALID_PYTHON_VER
);
return "";
}

return originalpythonExecutablePath;
return originalPythonExecutablePath;
};

public isPipInstalled = async (pythonExecutablePath: string) => {
try {
const { stdout } = await this.executePythonCommand(
pythonExecutablePath,
" -m pip"
);
await this.executePythonCommand(pythonExecutablePath, " -m pip");
return true;
} catch (err) {
vscode.window
.showErrorMessage(
CONSTANTS.ERROR.NO_PIP,
`We found that you may not have Pip installed on your interpreter at ${pythonExecutablePath}, please install it and try again.`,
DialogResponses.INSTALL_PIP
)
.then((selection: vscode.MessageItem | undefined) => {
Expand Down Expand Up @@ -294,22 +380,6 @@ export class SetupService {
"--version"
);
if (stdout < VERSIONS.MIN_PY_VERSION) {
vscode.window
.showInformationMessage(
CONSTANTS.ERROR.INVALID_PYTHON_PATH,
DialogResponses.INSTALL_PYTHON
)
.then((installChoice: vscode.MessageItem | undefined) => {
if (installChoice === DialogResponses.INSTALL_PYTHON) {
const okAction = () => {
open(CONSTANTS.LINKS.DOWNLOAD_PYTHON);
};
showPrivacyModal(
okAction,
CONSTANTS.INFO.THIRD_PARTY_WEBSITE_PYTHON
);
}
});
return false;
} else {
return true;
Expand Down Expand Up @@ -398,7 +468,7 @@ export class SetupService {
);
vscode.window
.showErrorMessage(
`Virtual environment for download could not be completed. Using original interpreter at: ${pythonExecutable}.`,
`Virtual environment for download could not be completed. Using original interpreter at: ${pythonExecutable}. If you're on Linux, try running "sudo apt-get install python3-venv".`,
DialogResponses.READ_INSTALL_MD
)
.then((selection: vscode.MessageItem | undefined) => {
Expand Down