mirror of
https://github.com/astral-sh/uv.git
synced 2025-10-31 20:09:09 +00:00
Make repeated uv add operations simpler (#5922)
## Summary Closes #5913.
This commit is contained in:
parent
2789830ac2
commit
dc3f498f58
4 changed files with 102 additions and 13 deletions
|
|
@ -30,7 +30,7 @@ pub struct PyProjectToml {
|
||||||
pub tool: Option<Tool>,
|
pub tool: Option<Tool>,
|
||||||
/// The raw unserialized document.
|
/// The raw unserialized document.
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub(crate) raw: String,
|
pub raw: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PyProjectToml {
|
impl PyProjectToml {
|
||||||
|
|
|
||||||
|
|
@ -55,11 +55,6 @@ impl PyProjectTomlMut {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize a [`PyProjectToml`] from a [`PyProjectTomlMut`].
|
|
||||||
pub fn to_toml(&self) -> Result<PyProjectToml, Error> {
|
|
||||||
Ok(toml::from_str(&self.doc.to_string()).map_err(Box::new)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds a project to the workspace.
|
/// Adds a project to the workspace.
|
||||||
pub fn add_workspace(&mut self, path: impl AsRef<Path>) -> Result<(), Error> {
|
pub fn add_workspace(&mut self, path: impl AsRef<Path>) -> Result<(), Error> {
|
||||||
// Get or create `tool.uv.workspace.members`.
|
// Get or create `tool.uv.workspace.members`.
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@ use std::collections::hash_map::Entry;
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use owo_colors::OwoColorize;
|
use owo_colors::OwoColorize;
|
||||||
use rustc_hash::{FxBuildHasher, FxHashMap};
|
|
||||||
|
|
||||||
use pep508_rs::{ExtraName, Requirement, VersionOrUrl};
|
use pep508_rs::{ExtraName, Requirement, VersionOrUrl};
|
||||||
|
use rustc_hash::{FxBuildHasher, FxHashMap};
|
||||||
|
use tracing::debug;
|
||||||
use uv_auth::store_credentials_from_url;
|
use uv_auth::store_credentials_from_url;
|
||||||
use uv_cache::Cache;
|
use uv_cache::Cache;
|
||||||
use uv_client::{BaseClientBuilder, Connectivity, FlatIndexClient, RegistryClientBuilder};
|
use uv_client::{BaseClientBuilder, Connectivity, FlatIndexClient, RegistryClientBuilder};
|
||||||
|
|
@ -247,7 +247,14 @@ pub(crate) async fn add(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save the modified `pyproject.toml`.
|
// Save the modified `pyproject.toml`.
|
||||||
fs_err::write(project.root().join("pyproject.toml"), pyproject.to_string())?;
|
let mut modified = false;
|
||||||
|
let content = pyproject.to_string();
|
||||||
|
if content == existing.raw {
|
||||||
|
debug!("No changes to `pyproject.toml`; skipping update");
|
||||||
|
} else {
|
||||||
|
fs_err::write(project.root().join("pyproject.toml"), &content)?;
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
|
|
||||||
// If `--frozen`, exit early. There's no reason to lock and sync, and we don't need a `uv.lock`
|
// If `--frozen`, exit early. There's no reason to lock and sync, and we don't need a `uv.lock`
|
||||||
// to exist at all.
|
// to exist at all.
|
||||||
|
|
@ -258,7 +265,7 @@ pub(crate) async fn add(
|
||||||
// Update the `pypackage.toml` in-memory.
|
// Update the `pypackage.toml` in-memory.
|
||||||
let project = project
|
let project = project
|
||||||
.clone()
|
.clone()
|
||||||
.with_pyproject_toml(pyproject.to_toml()?)
|
.with_pyproject_toml(toml::from_str(&content)?)
|
||||||
.context("Failed to update `pyproject.toml`")?;
|
.context("Failed to update `pyproject.toml`")?;
|
||||||
|
|
||||||
// Lock and sync the environment, if necessary.
|
// Lock and sync the environment, if necessary.
|
||||||
|
|
@ -286,8 +293,10 @@ pub(crate) async fn add(
|
||||||
let report = miette::Report::new(WithHelp { header, cause: err, help: Some("If this is intentional, run `uv add --frozen` to skip the lock and sync steps.") });
|
let report = miette::Report::new(WithHelp { header, cause: err, help: Some("If this is intentional, run `uv add --frozen` to skip the lock and sync steps.") });
|
||||||
anstream::eprint!("{report:?}");
|
anstream::eprint!("{report:?}");
|
||||||
|
|
||||||
// Revert the changes to the `pyproject.toml`.
|
// Revert the changes to the `pyproject.toml`, if necessary.
|
||||||
fs_err::write(project.root().join("pyproject.toml"), existing)?;
|
if modified {
|
||||||
|
fs_err::write(project.root().join("pyproject.toml"), existing)?;
|
||||||
|
}
|
||||||
|
|
||||||
return Ok(ExitStatus::Failure);
|
return Ok(ExitStatus::Failure);
|
||||||
}
|
}
|
||||||
|
|
@ -361,7 +370,9 @@ pub(crate) async fn add(
|
||||||
modified = true;
|
modified = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save the modified `pyproject.toml`.
|
// Save the modified `pyproject.toml`. No need to check for changes in the underlying
|
||||||
|
// string content, since the above loop _must_ change an empty specifier to a non-empty
|
||||||
|
// specifier.
|
||||||
if modified {
|
if modified {
|
||||||
fs_err::write(project.root().join("pyproject.toml"), pyproject.to_string())?;
|
fs_err::write(project.root().join("pyproject.toml"), pyproject.to_string())?;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2829,3 +2829,86 @@ fn add_virtual() -> Result<()> {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add the same requirement multiple times.
|
||||||
|
#[test]
|
||||||
|
fn add_repeat() -> Result<()> {
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||||
|
pyproject_toml.write_str(indoc! {r#"
|
||||||
|
[project]
|
||||||
|
name = "project"
|
||||||
|
version = "0.1.0"
|
||||||
|
# ...
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
dependencies = []
|
||||||
|
"#})?;
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.add(&["anyio"]), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
warning: `uv add` is experimental and may change without warning
|
||||||
|
Resolved 4 packages in [TIME]
|
||||||
|
Prepared 4 packages in [TIME]
|
||||||
|
Installed 4 packages in [TIME]
|
||||||
|
+ anyio==4.3.0
|
||||||
|
+ idna==3.6
|
||||||
|
+ project==0.1.0 (from file://[TEMP_DIR]/)
|
||||||
|
+ sniffio==1.3.1
|
||||||
|
"###);
|
||||||
|
|
||||||
|
let pyproject_toml = fs_err::read_to_string(context.temp_dir.join("pyproject.toml"))?;
|
||||||
|
|
||||||
|
insta::with_settings!({
|
||||||
|
filters => context.filters(),
|
||||||
|
}, {
|
||||||
|
assert_snapshot!(
|
||||||
|
pyproject_toml, @r###"
|
||||||
|
[project]
|
||||||
|
name = "project"
|
||||||
|
version = "0.1.0"
|
||||||
|
# ...
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
dependencies = [
|
||||||
|
"anyio>=4.3.0",
|
||||||
|
]
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.add(&["anyio"]), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
warning: `uv add` is experimental and may change without warning
|
||||||
|
Resolved 4 packages in [TIME]
|
||||||
|
Audited 4 packages in [TIME]
|
||||||
|
"###);
|
||||||
|
|
||||||
|
let pyproject_toml = fs_err::read_to_string(context.temp_dir.join("pyproject.toml"))?;
|
||||||
|
|
||||||
|
insta::with_settings!({
|
||||||
|
filters => context.filters(),
|
||||||
|
}, {
|
||||||
|
assert_snapshot!(
|
||||||
|
pyproject_toml, @r###"
|
||||||
|
[project]
|
||||||
|
name = "project"
|
||||||
|
version = "0.1.0"
|
||||||
|
# ...
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
dependencies = [
|
||||||
|
"anyio>=4.3.0",
|
||||||
|
]
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue