windows: Detect when python3 is not usable and notify the user (#40070)

Fixes #39998

Debugpy and pylsp are installed in a Zed-global venv with pip. We need a
Python interpreter to create this venv when it doesn't exist and one of
these tools needs to be installed, and sometimes we attempt to use
`python3` from `$PATH`. This can cause issues on Windows, where out of
the box `python3` is a sort of shim that opens the Microsoft Store app.

This PR changes the debugpy installation path to create the Zed-global
venv using the Python interpreter from a venv in the project, and only
use python3 from `$PATH` if that fails. That matches how pylsp
installation already works. It also tightens up how we search for a
global Python installation by doing a basic sanity check (`python3 -c
'print(1 + 2)`) before accepting it, which should catch the Windows
shim.

Release Notes:

- windows: improved the behavior of Zed in situations where no global
Python installation exists.
This commit is contained in:
Cole Miller
2025-10-13 21:11:33 +00:00
committed by GitHub
parent f0d097c66a
commit 6a1648825c
4 changed files with 135 additions and 87 deletions
+25 -3
View File
@@ -23,6 +23,7 @@ use serde_json::{Value, json};
use smol::lock::OnceCell;
use std::cmp::Ordering;
use std::env::consts;
use util::command::new_smol_command;
use util::fs::{make_file_executable, remove_matching};
use util::rel_path::RelPath;
@@ -1332,7 +1333,13 @@ impl PyLspAdapter {
async fn ensure_venv(delegate: &dyn LspAdapterDelegate) -> Result<Arc<Path>> {
let python_path = Self::find_base_python(delegate)
.await
.context("Could not find Python installation for PyLSP")?;
.with_context(|| {
let mut message = "Could not find Python installation for PyLSP".to_owned();
if cfg!(windows){
message.push_str(". Install Python from the Microsoft Store, or manually from https://www.python.org/downloads/windows.")
}
message
})?;
let work_dir = delegate
.language_server_download_dir(&Self::SERVER_NAME)
.await
@@ -1355,9 +1362,24 @@ impl PyLspAdapter {
// Find "baseline", user python version from which we'll create our own venv.
async fn find_base_python(delegate: &dyn LspAdapterDelegate) -> Option<PathBuf> {
for path in ["python3", "python"] {
if let Some(path) = delegate.which(path.as_ref()).await {
return Some(path);
let Some(path) = delegate.which(path.as_ref()).await else {
continue;
};
// Try to detect situations where `python3` exists but is not a real Python interpreter.
// Notably, on fresh Windows installs, `python3` is a shim that opens the Microsoft Store app
// when run with no arguments, and just fails otherwise.
let Some(output) = new_smol_command(&path)
.args(["-c", "print(1 + 2)"])
.output()
.await
.ok()
else {
continue;
};
if output.stdout.trim_ascii() != b"3" {
continue;
}
return Some(path);
}
None
}