mirror of
https://github.com/astral-sh/uv.git
synced 2025-08-04 19:08:04 +00:00
Add --upgrade
support to pip install
(#1379)
Adds support for `--upgrade` — similar to `--reinstall`. Closes https://github.com/astral-sh/uv/issues/1391
This commit is contained in:
parent
e9d82cf0fa
commit
896ab1c54f
5 changed files with 168 additions and 9 deletions
|
@ -467,4 +467,9 @@ impl Reinstall {
|
|||
pub fn is_none(&self) -> bool {
|
||||
matches!(self, Self::None)
|
||||
}
|
||||
|
||||
/// Returns `true` if all packages should be reinstalled.
|
||||
pub fn is_all(&self) -> bool {
|
||||
matches!(self, Self::All)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -425,6 +425,11 @@ impl Upgrade {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if no packages should be upgraded.
|
||||
pub(crate) fn is_none(&self) -> bool {
|
||||
matches!(self, Self::None)
|
||||
}
|
||||
|
||||
/// Returns `true` if all packages should be upgraded.
|
||||
pub(crate) fn is_all(&self) -> bool {
|
||||
matches!(self, Self::All)
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
use std::collections::HashSet;
|
||||
use std::fmt::Write;
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
use anstream::eprint;
|
||||
|
@ -38,6 +40,8 @@ use crate::commands::{elapsed, ChangeEvent, ChangeEventKind, ExitStatus};
|
|||
use crate::printer::Printer;
|
||||
use crate::requirements::{ExtrasSpecification, RequirementsSource, RequirementsSpecification};
|
||||
|
||||
use super::Upgrade;
|
||||
|
||||
/// Install packages into the current environment.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) async fn pip_install(
|
||||
|
@ -48,6 +52,7 @@ pub(crate) async fn pip_install(
|
|||
resolution_mode: ResolutionMode,
|
||||
prerelease_mode: PreReleaseMode,
|
||||
dependency_mode: DependencyMode,
|
||||
upgrade: Upgrade,
|
||||
index_locations: IndexLocations,
|
||||
reinstall: &Reinstall,
|
||||
link_mode: LinkMode,
|
||||
|
@ -115,7 +120,10 @@ pub(crate) async fn pip_install(
|
|||
// If the requirements are already satisfied, we're done. Ideally, the resolver would be fast
|
||||
// enough to let us remove this check. But right now, for large environments, it's an order of
|
||||
// magnitude faster to validate the environment than to resolve the requirements.
|
||||
if reinstall.is_none() && site_packages.satisfies(&requirements, &editables, &constraints)? {
|
||||
if reinstall.is_none()
|
||||
&& upgrade.is_none()
|
||||
&& site_packages.satisfies(&requirements, &editables, &constraints)?
|
||||
{
|
||||
let num_requirements = requirements.len() + editables.len();
|
||||
let s = if num_requirements == 1 { "" } else { "s" };
|
||||
writeln!(
|
||||
|
@ -206,6 +214,7 @@ pub(crate) async fn pip_install(
|
|||
&editables,
|
||||
&site_packages,
|
||||
reinstall,
|
||||
&upgrade,
|
||||
&interpreter,
|
||||
tags,
|
||||
markers,
|
||||
|
@ -378,6 +387,7 @@ async fn resolve(
|
|||
editables: &[BuiltEditable],
|
||||
site_packages: &SitePackages<'_>,
|
||||
reinstall: &Reinstall,
|
||||
upgrade: &Upgrade,
|
||||
interpreter: &Interpreter,
|
||||
tags: &Tags,
|
||||
markers: &MarkerEnvironment,
|
||||
|
@ -390,14 +400,25 @@ async fn resolve(
|
|||
) -> Result<ResolutionGraph, Error> {
|
||||
let start = std::time::Instant::now();
|
||||
|
||||
// Respect preferences from the existing environments.
|
||||
let preferences: Vec<Requirement> = match reinstall {
|
||||
Reinstall::All => vec![],
|
||||
Reinstall::None => site_packages.requirements().collect(),
|
||||
Reinstall::Packages(packages) => site_packages
|
||||
let preferences = if upgrade.is_all() || reinstall.is_all() {
|
||||
vec![]
|
||||
} else {
|
||||
// Combine upgrade and reinstall lists
|
||||
let mut exclusions: HashSet<&PackageName> = if let Reinstall::Packages(packages) = reinstall
|
||||
{
|
||||
HashSet::from_iter(packages)
|
||||
} else {
|
||||
HashSet::default()
|
||||
};
|
||||
if let Upgrade::Packages(packages) = upgrade {
|
||||
exclusions.extend(packages);
|
||||
};
|
||||
|
||||
// Prefer current site packages, unless in the upgrade or reinstall lists
|
||||
site_packages
|
||||
.requirements()
|
||||
.filter(|requirement| !packages.contains(&requirement.name))
|
||||
.collect(),
|
||||
.filter(|requirement| !exclusions.contains(&requirement.name))
|
||||
.collect()
|
||||
};
|
||||
|
||||
// Map the editables to their metadata.
|
||||
|
|
|
@ -470,6 +470,14 @@ struct PipInstallArgs {
|
|||
#[clap(long, conflicts_with = "extra")]
|
||||
all_extras: bool,
|
||||
|
||||
/// Allow package upgrades.
|
||||
#[clap(long)]
|
||||
upgrade: bool,
|
||||
|
||||
/// Allow upgrade of a specific package.
|
||||
#[clap(long)]
|
||||
upgrade_package: Vec<PackageName>,
|
||||
|
||||
/// Reinstall all packages, regardless of whether they're already installed.
|
||||
#[clap(long, alias = "force-reinstall")]
|
||||
reinstall: bool,
|
||||
|
@ -935,6 +943,7 @@ async fn run() -> Result<ExitStatus> {
|
|||
ExtrasSpecification::Some(&args.extra)
|
||||
};
|
||||
let reinstall = Reinstall::from_args(args.reinstall, args.reinstall_package);
|
||||
let upgrade = Upgrade::from_args(args.upgrade, args.upgrade_package);
|
||||
let no_binary = NoBinary::from_args(args.no_binary);
|
||||
let no_build = NoBuild::from_args(args.only_binary, args.no_build);
|
||||
let dependency_mode = if args.no_deps {
|
||||
|
@ -950,6 +959,7 @@ async fn run() -> Result<ExitStatus> {
|
|||
args.resolution,
|
||||
args.prerelease,
|
||||
dependency_mode,
|
||||
upgrade,
|
||||
index_urls,
|
||||
&reinstall,
|
||||
args.link_mode,
|
||||
|
|
|
@ -160,7 +160,7 @@ fn install_requirements_txt() -> Result<()> {
|
|||
|
||||
/// Respect installed versions when resolving.
|
||||
#[test]
|
||||
fn respect_installed() -> Result<()> {
|
||||
fn respect_installed_and_reinstall() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
// Install Flask.
|
||||
|
@ -268,6 +268,29 @@ fn respect_installed() -> Result<()> {
|
|||
"###
|
||||
);
|
||||
|
||||
// Re-install Flask. We should install even though the version is current
|
||||
let requirements_txt = context.temp_dir.child("requirements.txt");
|
||||
requirements_txt.touch()?;
|
||||
requirements_txt.write_str("Flask")?;
|
||||
|
||||
uv_snapshot!(filters, command(&context)
|
||||
.arg("-r")
|
||||
.arg("requirements.txt")
|
||||
.arg("--reinstall-package")
|
||||
.arg("Flask")
|
||||
.arg("--strict"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Resolved 7 packages in [TIME]
|
||||
Installed 1 package in [TIME]
|
||||
- flask==3.0.0
|
||||
+ flask==3.0.0
|
||||
"###
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -894,3 +917,98 @@ fn no_deps() {
|
|||
|
||||
context.assert_command("import flask").failure();
|
||||
}
|
||||
|
||||
/// Upgrade a package.
|
||||
#[test]
|
||||
fn install_upgrade() {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
// Install an old version of anyio and httpcore.
|
||||
uv_snapshot!(command(&context)
|
||||
.arg("anyio==3.6.2")
|
||||
.arg("httpcore==0.16.3")
|
||||
.arg("--strict"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Resolved 6 packages in [TIME]
|
||||
Downloaded 6 packages in [TIME]
|
||||
Installed 6 packages in [TIME]
|
||||
+ anyio==3.6.2
|
||||
+ certifi==2023.11.17
|
||||
+ h11==0.14.0
|
||||
+ httpcore==0.16.3
|
||||
+ idna==3.4
|
||||
+ sniffio==1.3.0
|
||||
"###
|
||||
);
|
||||
|
||||
context.assert_command("import anyio").success();
|
||||
|
||||
// Upgrade anyio.
|
||||
uv_snapshot!(command(&context)
|
||||
.arg("anyio")
|
||||
.arg("--upgrade-package")
|
||||
.arg("anyio"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Resolved 3 packages in [TIME]
|
||||
Downloaded 1 package in [TIME]
|
||||
Installed 1 package in [TIME]
|
||||
- anyio==3.6.2
|
||||
+ anyio==4.0.0
|
||||
"###
|
||||
);
|
||||
|
||||
// Upgrade anyio again, should not reinstall.
|
||||
uv_snapshot!(command(&context)
|
||||
.arg("anyio")
|
||||
.arg("--upgrade-package")
|
||||
.arg("anyio"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Resolved 3 packages in [TIME]
|
||||
Audited 3 packages in [TIME]
|
||||
"###
|
||||
);
|
||||
|
||||
// Install httpcore, request anyio upgrade should not reinstall
|
||||
uv_snapshot!(command(&context)
|
||||
.arg("httpcore")
|
||||
.arg("--upgrade-package")
|
||||
.arg("anyio"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Resolved 6 packages in [TIME]
|
||||
Audited 6 packages in [TIME]
|
||||
"###
|
||||
);
|
||||
|
||||
// Upgrade httpcore with global flag
|
||||
uv_snapshot!(command(&context)
|
||||
.arg("httpcore")
|
||||
.arg("--upgrade"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Resolved 3 packages in [TIME]
|
||||
Downloaded 1 package in [TIME]
|
||||
Installed 1 package in [TIME]
|
||||
- httpcore==0.16.3
|
||||
+ httpcore==1.0.2
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue