debugger/setup/adapters/
debugpy.rs1use crate::common::{Error, Result};
6use crate::setup::installer::{
7 adapters_dir, ensure_adapters_dir, run_command_args, write_version_file,
8 InstallMethod, InstallOptions, InstallResult, InstallStatus, Installer,
9};
10use crate::setup::registry::{DebuggerInfo, Platform};
11use crate::setup::verifier::{verify_dap_adapter, VerifyResult};
12use async_trait::async_trait;
13use std::path::PathBuf;
14
15static INFO: DebuggerInfo = DebuggerInfo {
16 id: "python",
17 name: "debugpy",
18 languages: &["python"],
19 platforms: &[Platform::Linux, Platform::MacOS, Platform::Windows],
20 description: "Microsoft's Python debugger",
21 primary: true,
22};
23
24pub struct DebugpyInstaller;
25
26#[async_trait]
27impl Installer for DebugpyInstaller {
28 fn info(&self) -> &DebuggerInfo {
29 &INFO
30 }
31
32 async fn status(&self) -> Result<InstallStatus> {
33 let adapter_dir = adapters_dir().join("debugpy");
34 let venv_dir = adapter_dir.join("venv");
35 let python_path = get_venv_python(&venv_dir);
36
37 if python_path.exists() {
38 let check = run_command_args(
40 &python_path,
41 &["-c", "import debugpy; print(debugpy.__version__)"],
42 )
43 .await;
44
45 match check {
46 Ok(version) => {
47 return Ok(InstallStatus::Installed {
48 path: python_path,
49 version: Some(version.trim().to_string()),
50 });
51 }
52 Err(_) => {
53 return Ok(InstallStatus::Broken {
54 path: python_path,
55 reason: "debugpy module not found in venv".to_string(),
56 });
57 }
58 }
59 }
60
61 if let Ok(python_path) = which::which("python3") {
63 if let Ok(version) = run_command_args(
64 &python_path,
65 &["-c", "import debugpy; print(debugpy.__version__)"],
66 )
67 .await
68 {
69 return Ok(InstallStatus::Installed {
70 path: python_path,
71 version: Some(version.trim().to_string()),
72 });
73 }
74 }
75
76 Ok(InstallStatus::NotInstalled)
77 }
78
79 async fn best_method(&self) -> Result<InstallMethod> {
80 let python = find_python().await?;
82
83 Ok(InstallMethod::LanguagePackage {
84 tool: python.to_string_lossy().to_string(),
85 package: "debugpy".to_string(),
86 })
87 }
88
89 async fn install(&self, opts: InstallOptions) -> Result<InstallResult> {
90 install_debugpy(&opts).await
91 }
92
93 async fn uninstall(&self) -> Result<()> {
94 let adapter_dir = adapters_dir().join("debugpy");
95 if adapter_dir.exists() {
96 std::fs::remove_dir_all(&adapter_dir)?;
97 println!("Removed {}", adapter_dir.display());
98 } else {
99 println!("debugpy managed installation not found");
100 println!("If installed globally, use: pip uninstall debugpy");
101 }
102 Ok(())
103 }
104
105 async fn verify(&self) -> Result<VerifyResult> {
106 let status = self.status().await?;
107
108 match status {
109 InstallStatus::Installed { path, .. } => {
110 verify_dap_adapter(&path, &["-m".to_string(), "debugpy.adapter".to_string()]).await
112 }
113 InstallStatus::Broken { reason, .. } => Ok(VerifyResult {
114 success: false,
115 capabilities: None,
116 error: Some(reason),
117 }),
118 InstallStatus::NotInstalled => Ok(VerifyResult {
119 success: false,
120 capabilities: None,
121 error: Some("Not installed".to_string()),
122 }),
123 }
124 }
125}
126
127async fn find_python() -> Result<PathBuf> {
129 for cmd in &["python3", "python"] {
131 if let Ok(path) = which::which(cmd) {
132 let version_check = run_command_args(
134 &path,
135 &["-c", "import sys; sys.exit(0 if sys.version_info >= (3, 7) else 1)"],
136 )
137 .await;
138
139 if version_check.is_ok() {
140 return Ok(path);
141 }
142 }
143 }
144
145 Err(Error::Internal(
146 "Python 3.7+ not found. Please install Python first.".to_string(),
147 ))
148}
149
150fn get_venv_python(venv_dir: &PathBuf) -> PathBuf {
152 if cfg!(windows) {
153 venv_dir.join("Scripts").join("python.exe")
154 } else {
155 venv_dir.join("bin").join("python")
156 }
157}
158
159fn get_venv_pip(venv_dir: &PathBuf) -> PathBuf {
161 if cfg!(windows) {
162 venv_dir.join("Scripts").join("pip.exe")
163 } else {
164 venv_dir.join("bin").join("pip")
165 }
166}
167
168async fn install_debugpy(opts: &InstallOptions) -> Result<InstallResult> {
169 println!("Checking for existing installation... not found");
170
171 let python = find_python().await?;
173 println!("Using Python: {}", python.display());
174
175 let adapter_dir = ensure_adapters_dir()?.join("debugpy");
177 let venv_dir = adapter_dir.join("venv");
178
179 if opts.force && venv_dir.exists() {
181 std::fs::remove_dir_all(&venv_dir)?;
182 }
183
184 if !venv_dir.exists() {
186 println!("Creating virtual environment...");
187 run_command_args(&python, &["-m", "venv", venv_dir.to_str().unwrap_or("venv")]).await?;
188 }
189
190 let pip = get_venv_pip(&venv_dir);
192 let venv_python = get_venv_python(&venv_dir);
193
194 println!("Upgrading pip...");
196 let _ = run_command_args(&venv_python, &["-m", "pip", "install", "--upgrade", "pip"]).await;
197
198 let package = if let Some(version) = &opts.version {
200 format!("debugpy=={}", version)
201 } else {
202 "debugpy".to_string()
203 };
204
205 println!("Installing {}...", package);
206 run_command_args(&pip, &["install", &package]).await?;
207
208 let version = run_command_args(
210 &venv_python,
211 &["-c", "import debugpy; print(debugpy.__version__)"],
212 )
213 .await
214 .ok()
215 .map(|s| s.trim().to_string());
216
217 if let Some(v) = &version {
219 write_version_file(&adapter_dir, v)?;
220 }
221
222 println!("Setting permissions... done");
223 println!("Verifying installation...");
224
225 Ok(InstallResult {
226 path: venv_python,
227 version,
228 args: vec!["-m".to_string(), "debugpy.adapter".to_string()],
229 })
230}