mirror of
https://github.com/joshuadavidthomas/django-language-server.git
synced 2025-09-27 20:42:14 +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" }
|
djls-python = { path = "crates/djls-python" }
|
||||||
|
|
||||||
anyhow = "1.0.94"
|
anyhow = "1.0.94"
|
||||||
pyo3 = { version = "0.23.3", features = ["auto-initialize", "abi3-py39"] }
|
|
||||||
serde = { version = "1.0.215", features = ["derive"] }
|
serde = { version = "1.0.215", features = ["derive"] }
|
||||||
serde_json = "1.0.133"
|
serde_json = "1.0.133"
|
||||||
|
|
|
@ -4,8 +4,8 @@ version = "0.0.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
pyo3 = { workspace = true }
|
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
|
|
||||||
thiserror = "2.0.4"
|
thiserror = "2.0.4"
|
||||||
|
which = "7.0.0"
|
||||||
|
|
|
@ -1,51 +1,32 @@
|
||||||
use crate::python::{Interpreter, PythonError};
|
use crate::python::{Interpreter, PythonError};
|
||||||
use pyo3::prelude::*;
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use which::which;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PythonEnvironment {
|
pub struct PythonEnvironment {
|
||||||
root: PathBuf,
|
root: PathBuf,
|
||||||
build: Interpreter,
|
py: Interpreter,
|
||||||
runtime: Interpreter,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PythonEnvironment {
|
impl PythonEnvironment {
|
||||||
fn new(root: PathBuf, build: Interpreter, runtime: Interpreter) -> Self {
|
fn new(root: PathBuf, py: Interpreter) -> Self {
|
||||||
Self {
|
Self { root, py }
|
||||||
root,
|
|
||||||
build,
|
|
||||||
runtime,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn initialize() -> Result<Self, EnvironmentError> {
|
pub fn initialize() -> Result<Self, EnvironmentError> {
|
||||||
Python::with_gil(|py| {
|
let executable = which("python")?;
|
||||||
let initial_build = Interpreter::for_build(py)?;
|
let py = Interpreter::from_sys_executable(&executable)?;
|
||||||
let runtime = Interpreter::for_runtime(initial_build.sys_executable())?;
|
let root = py.sys_prefix().clone();
|
||||||
let root = runtime.sys_prefix().clone();
|
Ok(Self::new(root, py))
|
||||||
|
|
||||||
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))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn root(&self) -> &PathBuf {
|
pub fn root(&self) -> &PathBuf {
|
||||||
&self.root
|
&self.root
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(&self) -> &Interpreter {
|
pub fn py(&self) -> &Interpreter {
|
||||||
&self.build
|
&self.py
|
||||||
}
|
|
||||||
|
|
||||||
pub fn runtime(&self) -> &Interpreter {
|
|
||||||
&self.runtime
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,18 +35,15 @@ impl fmt::Display for PythonEnvironment {
|
||||||
writeln!(f, "Python Environment")?;
|
writeln!(f, "Python Environment")?;
|
||||||
writeln!(f, "Root: {}", self.root.display())?;
|
writeln!(f, "Root: {}", self.root.display())?;
|
||||||
writeln!(f)?;
|
writeln!(f)?;
|
||||||
writeln!(f, "Build Interpreter")?;
|
writeln!(f, "Interpreter")?;
|
||||||
writeln!(f, "{}", self.build)?;
|
writeln!(f, "{}", self.py)
|
||||||
writeln!(f)?;
|
|
||||||
writeln!(f, "Runtime Interpreter")?;
|
|
||||||
write!(f, "{}", self.runtime)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum EnvironmentError {
|
pub enum EnvironmentError {
|
||||||
#[error("Python error: {0}")]
|
#[error("Failed to locate Python executable: {0}")]
|
||||||
Python(#[from] PyErr),
|
PythonNotFound(#[from] which::Error),
|
||||||
|
|
||||||
#[error("Runtime error: {0}")]
|
#[error("Runtime error: {0}")]
|
||||||
Runtime(#[from] PythonError),
|
Runtime(#[from] PythonError),
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use pyo3::prelude::*;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
@ -51,38 +50,6 @@ impl Packages {
|
||||||
Self(HashMap::new())
|
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> {
|
pub fn from_executable(executable: &Path) -> Result<Self, PackagingError> {
|
||||||
let output = Command::new(executable)
|
let output = Command::new(executable)
|
||||||
.args([
|
.args([
|
||||||
|
@ -126,6 +93,10 @@ print(json.dumps(packages))
|
||||||
})
|
})
|
||||||
.collect())
|
.collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn has_package(&self, name: &str) -> bool {
|
||||||
|
self.0.iter().any(|pkg| pkg.1.name == name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromIterator<(String, Package)> for Packages {
|
impl FromIterator<(String, Package)> for Packages {
|
||||||
|
@ -158,9 +129,6 @@ pub enum PackagingError {
|
||||||
#[error("JSON parsing error: {0}")]
|
#[error("JSON parsing error: {0}")]
|
||||||
Json(#[from] serde_json::Error),
|
Json(#[from] serde_json::Error),
|
||||||
|
|
||||||
#[error("Python error: {0}")]
|
|
||||||
Python(#[from] PyErr),
|
|
||||||
|
|
||||||
#[error("UTF-8 conversion error: {0}")]
|
#[error("UTF-8 conversion error: {0}")]
|
||||||
Utf8(#[from] std::string::FromUtf8Error),
|
Utf8(#[from] std::string::FromUtf8Error),
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
use crate::packaging::{Packages, PackagingError};
|
use crate::packaging::{Packages, PackagingError};
|
||||||
use pyo3::prelude::*;
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::PathBuf;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
#[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> {
|
pub fn from_executable(executable: &PathBuf) -> Result<Self, PythonError> {
|
||||||
let output = Command::new(executable)
|
let output = Command::new(executable)
|
||||||
.args(["-c", "import sys; print(sys.version.split()[0])"])
|
.args(["-c", "import sys; print(sys.version.split()[0])"])
|
||||||
|
@ -81,22 +69,6 @@ pub struct SysconfigPaths {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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> {
|
pub fn from_executable(executable: &PathBuf) -> Result<Self, PythonError> {
|
||||||
let output = Command::new(executable)
|
let output = Command::new(executable)
|
||||||
.args([
|
.args([
|
||||||
|
@ -160,25 +132,7 @@ impl Interpreter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn for_build(py: Python) -> PyResult<Self> {
|
pub fn from_sys_executable(executable: &PathBuf) -> Result<Self, PythonError> {
|
||||||
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> {
|
|
||||||
let output = Command::new(executable)
|
let output = Command::new(executable)
|
||||||
.args([
|
.args([
|
||||||
"-c",
|
"-c",
|
||||||
|
@ -241,49 +195,38 @@ print(json.dumps({
|
||||||
&self.packages
|
&self.packages
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn project_paths(&self) -> Vec<&PathBuf> {
|
pub fn run_python(&self, code: &str) -> std::io::Result<String> {
|
||||||
let mut paths: Vec<&PathBuf> = self
|
let output = Command::new(self.sys_executable())
|
||||||
.sys_path
|
.args(["-c", code])
|
||||||
.iter()
|
.output()?;
|
||||||
.filter(|path| {
|
|
||||||
path.starts_with(&self.sys_prefix) && !path.starts_with(&self.sys_base_prefix)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
if let Some(project_path) = self.sys_path.last() {
|
if !output.status.success() {
|
||||||
if !paths.contains(&project_path) {
|
let error = String::from_utf8_lossy(&output.stderr);
|
||||||
paths.push(project_path);
|
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<()> {
|
pub fn run(&self, command: &str, args: &[&str]) -> std::io::Result<String> {
|
||||||
if let Some(path_str) = path.to_str() {
|
let output = Command::new(self.sys_executable())
|
||||||
let sys = py.import("sys")?;
|
.arg("-m")
|
||||||
let sys_path = sys.getattr("path")?;
|
.arg(command)
|
||||||
sys_path.call_method1("append", (path_str,))?;
|
.args(args)
|
||||||
}
|
.output()?;
|
||||||
Ok(())
|
|
||||||
|
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),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn refresh_state(&self, py: Python) -> PyResult<Self> {
|
Ok(String::from_utf8_lossy(&output.stdout).into_owned())
|
||||||
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()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@ edition = "2021"
|
||||||
djls-python = { workspace = true }
|
djls-python = { workspace = true }
|
||||||
|
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
pyo3 = { workspace = true }
|
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue