mirror of
https://github.com/joshuadavidthomas/django-language-server.git
synced 2025-08-03 17:48:20 +00:00
parent
7e899a0778
commit
39523d1f89
6 changed files with 46 additions and 159 deletions
|
@ -7,6 +7,5 @@ djls = { path = "crates/djls" }
|
|||
djls-python = { path = "crates/djls-python" }
|
||||
|
||||
anyhow = "1.0.94"
|
||||
pyo3 = { version = "0.23.3", features = ["auto-initialize", "abi3-py39"] }
|
||||
serde = { version = "1.0.215", features = ["derive"] }
|
||||
serde_json = "1.0.133"
|
||||
|
|
|
@ -4,8 +4,8 @@ version = "0.0.0"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
pyo3 = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
|
||||
thiserror = "2.0.4"
|
||||
which = "7.0.0"
|
||||
|
|
|
@ -1,51 +1,32 @@
|
|||
use crate::python::{Interpreter, PythonError};
|
||||
use pyo3::prelude::*;
|
||||
use std::fmt;
|
||||
use std::path::PathBuf;
|
||||
use which::which;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PythonEnvironment {
|
||||
root: PathBuf,
|
||||
build: Interpreter,
|
||||
runtime: Interpreter,
|
||||
py: Interpreter,
|
||||
}
|
||||
|
||||
impl PythonEnvironment {
|
||||
fn new(root: PathBuf, build: Interpreter, runtime: Interpreter) -> Self {
|
||||
Self {
|
||||
root,
|
||||
build,
|
||||
runtime,
|
||||
}
|
||||
fn new(root: PathBuf, py: Interpreter) -> Self {
|
||||
Self { root, py }
|
||||
}
|
||||
|
||||
pub fn initialize() -> Result<Self, EnvironmentError> {
|
||||
Python::with_gil(|py| {
|
||||
let initial_build = Interpreter::for_build(py)?;
|
||||
let runtime = Interpreter::for_runtime(initial_build.sys_executable())?;
|
||||
let root = runtime.sys_prefix().clone();
|
||||
|
||||
let runtime_project_paths = runtime.project_paths();
|
||||
for path in runtime_project_paths {
|
||||
initial_build.add_to_path(py, path)?;
|
||||
}
|
||||
|
||||
let final_build = initial_build.refresh_state(py)?;
|
||||
|
||||
Ok(Self::new(root, final_build, runtime))
|
||||
})
|
||||
let executable = which("python")?;
|
||||
let py = Interpreter::from_sys_executable(&executable)?;
|
||||
let root = py.sys_prefix().clone();
|
||||
Ok(Self::new(root, py))
|
||||
}
|
||||
|
||||
pub fn root(&self) -> &PathBuf {
|
||||
&self.root
|
||||
}
|
||||
|
||||
pub fn build(&self) -> &Interpreter {
|
||||
&self.build
|
||||
}
|
||||
|
||||
pub fn runtime(&self) -> &Interpreter {
|
||||
&self.runtime
|
||||
pub fn py(&self) -> &Interpreter {
|
||||
&self.py
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,18 +35,15 @@ impl fmt::Display for PythonEnvironment {
|
|||
writeln!(f, "Python Environment")?;
|
||||
writeln!(f, "Root: {}", self.root.display())?;
|
||||
writeln!(f)?;
|
||||
writeln!(f, "Build Interpreter")?;
|
||||
writeln!(f, "{}", self.build)?;
|
||||
writeln!(f)?;
|
||||
writeln!(f, "Runtime Interpreter")?;
|
||||
write!(f, "{}", self.runtime)
|
||||
writeln!(f, "Interpreter")?;
|
||||
writeln!(f, "{}", self.py)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum EnvironmentError {
|
||||
#[error("Python error: {0}")]
|
||||
Python(#[from] PyErr),
|
||||
#[error("Failed to locate Python executable: {0}")]
|
||||
PythonNotFound(#[from] which::Error),
|
||||
|
||||
#[error("Runtime error: {0}")]
|
||||
Runtime(#[from] PythonError),
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
use pyo3::prelude::*;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
@ -51,38 +50,6 @@ impl Packages {
|
|||
Self(HashMap::new())
|
||||
}
|
||||
|
||||
pub fn from_python(py: Python) -> PyResult<Self> {
|
||||
let importlib_metadata = py.import("importlib.metadata")?;
|
||||
let distributions = importlib_metadata.call_method0("distributions")?;
|
||||
|
||||
let mut packages = Packages::new();
|
||||
|
||||
for dist in (distributions.try_iter()?).flatten() {
|
||||
if let Ok(metadata) = dist.getattr("metadata") {
|
||||
if let (Ok(name), Ok(version)) = (
|
||||
metadata.get_item("Name")?.extract::<String>(),
|
||||
dist.getattr("version")?.extract::<String>(),
|
||||
) {
|
||||
let location = match dist.call_method1("locate_file", ("",)) {
|
||||
Ok(path) => path
|
||||
.getattr("parent")?
|
||||
.call_method0("as_posix")?
|
||||
.extract::<String>()
|
||||
.ok()
|
||||
.map(PathBuf::from),
|
||||
Err(_) => None,
|
||||
};
|
||||
|
||||
packages
|
||||
.0
|
||||
.insert(name.clone(), Package::new(name, version, location));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(packages)
|
||||
}
|
||||
|
||||
pub fn from_executable(executable: &Path) -> Result<Self, PackagingError> {
|
||||
let output = Command::new(executable)
|
||||
.args([
|
||||
|
@ -126,6 +93,10 @@ print(json.dumps(packages))
|
|||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
pub fn has_package(&self, name: &str) -> bool {
|
||||
self.0.iter().any(|pkg| pkg.1.name == name)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<(String, Package)> for Packages {
|
||||
|
@ -158,9 +129,6 @@ pub enum PackagingError {
|
|||
#[error("JSON parsing error: {0}")]
|
||||
Json(#[from] serde_json::Error),
|
||||
|
||||
#[error("Python error: {0}")]
|
||||
Python(#[from] PyErr),
|
||||
|
||||
#[error("UTF-8 conversion error: {0}")]
|
||||
Utf8(#[from] std::string::FromUtf8Error),
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
use crate::packaging::{Packages, PackagingError};
|
||||
use pyo3::prelude::*;
|
||||
use serde::Deserialize;
|
||||
use std::fmt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
|
@ -23,17 +22,6 @@ impl VersionInfo {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn from_python(py: Python) -> PyResult<Self> {
|
||||
let version_info = py.version_info();
|
||||
|
||||
Ok(Self::new(
|
||||
version_info.major,
|
||||
version_info.minor,
|
||||
version_info.patch,
|
||||
version_info.suffix.map(String::from),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn from_executable(executable: &PathBuf) -> Result<Self, PythonError> {
|
||||
let output = Command::new(executable)
|
||||
.args(["-c", "import sys; print(sys.version.split()[0])"])
|
||||
|
@ -81,22 +69,6 @@ pub struct SysconfigPaths {
|
|||
}
|
||||
|
||||
impl SysconfigPaths {
|
||||
pub fn from_python(py: Python) -> PyResult<Self> {
|
||||
let sysconfig = py.import("sysconfig")?;
|
||||
let paths = sysconfig.call_method0("get_paths")?;
|
||||
|
||||
Ok(Self {
|
||||
data: PathBuf::from(paths.get_item("data").unwrap().extract::<String>()?),
|
||||
include: PathBuf::from(paths.get_item("include").unwrap().extract::<String>()?),
|
||||
platinclude: PathBuf::from(paths.get_item("platinclude").unwrap().extract::<String>()?),
|
||||
platlib: PathBuf::from(paths.get_item("platlib").unwrap().extract::<String>()?),
|
||||
platstdlib: PathBuf::from(paths.get_item("platstdlib").unwrap().extract::<String>()?),
|
||||
purelib: PathBuf::from(paths.get_item("purelib").unwrap().extract::<String>()?),
|
||||
scripts: PathBuf::from(paths.get_item("scripts").unwrap().extract::<String>()?),
|
||||
stdlib: PathBuf::from(paths.get_item("stdlib").unwrap().extract::<String>()?),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn from_executable(executable: &PathBuf) -> Result<Self, PythonError> {
|
||||
let output = Command::new(executable)
|
||||
.args([
|
||||
|
@ -160,25 +132,7 @@ impl Interpreter {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn for_build(py: Python) -> PyResult<Self> {
|
||||
let sys = py.import("sys")?;
|
||||
|
||||
Ok(Self::new(
|
||||
VersionInfo::from_python(py)?,
|
||||
SysconfigPaths::from_python(py)?,
|
||||
PathBuf::from(sys.getattr("prefix")?.extract::<String>()?),
|
||||
PathBuf::from(sys.getattr("base_prefix")?.extract::<String>()?),
|
||||
PathBuf::from(sys.getattr("executable")?.extract::<String>()?),
|
||||
sys.getattr("path")?
|
||||
.extract::<Vec<String>>()?
|
||||
.into_iter()
|
||||
.map(PathBuf::from)
|
||||
.collect(),
|
||||
Packages::from_python(py)?,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn for_runtime(executable: &PathBuf) -> Result<Self, PythonError> {
|
||||
pub fn from_sys_executable(executable: &PathBuf) -> Result<Self, PythonError> {
|
||||
let output = Command::new(executable)
|
||||
.args([
|
||||
"-c",
|
||||
|
@ -241,49 +195,38 @@ print(json.dumps({
|
|||
&self.packages
|
||||
}
|
||||
|
||||
pub fn project_paths(&self) -> Vec<&PathBuf> {
|
||||
let mut paths: Vec<&PathBuf> = self
|
||||
.sys_path
|
||||
.iter()
|
||||
.filter(|path| {
|
||||
path.starts_with(&self.sys_prefix) && !path.starts_with(&self.sys_base_prefix)
|
||||
})
|
||||
.collect();
|
||||
pub fn run_python(&self, code: &str) -> std::io::Result<String> {
|
||||
let output = Command::new(self.sys_executable())
|
||||
.args(["-c", code])
|
||||
.output()?;
|
||||
|
||||
if let Some(project_path) = self.sys_path.last() {
|
||||
if !paths.contains(&project_path) {
|
||||
paths.push(project_path);
|
||||
}
|
||||
if !output.status.success() {
|
||||
let error = String::from_utf8_lossy(&output.stderr);
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!("Python execution failed: {}", error),
|
||||
));
|
||||
}
|
||||
|
||||
paths
|
||||
Ok(String::from_utf8_lossy(&output.stdout).into_owned())
|
||||
}
|
||||
|
||||
pub fn add_to_path(&self, py: Python, path: &Path) -> PyResult<()> {
|
||||
if let Some(path_str) = path.to_str() {
|
||||
let sys = py.import("sys")?;
|
||||
let sys_path = sys.getattr("path")?;
|
||||
sys_path.call_method1("append", (path_str,))?;
|
||||
pub fn run(&self, command: &str, args: &[&str]) -> std::io::Result<String> {
|
||||
let output = Command::new(self.sys_executable())
|
||||
.arg("-m")
|
||||
.arg(command)
|
||||
.args(args)
|
||||
.output()?;
|
||||
|
||||
if !output.status.success() {
|
||||
let error = String::from_utf8_lossy(&output.stderr);
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!("Command failed: {}", error),
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn refresh_state(&self, py: Python) -> PyResult<Self> {
|
||||
let sys = py.import("sys")?;
|
||||
let new_sys_path: Vec<PathBuf> = sys
|
||||
.getattr("path")?
|
||||
.extract::<Vec<String>>()?
|
||||
.into_iter()
|
||||
.map(PathBuf::from)
|
||||
.collect();
|
||||
|
||||
let new_packages = Packages::from_python(py)?;
|
||||
|
||||
Ok(Self {
|
||||
sys_path: new_sys_path,
|
||||
packages: new_packages,
|
||||
..self.clone()
|
||||
})
|
||||
Ok(String::from_utf8_lossy(&output.stdout).into_owned())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ edition = "2021"
|
|||
djls-python = { workspace = true }
|
||||
|
||||
anyhow = { workspace = true }
|
||||
pyo3 = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue