Skip to content

Commit

Permalink
Add --frozen to uv add, uv remove, and uv tree (#5214)
Browse files Browse the repository at this point in the history
## Summary

E.g., `uv add foo --frozen` will avoid updating or even creating a
`uv.lock`.
  • Loading branch information
charliermarsh committed Jul 19, 2024
1 parent fb1a529 commit 729148d
Show file tree
Hide file tree
Showing 8 changed files with 209 additions and 44 deletions.
59 changes: 42 additions & 17 deletions crates/uv-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1784,14 +1784,6 @@ impl ExternalCommand {
#[derive(Args)]
#[allow(clippy::struct_excessive_bools)]
pub struct RunArgs {
/// Assert that the `uv.lock` will remain unchanged.
#[arg(long, conflicts_with = "frozen")]
pub locked: bool,

/// Install without updating the `uv.lock` file.
#[arg(long, conflicts_with = "locked")]
pub frozen: bool,

/// Include optional dependencies from the extra group name; may be provided more than once.
///
/// Only applies to `pyproject.toml`, `setup.py`, and `setup.cfg` sources.
Expand Down Expand Up @@ -1823,6 +1815,14 @@ pub struct RunArgs {
#[arg(long)]
pub with: Vec<String>,

/// Assert that the `uv.lock` will remain unchanged.
#[arg(long, conflicts_with = "frozen")]
pub locked: bool,

/// Install without updating the `uv.lock` file.
#[arg(long, conflicts_with = "locked")]
pub frozen: bool,

#[command(flatten)]
pub installer: ResolverInstallerArgs,

Expand Down Expand Up @@ -1854,14 +1854,6 @@ pub struct RunArgs {
#[derive(Args)]
#[allow(clippy::struct_excessive_bools)]
pub struct SyncArgs {
/// Assert that the `uv.lock` will remain unchanged.
#[arg(long, conflicts_with = "frozen")]
pub locked: bool,

/// Install without updating the `uv.lock` file.
#[arg(long, conflicts_with = "locked")]
pub frozen: bool,

/// Include optional dependencies from the extra group name; may be provided more than once.
///
/// Only applies to `pyproject.toml`, `setup.py`, and `setup.cfg` sources.
Expand All @@ -1886,10 +1878,19 @@ pub struct SyncArgs {
pub no_dev: bool,

/// Does not clean the environment.
/// Without this flag any extraneous installations will be removed.
///
/// When omitted, any extraneous installations will be removed.
#[arg(long)]
pub no_clean: bool,

/// Assert that the `uv.lock` will remain unchanged.
#[arg(long, conflicts_with = "frozen")]
pub locked: bool,

/// Install without updating the `uv.lock` file.
#[arg(long, conflicts_with = "locked")]
pub frozen: bool,

#[command(flatten)]
pub installer: ResolverInstallerArgs,

Expand Down Expand Up @@ -1990,6 +1991,14 @@ pub struct AddArgs {
#[arg(long)]
pub extra: Option<Vec<ExtraName>>,

/// Assert that the `uv.lock` will remain unchanged.
#[arg(long, conflicts_with = "frozen")]
pub locked: bool,

/// Add the requirements without updating the `uv.lock` file.
#[arg(long, conflicts_with = "locked")]
pub frozen: bool,

#[command(flatten)]
pub installer: ResolverInstallerArgs,

Expand Down Expand Up @@ -2034,6 +2043,14 @@ pub struct RemoveArgs {
#[arg(long, conflicts_with("dev"))]
pub optional: Option<ExtraName>,

/// Assert that the `uv.lock` will remain unchanged.
#[arg(long, conflicts_with = "frozen")]
pub locked: bool,

/// Remove the requirements without updating the `uv.lock` file.
#[arg(long, conflicts_with = "locked")]
pub frozen: bool,

#[command(flatten)]
pub installer: ResolverInstallerArgs,

Expand Down Expand Up @@ -2069,6 +2086,14 @@ pub struct TreeArgs {
#[command(flatten)]
pub tree: DisplayTreeArgs,

/// Assert that the `uv.lock` will remain unchanged.
#[arg(long, conflicts_with = "frozen")]
pub locked: bool,

/// Display the requirements without updating the `uv.lock` file.
#[arg(long, conflicts_with = "locked")]
pub frozen: bool,

#[command(flatten)]
pub build: BuildArgs,

Expand Down
20 changes: 11 additions & 9 deletions crates/uv/src/commands/project/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ use uv_warnings::warn_user_once;

use crate::commands::pip::operations::Modifications;
use crate::commands::pip::resolution_environment;
use crate::commands::project::lock::commit;
use crate::commands::reporters::ResolverReporter;
use crate::commands::{project, ExitStatus, SharedState};
use crate::printer::Printer;
Expand All @@ -27,6 +26,8 @@ use crate::settings::ResolverInstallerSettings;
/// Add one or more packages to the project requirements.
#[allow(clippy::fn_params_excessive_bools)]
pub(crate) async fn add(
locked: bool,
frozen: bool,
requirements: Vec<RequirementsSource>,
editable: Option<bool>,
dependency_type: DependencyType,
Expand Down Expand Up @@ -203,17 +204,21 @@ pub(crate) async fn add(
pyproject.to_string(),
)?;

// If `--frozen`, exit early. There's no reason to lock and sync, and we don't need a `uv.lock`
// to exist at all.
if frozen {
return Ok(ExitStatus::Success);
}

// Initialize any shared state.
let state = SharedState::default();

// Read the existing lockfile.
let existing = project::lock::read(project.workspace()).await?;

// Lock and sync the environment, if necessary.
let lock = project::lock::do_lock(
let lock = project::lock::do_safe_lock(
locked,
frozen,
project.workspace(),
venv.interpreter(),
existing.as_ref(),
settings.as_ref().into(),
&state,
preview,
Expand All @@ -224,9 +229,6 @@ pub(crate) async fn add(
printer,
)
.await?;
if !existing.is_some_and(|existing| existing == lock) {
commit(&lock, project.workspace()).await?;
}

// Perform a full sync, because we don't know what exactly is affected by the removal.
// TODO(ibraheem): Should we accept CLI overrides for this? Should we even sync here?
Expand Down
20 changes: 11 additions & 9 deletions crates/uv/src/commands/project/remove.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@ use uv_python::{PythonFetch, PythonPreference, PythonRequest};
use uv_warnings::{warn_user, warn_user_once};

use crate::commands::pip::operations::Modifications;
use crate::commands::project::lock::commit;
use crate::commands::{project, ExitStatus, SharedState};
use crate::printer::Printer;
use crate::settings::ResolverInstallerSettings;

/// Remove one or more packages from the project requirements.
pub(crate) async fn remove(
locked: bool,
frozen: bool,
requirements: Vec<PackageName>,
dependency_type: DependencyType,
package: Option<PackageName>,
Expand Down Expand Up @@ -83,6 +84,12 @@ pub(crate) async fn remove(
pyproject.to_string(),
)?;

// If `--frozen`, exit early. There's no reason to lock and sync, and we don't need a `uv.lock`
// to exist at all.
if frozen {
return Ok(ExitStatus::Success);
}

// Discover or create the virtual environment.
let venv = project::get_or_init_environment(
project.workspace(),
Expand All @@ -99,14 +106,12 @@ pub(crate) async fn remove(
// Initialize any shared state.
let state = SharedState::default();

// Read the existing lockfile.
let existing = project::lock::read(project.workspace()).await?;

// Lock and sync the environment, if necessary.
let lock = project::lock::do_lock(
let lock = project::lock::do_safe_lock(
locked,
frozen,
project.workspace(),
venv.interpreter(),
existing.as_ref(),
settings.as_ref().into(),
&state,
preview,
Expand All @@ -117,9 +122,6 @@ pub(crate) async fn remove(
printer,
)
.await?;
if !existing.is_some_and(|existing| existing == lock) {
commit(&lock, project.workspace()).await?;
}

// Perform a full sync, because we don't know what exactly is affected by the removal.
// TODO(ibraheem): Should we accept CLI overrides for this? Should we even sync here?
Expand Down
14 changes: 6 additions & 8 deletions crates/uv/src/commands/project/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ use crate::settings::ResolverSettings;
use super::SharedState;

/// Run a command.
#[allow(clippy::fn_params_excessive_bools)]
pub(crate) async fn tree(
locked: bool,
frozen: bool,
depth: u8,
prune: Vec<PackageName>,
package: Vec<PackageName>,
Expand Down Expand Up @@ -60,14 +63,12 @@ pub(crate) async fn tree(
.await?
.into_interpreter();

// Read the existing lockfile.
let existing = project::lock::read(&workspace).await?;

// Update the lock file, if necessary.
let lock = project::lock::do_lock(
let lock = project::lock::do_safe_lock(
locked,
frozen,
&workspace,
&interpreter,
existing.as_ref(),
settings.as_ref(),
&SharedState::default(),
preview,
Expand All @@ -78,9 +79,6 @@ pub(crate) async fn tree(
printer,
)
.await?;
if !existing.is_some_and(|existing| existing == lock) {
project::lock::commit(&lock, &workspace).await?;
}

// Read packages from the lockfile.
let mut packages: IndexMap<_, Vec<_>> = IndexMap::new();
Expand Down
6 changes: 6 additions & 0 deletions crates/uv/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -917,6 +917,8 @@ async fn run_project(
let cache = cache.init()?.with_refresh(args.refresh);

commands::add(
args.locked,
args.frozen,
args.requirements,
args.editable,
args.dependency_type,
Expand Down Expand Up @@ -948,6 +950,8 @@ async fn run_project(
let cache = cache.init()?.with_refresh(args.refresh);

commands::remove(
args.locked,
args.frozen,
args.requirements,
args.dependency_type,
args.package,
Expand All @@ -973,6 +977,8 @@ async fn run_project(
let cache = cache.init()?;

commands::tree(
args.locked,
args.frozen,
args.depth,
args.prune,
args.package,
Expand Down
20 changes: 19 additions & 1 deletion crates/uv/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,8 @@ impl LockSettings {
#[allow(clippy::struct_excessive_bools, dead_code)]
#[derive(Debug, Clone)]
pub(crate) struct AddSettings {
pub(crate) locked: bool,
pub(crate) frozen: bool,
pub(crate) requirements: Vec<RequirementsSource>,
pub(crate) dependency_type: DependencyType,
pub(crate) editable: Option<bool>,
Expand Down Expand Up @@ -585,6 +587,8 @@ impl AddSettings {
rev,
tag,
branch,
locked,
frozen,
installer,
build,
refresh,
Expand All @@ -606,6 +610,8 @@ impl AddSettings {
};

Self {
locked,
frozen,
requirements,
dependency_type,
editable,
Expand All @@ -629,6 +635,8 @@ impl AddSettings {
#[allow(clippy::struct_excessive_bools, dead_code)]
#[derive(Debug, Clone)]
pub(crate) struct RemoveSettings {
pub(crate) locked: bool,
pub(crate) frozen: bool,
pub(crate) requirements: Vec<PackageName>,
pub(crate) dependency_type: DependencyType,
pub(crate) package: Option<PackageName>,
Expand All @@ -645,6 +653,8 @@ impl RemoveSettings {
dev,
optional,
requirements,
locked,
frozen,
installer,
build,
refresh,
Expand All @@ -661,6 +671,8 @@ impl RemoveSettings {
};

Self {
locked,
frozen,
requirements,
dependency_type,
package,
Expand All @@ -678,6 +690,8 @@ impl RemoveSettings {
#[allow(clippy::struct_excessive_bools)]
#[derive(Debug, Clone)]
pub(crate) struct TreeSettings {
pub(crate) locked: bool,
pub(crate) frozen: bool,
pub(crate) depth: u8,
pub(crate) prune: Vec<PackageName>,
pub(crate) package: Vec<PackageName>,
Expand All @@ -692,18 +706,22 @@ impl TreeSettings {
pub(crate) fn resolve(args: TreeArgs, filesystem: Option<FilesystemOptions>) -> Self {
let TreeArgs {
tree,
locked,
frozen,
build,
resolver,
python,
} = args;

Self {
python,
locked,
frozen,
depth: tree.depth,
prune: tree.prune,
package: tree.package,
no_dedupe: tree.no_dedupe,
invert: tree.invert,
python,
resolver: ResolverSettings::combine(resolver_options(resolver, build), filesystem),
}
}
Expand Down
Loading

0 comments on commit 729148d

Please sign in to comment.