remove PyO3 dep (#4)

* remove PyO3 dep

* swap pyo3 for which
This commit is contained in:
Josh Thomas 2024-12-06 00:03:23 -06:00 committed by GitHub
parent 7e899a0778
commit 39523d1f89
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 46 additions and 159 deletions

View file

@ -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"

View file

@ -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"

View file

@ -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),

View file

@ -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),
} }

View file

@ -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()?;
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> { 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()
})
} }
} }

View file

@ -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 }