mirror of https://github.com/astral-sh/uv
Move `DependencyGroups` to uv-pypi-types so it can be imported there (#12037)
This PR is in support of #12005, where we need to import `DependencyGroups` in the `uv-pypi-types` crate without a circular dependency on `uv-workspace`.
This commit is contained in:
parent
7a56aef7d1
commit
1ab1945dd9
|
|
@ -32,7 +32,7 @@ mailparse = { workspace = true }
|
|||
regex = { workspace = true }
|
||||
rkyv = { workspace = true }
|
||||
schemars = { workspace = true, optional = true }
|
||||
serde = { workspace = true }
|
||||
serde = { workspace = true, optional = true }
|
||||
serde-untagged = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
toml = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,148 @@
|
|||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
use std::collections::BTreeMap;
|
||||
use std::str::FromStr;
|
||||
|
||||
use uv_normalize::GroupName;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, 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 `true` if the dependency group is in the list.
|
||||
pub fn contains_key(&self, group: &GroupName) -> bool {
|
||||
self.0.contains_key(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)
|
||||
}
|
||||
}
|
||||
|
||||
/// A specifier item in a [PEP 735](https://peps.python.org/pep-0735/) Dependency Group.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
|
||||
pub enum DependencyGroupSpecifier {
|
||||
/// A PEP 508-compatible requirement string.
|
||||
Requirement(String),
|
||||
/// A reference to another dependency group.
|
||||
IncludeGroup {
|
||||
/// The name of the group to include.
|
||||
include_group: GroupName,
|
||||
},
|
||||
/// A Dependency Object Specifier.
|
||||
Object(BTreeMap<String, String>),
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for DependencyGroupSpecifier {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct Visitor;
|
||||
|
||||
impl<'de> serde::de::Visitor<'de> for Visitor {
|
||||
type Value = DependencyGroupSpecifier;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
formatter.write_str("a string or a map with the `include-group` key")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
Ok(DependencyGroupSpecifier::Requirement(value.to_owned()))
|
||||
}
|
||||
|
||||
fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
|
||||
where
|
||||
M: serde::de::MapAccess<'de>,
|
||||
{
|
||||
let mut map_data = BTreeMap::new();
|
||||
while let Some((key, value)) = map.next_entry()? {
|
||||
map_data.insert(key, value);
|
||||
}
|
||||
|
||||
if map_data.is_empty() {
|
||||
return Err(serde::de::Error::custom("missing field `include-group`"));
|
||||
}
|
||||
|
||||
if let Some(include_group) = map_data
|
||||
.get("include-group")
|
||||
.map(String::as_str)
|
||||
.map(GroupName::from_str)
|
||||
.transpose()
|
||||
.map_err(serde::de::Error::custom)?
|
||||
{
|
||||
Ok(DependencyGroupSpecifier::IncludeGroup { include_group })
|
||||
} else {
|
||||
Ok(DependencyGroupSpecifier::Object(map_data))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_any(Visitor)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
pub use base_url::*;
|
||||
pub use conflicts::*;
|
||||
pub use dependency_groups::*;
|
||||
pub use direct_url::*;
|
||||
pub use lenient_requirement::*;
|
||||
pub use marker_environment::*;
|
||||
|
|
@ -12,6 +13,7 @@ pub use supported_environments::*;
|
|||
|
||||
mod base_url;
|
||||
mod conflicts;
|
||||
mod dependency_groups;
|
||||
mod direct_url;
|
||||
mod lenient_requirement;
|
||||
mod marker_environment;
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ uv-normalize = { workspace = true }
|
|||
uv-options-metadata = { workspace = true }
|
||||
uv-pep440 = { workspace = true }
|
||||
uv-pep508 = { workspace = true }
|
||||
uv-pypi-types = { workspace = true }
|
||||
uv-pypi-types = { workspace = true, features = ["serde"] }
|
||||
uv-static = { workspace = true }
|
||||
uv-warnings = { workspace = true }
|
||||
|
||||
|
|
|
|||
|
|
@ -7,9 +7,7 @@ use tracing::warn;
|
|||
|
||||
use uv_normalize::{GroupName, DEV_DEPENDENCIES};
|
||||
use uv_pep508::Pep508Error;
|
||||
use uv_pypi_types::VerbatimParsedUrl;
|
||||
|
||||
use crate::pyproject::DependencyGroupSpecifier;
|
||||
use uv_pypi_types::{DependencyGroupSpecifier, VerbatimParsedUrl};
|
||||
|
||||
/// PEP 735 dependency groups, with any `include-group` entries resolved.
|
||||
#[derive(Debug, Default, Clone)]
|
||||
|
|
|
|||
|
|
@ -27,7 +27,8 @@ use uv_normalize::{ExtraName, GroupName, PackageName};
|
|||
use uv_pep440::{Version, VersionSpecifiers};
|
||||
use uv_pep508::MarkerTree;
|
||||
use uv_pypi_types::{
|
||||
Conflicts, RequirementSource, SchemaConflicts, SupportedEnvironments, VerbatimParsedUrl,
|
||||
Conflicts, DependencyGroups, RequirementSource, SchemaConflicts, SupportedEnvironments,
|
||||
VerbatimParsedUrl,
|
||||
};
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
|
|
@ -143,73 +144,6 @@ impl AsRef<[u8]> for PyProjectToml {
|
|||
}
|
||||
}
|
||||
|
||||
/// A specifier item in a [PEP 735](https://peps.python.org/pep-0735/) Dependency Group.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(test, derive(Serialize))]
|
||||
pub enum DependencyGroupSpecifier {
|
||||
/// A PEP 508-compatible requirement string.
|
||||
Requirement(String),
|
||||
/// A reference to another dependency group.
|
||||
IncludeGroup {
|
||||
/// The name of the group to include.
|
||||
include_group: GroupName,
|
||||
},
|
||||
/// A Dependency Object Specifier.
|
||||
Object(BTreeMap<String, String>),
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for DependencyGroupSpecifier {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct Visitor;
|
||||
|
||||
impl<'de> serde::de::Visitor<'de> for Visitor {
|
||||
type Value = DependencyGroupSpecifier;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
formatter.write_str("a string or a map with the `include-group` key")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
Ok(DependencyGroupSpecifier::Requirement(value.to_owned()))
|
||||
}
|
||||
|
||||
fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
|
||||
where
|
||||
M: serde::de::MapAccess<'de>,
|
||||
{
|
||||
let mut map_data = BTreeMap::new();
|
||||
while let Some((key, value)) = map.next_entry()? {
|
||||
map_data.insert(key, value);
|
||||
}
|
||||
|
||||
if map_data.is_empty() {
|
||||
return Err(serde::de::Error::custom("missing field `include-group`"));
|
||||
}
|
||||
|
||||
if let Some(include_group) = map_data
|
||||
.get("include-group")
|
||||
.map(String::as_str)
|
||||
.map(GroupName::from_str)
|
||||
.transpose()
|
||||
.map_err(serde::de::Error::custom)?
|
||||
{
|
||||
Ok(DependencyGroupSpecifier::IncludeGroup { include_group })
|
||||
} else {
|
||||
Ok(DependencyGroupSpecifier::Object(map_data))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_any(Visitor)
|
||||
}
|
||||
}
|
||||
|
||||
/// PEP 621 project metadata (`project`).
|
||||
///
|
||||
/// See <https://packaging.python.org/en/latest/specifications/pyproject-toml>.
|
||||
|
|
@ -797,84 +731,6 @@ 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 `true` if the dependency group is in the list.
|
||||
pub fn contains_key(&self, group: &GroupName) -> bool {
|
||||
self.0.contains_key(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")]
|
||||
|
|
|
|||
|
|
@ -1500,8 +1500,9 @@ mod tests {
|
|||
use insta::{assert_json_snapshot, assert_snapshot};
|
||||
|
||||
use uv_normalize::GroupName;
|
||||
use uv_pypi_types::DependencyGroupSpecifier;
|
||||
|
||||
use crate::pyproject::{DependencyGroupSpecifier, PyProjectToml};
|
||||
use crate::pyproject::PyProjectToml;
|
||||
use crate::workspace::{DiscoveryOptions, ProjectWorkspace};
|
||||
use crate::WorkspaceError;
|
||||
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@ use rustc_hash::FxHashSet;
|
|||
use uv_configuration::{DependencyGroupsWithDefaults, ExtrasSpecification};
|
||||
use uv_distribution_types::Index;
|
||||
use uv_normalize::PackageName;
|
||||
use uv_pypi_types::{LenientRequirement, VerbatimParsedUrl};
|
||||
use uv_pypi_types::{DependencyGroupSpecifier, LenientRequirement, VerbatimParsedUrl};
|
||||
use uv_resolver::{Installable, Lock, Package};
|
||||
use uv_scripts::Pep723Script;
|
||||
use uv_workspace::pyproject::{DependencyGroupSpecifier, Source, Sources, ToolUvSources};
|
||||
use uv_workspace::pyproject::{Source, Sources, ToolUvSources};
|
||||
use uv_workspace::Workspace;
|
||||
|
||||
use crate::commands::project::ProjectError;
|
||||
|
|
|
|||
Loading…
Reference in New Issue