Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split install plan into builder and struct #955

Merged
merged 1 commit into from
Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 7 additions & 14 deletions crates/puffin-cli/src/commands/pip_install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use puffin_cache::Cache;
use puffin_client::{FlatIndex, FlatIndexClient, RegistryClient, RegistryClientBuilder};
use puffin_dispatch::BuildDispatch;
use puffin_installer::{
BuiltEditable, Downloader, InstallPlan, Reinstall, ResolvedEditable, SitePackages,
BuiltEditable, Downloader, Plan, Planner, Reinstall, ResolvedEditable, SitePackages,
};
use puffin_interpreter::{Interpreter, Virtualenv};
use puffin_normalize::PackageName;
Expand Down Expand Up @@ -470,22 +470,16 @@ async fn install(
.into_iter()
.map(ResolvedEditable::Built)
.collect::<Vec<_>>();
let InstallPlan {

let Plan {
local,
remote,
reinstalls,
extraneous: _,
} = InstallPlan::from_requirements(
&requirements,
editables,
site_packages,
reinstall,
index_urls,
cache,
venv,
tags,
)
.context("Failed to determine installation plan")?;
} = Planner::with_requirements(&requirements)
.with_editable_requirements(editables)
.build(site_packages, reinstall, index_urls, cache, venv, tags)
.context("Failed to determine installation plan")?;

// Nothing to do.
if remote.is_empty() && local.is_empty() && reinstalls.is_empty() {
Expand Down Expand Up @@ -524,7 +518,6 @@ async fn install(
let downloader = Downloader::new(cache, tags, client, build_dispatch)
.with_reporter(DownloadReporter::from(printer).with_length(remote.len() as u64));

// STOPSHIP(charlie): This needs to be shared!
let wheels = downloader
.download(remote, in_flight)
.await
Expand Down
26 changes: 13 additions & 13 deletions crates/puffin-cli/src/commands/pip_sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use platform_tags::Tags;
use puffin_cache::Cache;
use puffin_client::{FlatIndex, FlatIndexClient, RegistryClient, RegistryClientBuilder};
use puffin_dispatch::BuildDispatch;
use puffin_installer::{Downloader, InstallPlan, Reinstall, ResolvedEditable, SitePackages};
use puffin_installer::{Downloader, Plan, Planner, Reinstall, ResolvedEditable, SitePackages};
use puffin_interpreter::Virtualenv;
use puffin_resolver::InMemoryIndex;
use puffin_traits::{InFlight, SetupPyStrategy};
Expand Down Expand Up @@ -111,22 +111,22 @@ pub(crate) async fn pip_sync(

// Partition into those that should be linked from the cache (`local`), those that need to be
// downloaded (`remote`), and those that should be removed (`extraneous`).
let InstallPlan {
let Plan {
local,
remote,
reinstalls,
extraneous,
} = InstallPlan::from_requirements(
&requirements,
resolved_editables.editables,
site_packages,
reinstall,
&index_locations,
&cache,
&venv,
tags,
)
.context("Failed to determine installation plan")?;
} = Planner::with_requirements(&requirements)
.with_editable_requirements(resolved_editables.editables)
.build(
site_packages,
reinstall,
&index_locations,
&cache,
&venv,
tags,
)
.context("Failed to determine installation plan")?;

// Nothing to do.
if remote.is_empty() && local.is_empty() && reinstalls.is_empty() && extraneous.is_empty() {
Expand Down
8 changes: 3 additions & 5 deletions crates/puffin-dispatch/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use pep508_rs::Requirement;
use puffin_build::{SourceBuild, SourceBuildContext};
use puffin_cache::Cache;
use puffin_client::{FlatIndex, RegistryClient};
use puffin_installer::{Downloader, InstallPlan, Installer, Reinstall, SitePackages};
use puffin_installer::{Downloader, Installer, Plan, Planner, Reinstall, SitePackages};
use puffin_interpreter::{Interpreter, Virtualenv};
use puffin_resolver::{InMemoryIndex, Manifest, ResolutionOptions, Resolver};
use puffin_traits::{BuildContext, BuildKind, InFlight, SetupPyStrategy};
Expand Down Expand Up @@ -149,14 +149,12 @@ impl<'a> BuildContext for BuildDispatch<'a> {
let site_packages =
SitePackages::from_executable(venv).context("Failed to list installed packages")?;

let InstallPlan {
let Plan {
local,
remote,
reinstalls,
extraneous,
} = InstallPlan::from_requirements(
&resolution.requirements(),
Vec::new(),
} = Planner::with_requirements(&resolution.requirements()).build(
site_packages,
&Reinstall::None,
self.index_locations,
Expand Down
2 changes: 1 addition & 1 deletion crates/puffin-installer/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
pub use downloader::{Downloader, Reporter as DownloadReporter};
pub use editable::{BuiltEditable, ResolvedEditable};
pub use installer::{Installer, Reporter as InstallReporter};
pub use plan::{InstallPlan, Reinstall};
pub use plan::{Plan, Planner, Reinstall};
pub use site_packages::SitePackages;
pub use uninstall::uninstall;

Expand Down
79 changes: 52 additions & 27 deletions crates/puffin-installer/src/plan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,51 +17,57 @@ use puffin_normalize::PackageName;

use crate::{ResolvedEditable, SitePackages};

#[derive(Debug, Default)]
pub struct InstallPlan {
/// The distributions that are not already installed in the current environment, but are
/// available in the local cache.
pub local: Vec<CachedDist>,

/// The distributions that are not already installed in the current environment, and are
/// not available in the local cache.
pub remote: Vec<Requirement>,
/// A planner to generate an [`Plan`] based on a set of requirements.
#[derive(Debug)]
pub struct Planner<'a> {
requirements: &'a [Requirement],
editable_requirements: Vec<ResolvedEditable>,
}

/// Any distributions that are already installed in the current environment, but will be
/// re-installed (including upgraded) to satisfy the requirements.
pub reinstalls: Vec<InstalledDist>,
impl<'a> Planner<'a> {
/// Set the requirements use in the [`Plan`].
#[must_use]
pub fn with_requirements(requirements: &'a [Requirement]) -> Self {
Self {
requirements,
editable_requirements: Vec::new(),
}
}

/// Any distributions that are already installed in the current environment, and are
/// _not_ necessary to satisfy the requirements.
pub extraneous: Vec<InstalledDist>,
}
/// Set the editable requirements use in the [`Plan`].
#[must_use]
pub fn with_editable_requirements(self, editable_requirements: Vec<ResolvedEditable>) -> Self {
Self {
editable_requirements,
..self
}
}

impl InstallPlan {
/// Partition a set of requirements into those that should be linked from the cache, those that
/// need to be downloaded, and those that should be removed.
#[allow(clippy::too_many_arguments)]
pub fn from_requirements(
requirements: &[Requirement],
editable_requirements: Vec<ResolvedEditable>,
pub fn build(
self,
mut site_packages: SitePackages,
reinstall: &Reinstall,
index_locations: &IndexLocations,
cache: &Cache,
venv: &Virtualenv,
tags: &Tags,
) -> Result<Self> {
) -> Result<Plan> {
// Index all the already-downloaded wheels in the cache.
let mut registry_index = RegistryWheelIndex::new(cache, tags, index_locations);

let mut local = vec![];
let mut remote = vec![];
let mut reinstalls = vec![];
let mut extraneous = vec![];
let mut seen =
FxHashSet::with_capacity_and_hasher(requirements.len(), BuildHasherDefault::default());
let mut seen = FxHashSet::with_capacity_and_hasher(
self.requirements.len(),
BuildHasherDefault::default(),
);

// Remove any editable requirements.
for requirement in editable_requirements {
for requirement in self.editable_requirements {
// If we see the same requirement twice, then we have a conflict.
if !seen.insert(requirement.name().clone()) {
bail!(
Expand Down Expand Up @@ -99,7 +105,7 @@ impl InstallPlan {
}
}

for requirement in requirements {
for requirement in self.requirements {
// Filter out incompatible requirements.
if !requirement.evaluate_markers(venv.interpreter().markers(), &[]) {
continue;
Expand Down Expand Up @@ -322,7 +328,7 @@ impl InstallPlan {
}
}

Ok(InstallPlan {
Ok(Plan {
local,
remote,
reinstalls,
Expand All @@ -331,6 +337,25 @@ impl InstallPlan {
}
}

#[derive(Debug, Default)]
pub struct Plan {
/// The distributions that are not already installed in the current environment, but are
/// available in the local cache.
pub local: Vec<CachedDist>,

/// The distributions that are not already installed in the current environment, and are
/// not available in the local cache.
pub remote: Vec<Requirement>,

/// Any distributions that are already installed in the current environment, but will be
/// re-installed (including upgraded) to satisfy the requirements.
pub reinstalls: Vec<InstalledDist>,

/// Any distributions that are already installed in the current environment, and are
/// _not_ necessary to satisfy the requirements.
pub extraneous: Vec<InstalledDist>,
}

#[derive(Debug)]
pub enum Reinstall {
/// Don't reinstall any packages; respect the existing installation.
Expand Down