mirror of
https://github.com/astral-sh/uv.git
synced 2025-11-02 04:48:18 +00:00
Add uv-workspace crate with settings discovery and deserialization (#3007)
## Summary This PR adds basic struct definitions along with a "workspace" concept for discovering settings. (The "workspace" terminology is used to match Ruff; I did not invent it.) A few notes: - We discover any `pyproject.toml` or `uv.toml` file in any parent directory of the current working directory. (We could adjust this to look at the directories of the input files.) - We don't actually do anything with the configuration yet; but those PRs are large and I want this to be reviewed in isolation.
This commit is contained in:
parent
c0efeeddf6
commit
295b58ad37
21 changed files with 433 additions and 20 deletions
|
|
@ -3,6 +3,7 @@ use uv_auth::{self, KeyringProvider};
|
|||
/// Keyring provider type to use for credential lookup.
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
|
||||
pub enum KeyringProviderType {
|
||||
/// Do not use keyring for credential lookup.
|
||||
#[default]
|
||||
|
|
|
|||
|
|
@ -198,6 +198,7 @@ impl NoBuild {
|
|||
|
||||
#[derive(Debug, Default, Clone, Hash, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
|
||||
pub enum IndexStrategy {
|
||||
/// Only use results from the first index that returns a match for a given package name.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use serde::ser::SerializeMap;
|
||||
use std::{
|
||||
collections::{btree_map::Entry, BTreeMap},
|
||||
str::FromStr,
|
||||
|
|
@ -35,12 +36,53 @@ enum ConfigSettingValue {
|
|||
List(Vec<String>),
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl serde::Serialize for ConfigSettingValue {
|
||||
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
match self {
|
||||
ConfigSettingValue::String(value) => serializer.serialize_str(value),
|
||||
ConfigSettingValue::List(values) => serializer.collect_seq(values.iter()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de> serde::Deserialize<'de> for ConfigSettingValue {
|
||||
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
struct Visitor;
|
||||
|
||||
impl<'de> serde::de::Visitor<'de> for Visitor {
|
||||
type Value = ConfigSettingValue;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
formatter.write_str("a string or list of strings")
|
||||
}
|
||||
|
||||
fn visit_str<E: serde::de::Error>(self, value: &str) -> Result<Self::Value, E> {
|
||||
Ok(ConfigSettingValue::String(value.to_string()))
|
||||
}
|
||||
|
||||
fn visit_seq<A: serde::de::SeqAccess<'de>>(
|
||||
self,
|
||||
mut seq: A,
|
||||
) -> Result<Self::Value, A::Error> {
|
||||
let mut values = Vec::new();
|
||||
while let Some(value) = seq.next_element()? {
|
||||
values.push(value);
|
||||
}
|
||||
Ok(ConfigSettingValue::List(values))
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_any(Visitor)
|
||||
}
|
||||
}
|
||||
|
||||
/// Settings to pass to a PEP 517 build backend, structured as a map from (string) key to string or
|
||||
/// list of strings.
|
||||
///
|
||||
/// See: <https://peps.python.org/pep-0517/#config-settings>
|
||||
#[derive(Debug, Default, Clone)]
|
||||
#[cfg_attr(not(feature = "serde"), allow(dead_code))]
|
||||
pub struct ConfigSettings(BTreeMap<String, ConfigSettingValue>);
|
||||
|
||||
impl FromIterator<ConfigSettingEntry> for ConfigSettings {
|
||||
|
|
@ -77,23 +119,42 @@ impl ConfigSettings {
|
|||
#[cfg(feature = "serde")]
|
||||
impl serde::Serialize for ConfigSettings {
|
||||
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
use serde::ser::SerializeMap;
|
||||
|
||||
let mut map = serializer.serialize_map(Some(self.0.len()))?;
|
||||
for (key, value) in &self.0 {
|
||||
match value {
|
||||
ConfigSettingValue::String(value) => {
|
||||
map.serialize_entry(&key, &value)?;
|
||||
}
|
||||
ConfigSettingValue::List(values) => {
|
||||
map.serialize_entry(&key, &values)?;
|
||||
}
|
||||
}
|
||||
map.serialize_entry(key, value)?;
|
||||
}
|
||||
map.end()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de> serde::Deserialize<'de> for ConfigSettings {
|
||||
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
struct Visitor;
|
||||
|
||||
impl<'de> serde::de::Visitor<'de> for Visitor {
|
||||
type Value = ConfigSettings;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
formatter.write_str("a map from string to string or list of strings")
|
||||
}
|
||||
|
||||
fn visit_map<A: serde::de::MapAccess<'de>>(
|
||||
self,
|
||||
mut map: A,
|
||||
) -> Result<Self::Value, A::Error> {
|
||||
let mut config = BTreeMap::default();
|
||||
while let Some((key, value)) = map.next_entry()? {
|
||||
config.insert(key, value);
|
||||
}
|
||||
Ok(ConfigSettings(config))
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_map(Visitor)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
|||
|
|
@ -21,6 +21,44 @@ impl FromStr for PackageNameSpecifier {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de> serde::Deserialize<'de> for PackageNameSpecifier {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
struct Visitor;
|
||||
|
||||
impl<'de> serde::de::Visitor<'de> for Visitor {
|
||||
type Value = PackageNameSpecifier;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
formatter.write_str("a package name or `:all:` or `:none:`")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
// Accept the special values `:all:` and `:none:`.
|
||||
match value {
|
||||
":all:" => Ok(PackageNameSpecifier::All),
|
||||
":none:" => Ok(PackageNameSpecifier::None),
|
||||
_ => {
|
||||
// Otherwise, parse the value as a package name.
|
||||
match PackageName::from_str(value) {
|
||||
Ok(name) => Ok(PackageNameSpecifier::Package(name)),
|
||||
Err(err) => Err(E::custom(err)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_str(Visitor)
|
||||
}
|
||||
}
|
||||
|
||||
/// Package name specification.
|
||||
///
|
||||
/// Consumes both package names and selection directives for compatibility with pip flags
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue