mirror of https://github.com/astral-sh/uv
Error on duplicate PEP 735 dependency groups (#8390)
## Summary Part of: https://github.com/astral-sh/uv/pull/8272.
This commit is contained in:
parent
384f4459a1
commit
4d134a4ffe
|
|
@ -46,7 +46,7 @@ pub struct PyProjectToml {
|
|||
/// Tool-specific metadata.
|
||||
pub tool: Option<Tool>,
|
||||
/// Non-project dependency groups, as defined in PEP 735.
|
||||
pub dependency_groups: Option<BTreeMap<GroupName, Vec<DependencyGroupSpecifier>>>,
|
||||
pub dependency_groups: Option<DependencyGroups>,
|
||||
/// The raw unserialized document.
|
||||
#[serde(skip)]
|
||||
pub raw: String,
|
||||
|
|
@ -540,6 +540,79 @@ impl Deref for SerdePattern {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[cfg_attr(test, derive(Serialize))]
|
||||
pub struct DependencyGroups(BTreeMap<GroupName, Vec<DependencyGroupSpecifier>>);
|
||||
|
||||
impl DependencyGroups {
|
||||
/// Returns the names of the dependency groups.
|
||||
pub fn keys(&self) -> impl Iterator<Item = &GroupName> {
|
||||
self.0.keys()
|
||||
}
|
||||
|
||||
/// Returns the dependency group with the given name.
|
||||
pub fn get(&self, group: &GroupName) -> Option<&Vec<DependencyGroupSpecifier>> {
|
||||
self.0.get(group)
|
||||
}
|
||||
|
||||
/// Returns an iterator over the dependency groups.
|
||||
pub fn iter(&self) -> impl Iterator<Item = (&GroupName, &Vec<DependencyGroupSpecifier>)> {
|
||||
self.0.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a DependencyGroups {
|
||||
type Item = (&'a GroupName, &'a Vec<DependencyGroupSpecifier>);
|
||||
type IntoIter = std::collections::btree_map::Iter<'a, GroupName, Vec<DependencyGroupSpecifier>>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.iter()
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensure that all keys in the TOML table are unique.
|
||||
impl<'de> serde::de::Deserialize<'de> for DependencyGroups {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct GroupVisitor;
|
||||
|
||||
impl<'de> serde::de::Visitor<'de> for GroupVisitor {
|
||||
type Value = DependencyGroups;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
formatter.write_str("a table with unique dependency group names")
|
||||
}
|
||||
|
||||
fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
|
||||
where
|
||||
M: serde::de::MapAccess<'de>,
|
||||
{
|
||||
let mut sources = BTreeMap::new();
|
||||
while let Some((key, value)) =
|
||||
access.next_entry::<GroupName, Vec<DependencyGroupSpecifier>>()?
|
||||
{
|
||||
match sources.entry(key) {
|
||||
std::collections::btree_map::Entry::Occupied(entry) => {
|
||||
return Err(serde::de::Error::custom(format!(
|
||||
"duplicate dependency group: `{}`",
|
||||
entry.key()
|
||||
)));
|
||||
}
|
||||
std::collections::btree_map::Entry::Vacant(entry) => {
|
||||
entry.insert(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(DependencyGroups(sources))
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_map(GroupVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
#[serde(rename_all = "kebab-case", try_from = "SourcesWire")]
|
||||
|
|
|
|||
|
|
@ -16635,6 +16635,42 @@ fn lock_group_invalid_entry_group_name() -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lock_group_invalid_duplicate_group_name() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||
pyproject_toml.write_str(
|
||||
r#"
|
||||
[project]
|
||||
name = "project"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = ["typing-extensions"]
|
||||
|
||||
[dependency-groups]
|
||||
foo-bar = []
|
||||
foo_bar = []
|
||||
"#,
|
||||
)?;
|
||||
|
||||
uv_snapshot!(context.filters(), context.lock(), @r###"
|
||||
success: false
|
||||
exit_code: 2
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
error: Failed to parse: `pyproject.toml`
|
||||
Caused by: TOML parse error at line 8, column 9
|
||||
|
|
||||
8 | [dependency-groups]
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
duplicate dependency group: `foo-bar`
|
||||
"###);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lock_group_invalid_entry_table() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
|
|
|||
Loading…
Reference in New Issue