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

View file

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

View file

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

View file

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

View file

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

View file

@ -7,7 +7,6 @@ edition = "2021"
djls-python = { workspace = true }
anyhow = { workspace = true }
pyo3 = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }