mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 05:15:00 +00:00
Move puffin-installer
to its own crate (#23)
This commit is contained in:
parent
f395c9c98c
commit
ff8e24a621
9 changed files with 105 additions and 63 deletions
|
@ -8,6 +8,10 @@ Command-line interface for the Puffin package manager.
|
|||
|
||||
Client for interacting with PyPI-compatible HTTP APIs.
|
||||
|
||||
## [puffin-installer](./puffin-installer)
|
||||
|
||||
Functionality for installing Python packages into a virtual environment.
|
||||
|
||||
## [puffin-interpreter](./puffin-interpreter)
|
||||
|
||||
Functionality for detecting and leveraging the current Python interpreter.
|
||||
|
@ -20,6 +24,6 @@ Types and functionality for working with Python packages, e.g., parsing wheel fi
|
|||
|
||||
Functionality for detecting the current platform (operating system, architecture, etc.).
|
||||
|
||||
## [puffin-resolve](./puffin-resolve)
|
||||
## [puffin-resolver](./puffin-resolver)
|
||||
|
||||
Functionality for resolving Python packages and their dependencies.
|
||||
|
|
|
@ -5,10 +5,11 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
puffin-client = { path = "../puffin-client" }
|
||||
puffin-installer = { path = "../puffin-installer" }
|
||||
puffin-interpreter = { path = "../puffin-interpreter" }
|
||||
puffin-platform = { path = "../puffin-platform" }
|
||||
puffin-package = { path = "../puffin-package" }
|
||||
puffin-resolve = { path = "../puffin-resolve" }
|
||||
puffin-resolver = { path = "../puffin-resolver" }
|
||||
|
||||
anyhow = { version = "1.0.75" }
|
||||
async-std = { version = "1.12.0", features = [
|
||||
|
@ -26,6 +27,5 @@ pep440_rs = { version = "0.3.12" }
|
|||
tracing = { version = "0.1.37" }
|
||||
tracing-tree = { version = "0.2.5" }
|
||||
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
|
||||
rayon = { version = "1.8.0" }
|
||||
url = { version = "2.4.1" }
|
||||
tempfile = { version = "3.8.0" }
|
||||
|
|
|
@ -8,7 +8,6 @@ use puffin_client::PypiClientBuilder;
|
|||
use puffin_interpreter::PythonExecutable;
|
||||
use puffin_platform::tags::Tags;
|
||||
use puffin_platform::Platform;
|
||||
use puffin_resolve::resolve;
|
||||
|
||||
use crate::commands::ExitStatus;
|
||||
|
||||
|
@ -44,7 +43,7 @@ pub(crate) async fn compile(src: &Path, cache: Option<&Path>) -> Result<ExitStat
|
|||
};
|
||||
|
||||
// Resolve the dependencies.
|
||||
let resolution = resolve(&requirements, markers, &tags, &client).await?;
|
||||
let resolution = puffin_resolver::resolve(&requirements, markers, &tags, &client).await?;
|
||||
|
||||
for (name, package) in resolution.iter() {
|
||||
#[allow(clippy::print_stdout)]
|
||||
|
|
|
@ -2,16 +2,12 @@ use std::path::Path;
|
|||
use std::str::FromStr;
|
||||
|
||||
use anyhow::Result;
|
||||
use async_std::fs::File;
|
||||
use install_wheel_rs::{install_wheel, InstallLocation};
|
||||
use tracing::debug;
|
||||
use url::Url;
|
||||
|
||||
use puffin_client::PypiClientBuilder;
|
||||
use puffin_interpreter::PythonExecutable;
|
||||
use puffin_platform::tags::Tags;
|
||||
use puffin_platform::Platform;
|
||||
use puffin_resolve::resolve;
|
||||
|
||||
use crate::commands::ExitStatus;
|
||||
|
||||
|
@ -48,45 +44,11 @@ pub(crate) async fn install(src: &Path, cache: Option<&Path>) -> Result<ExitStat
|
|||
|
||||
// Resolve the dependencies.
|
||||
// TODO(charlie): When installing, assume `--no-deps`.
|
||||
let resolution = resolve(&requirements, markers, &tags, &client).await?;
|
||||
let resolution = puffin_resolver::resolve(&requirements, markers, &tags, &client).await?;
|
||||
|
||||
// Create a temporary directory, in which we'll store the wheels.
|
||||
let tmp_dir = tempfile::tempdir()?;
|
||||
|
||||
// Download each wheel.
|
||||
// TODO(charlie): Store these in a content-addressed cache.
|
||||
// TODO(charlie): Use channels to efficiently stream-and-install.
|
||||
for (name, package) in resolution.iter() {
|
||||
let url = Url::parse(package.url())?;
|
||||
let reader = client.stream_external(&url).await?;
|
||||
|
||||
// TODO(charlie): Stream the unzip.
|
||||
let mut writer = File::create(tmp_dir.path().join(format!("{name}.whl"))).await?;
|
||||
async_std::io::copy(reader, &mut writer).await?;
|
||||
}
|
||||
|
||||
// Install each wheel.
|
||||
// TODO(charlie): Use channels to efficiently stream-and-install.
|
||||
let location = InstallLocation::Venv {
|
||||
venv_base: python.venv().to_path_buf(),
|
||||
python_version: python.simple_version(),
|
||||
};
|
||||
let locked_dir = location.acquire_lock()?;
|
||||
for (name, package) in resolution.iter() {
|
||||
let path = tmp_dir.path().join(format!("{name}.whl"));
|
||||
let filename = install_wheel_rs::WheelFilename::from_str(package.filename())?;
|
||||
|
||||
// TODO(charlie): Should this be async?
|
||||
install_wheel(
|
||||
&locked_dir,
|
||||
std::fs::File::open(path)?,
|
||||
filename,
|
||||
false,
|
||||
&[],
|
||||
"",
|
||||
python.executable(),
|
||||
)?;
|
||||
}
|
||||
// Install into the current environment.
|
||||
let wheels = resolution.into_files().collect::<Vec<_>>();
|
||||
puffin_installer::install(&wheels, &python, &client).await?;
|
||||
|
||||
Ok(ExitStatus::Success)
|
||||
}
|
||||
|
|
25
crates/puffin-installer/Cargo.toml
Normal file
25
crates/puffin-installer/Cargo.toml
Normal file
|
@ -0,0 +1,25 @@
|
|||
[package]
|
||||
name = "puffin-installer"
|
||||
version = "0.1.0"
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
repository.workspace = true
|
||||
authors.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
puffin-client = { path = "../puffin-client" }
|
||||
puffin-interpreter = { path = "../puffin-interpreter" }
|
||||
|
||||
anyhow = { version = "1.0.75" }
|
||||
async-std = { version = "1.12.0", features = [
|
||||
"attributes",
|
||||
"tokio1",
|
||||
"unstable",
|
||||
] }
|
||||
install-wheel-rs = { version = "0.0.1" }
|
||||
tempfile = { version = "3.8.0" }
|
||||
tracing = { version = "0.1.37" }
|
||||
url = { version = "2.4.1" }
|
56
crates/puffin-installer/src/lib.rs
Normal file
56
crates/puffin-installer/src/lib.rs
Normal file
|
@ -0,0 +1,56 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use anyhow::Result;
|
||||
use install_wheel_rs::{install_wheel, InstallLocation};
|
||||
use url::Url;
|
||||
|
||||
use puffin_client::{File, PypiClient};
|
||||
use puffin_interpreter::PythonExecutable;
|
||||
|
||||
/// Install a set of wheels into a Python virtual environment.
|
||||
pub async fn install(
|
||||
wheels: &[File],
|
||||
python: &PythonExecutable,
|
||||
client: &PypiClient,
|
||||
) -> Result<()> {
|
||||
// Create a temporary directory, in which we'll store the wheels.
|
||||
let tmp_dir = tempfile::tempdir()?;
|
||||
|
||||
// Download each wheel.
|
||||
// TODO(charlie): Store these in a content-addressed cache.
|
||||
// TODO(charlie): Use channels to efficiently stream-and-install.
|
||||
for wheel in wheels {
|
||||
let url = Url::parse(&wheel.url)?;
|
||||
let reader = client.stream_external(&url).await?;
|
||||
|
||||
// TODO(charlie): Stream the unzip.
|
||||
let mut writer =
|
||||
async_std::fs::File::create(tmp_dir.path().join(&wheel.hashes.sha256)).await?;
|
||||
async_std::io::copy(reader, &mut writer).await?;
|
||||
}
|
||||
|
||||
// Install each wheel.
|
||||
// TODO(charlie): Use channels to efficiently stream-and-install.
|
||||
let location = InstallLocation::Venv {
|
||||
venv_base: python.venv().to_path_buf(),
|
||||
python_version: python.simple_version(),
|
||||
};
|
||||
let locked_dir = location.acquire_lock()?;
|
||||
for wheel in wheels {
|
||||
let path = tmp_dir.path().join(&wheel.hashes.sha256);
|
||||
let filename = install_wheel_rs::WheelFilename::from_str(&wheel.filename)?;
|
||||
|
||||
// TODO(charlie): Should this be async?
|
||||
install_wheel(
|
||||
&locked_dir,
|
||||
std::fs::File::open(path)?,
|
||||
filename,
|
||||
false,
|
||||
&[],
|
||||
"",
|
||||
python.executable(),
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
[package]
|
||||
name = "puffin-resolve"
|
||||
name = "puffin-resolver"
|
||||
version = "0.1.0"
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
@ -11,15 +11,9 @@ license.workspace = true
|
|||
|
||||
[dependencies]
|
||||
puffin-client = { path = "../puffin-client" }
|
||||
puffin-interpreter = { path = "../puffin-interpreter" }
|
||||
puffin-platform = { path = "../puffin-platform" }
|
||||
puffin-package = { path = "../puffin-package" }
|
||||
|
||||
async-std = { version = "1.12.0", features = [
|
||||
"attributes",
|
||||
"tokio1",
|
||||
"unstable",
|
||||
] }
|
||||
pep440_rs = "0.3.12"
|
||||
futures = "0.3.28"
|
||||
anyhow = "1.0.75"
|
|
@ -19,9 +19,15 @@ use puffin_platform::tags::Tags;
|
|||
pub struct Resolution(HashMap<PackageName, PinnedPackage>);
|
||||
|
||||
impl Resolution {
|
||||
/// Iterate over the pinned packages in this resolution.
|
||||
pub fn iter(&self) -> impl Iterator<Item = (&PackageName, &PinnedPackage)> {
|
||||
self.0.iter()
|
||||
}
|
||||
|
||||
/// Iterate over the wheels in this resolution.
|
||||
pub fn into_files(self) -> impl Iterator<Item = File> {
|
||||
self.0.into_values().map(|package| package.file)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -31,14 +37,6 @@ pub struct PinnedPackage {
|
|||
}
|
||||
|
||||
impl PinnedPackage {
|
||||
pub fn filename(&self) -> &str {
|
||||
&self.file.filename
|
||||
}
|
||||
|
||||
pub fn url(&self) -> &str {
|
||||
&self.file.url
|
||||
}
|
||||
|
||||
pub fn version(&self) -> &Version {
|
||||
&self.metadata.version
|
||||
}
|
|
@ -1 +1,5 @@
|
|||
black
|
||||
packaging>=23.1
|
||||
pygls>=1.0.1
|
||||
lsprotocol>=2023.0.0a1
|
||||
ruff>=0.0.274
|
||||
typing_extensions
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue