From 6275b54d5168b24e9ecac2ae4e5c04cdba6e57f8 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Mon, 15 Jul 2024 15:48:40 -0400 Subject: [PATCH] Generate API reference for options documentation (#5072) ## Summary Generates Markdown from the `OptionsMetadata`, following the same strategy as in Ruff. ## Test Plan `cargo dev generate-options-reference` --- Cargo.lock | 14 ++ Cargo.toml | 1 + crates/uv-dev/Cargo.toml | 6 +- crates/uv-dev/src/generate_json_schema.rs | 11 +- .../uv-dev/src/generate_options_reference.rs | 221 ++++++++++++++++++ crates/uv-dev/src/main.rs | 4 + crates/uv-distribution/Cargo.toml | 5 + crates/uv-distribution/src/pyproject.rs | 38 ++- crates/uv-macros/src/options_metadata.rs | 10 +- crates/uv-options-metadata/Cargo.toml | 16 ++ .../src/lib.rs} | 10 +- crates/uv-settings/Cargo.toml | 6 +- crates/uv-settings/src/lib.rs | 1 - crates/uv-settings/src/settings.rs | 17 +- uv.schema.json | 5 +- 15 files changed, 343 insertions(+), 22 deletions(-) create mode 100644 crates/uv-dev/src/generate_options_reference.rs create mode 100644 crates/uv-options-metadata/Cargo.toml rename crates/{uv-settings/src/options_base.rs => uv-options-metadata/src/lib.rs} (96%) diff --git a/Cargo.lock b/Cargo.lock index cbfc4e4c4..d37e96951 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4683,6 +4683,7 @@ dependencies = [ "distribution-types", "fs-err", "install-wheel-rs", + "itertools 0.13.0", "mimalloc", "owo-colors", "pep508_rs", @@ -4695,6 +4696,7 @@ dependencies = [ "serde", "serde_json", "tagu", + "textwrap", "tikv-jemallocator", "tokio", "tracing", @@ -4708,6 +4710,8 @@ dependencies = [ "uv-distribution", "uv-git", "uv-installer", + "uv-macros", + "uv-options-metadata", "uv-python", "uv-resolver", "uv-settings", @@ -4781,7 +4785,9 @@ dependencies = [ "uv-extract", "uv-fs", "uv-git", + "uv-macros", "uv-normalize", + "uv-options-metadata", "uv-types", "uv-warnings", "zip", @@ -4905,6 +4911,13 @@ dependencies = [ "serde", ] +[[package]] +name = "uv-options-metadata" +version = "0.0.1" +dependencies = [ + "serde", +] + [[package]] name = "uv-python" version = "0.0.1" @@ -5071,6 +5084,7 @@ dependencies = [ "uv-fs", "uv-macros", "uv-normalize", + "uv-options-metadata", "uv-python", "uv-resolver", "uv-warnings", diff --git a/Cargo.toml b/Cargo.toml index 4a9b0e3ef..812deb841 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,7 @@ uv-git = { path = "crates/uv-git" } uv-installer = { path = "crates/uv-installer" } uv-macros = { path = "crates/uv-macros" } uv-normalize = { path = "crates/uv-normalize" } +uv-options-metadata = { path = "crates/uv-options-metadata" } uv-python = { path = "crates/uv-python" } uv-requirements = { path = "crates/uv-requirements" } uv-resolver = { path = "crates/uv-resolver" } diff --git a/crates/uv-dev/Cargo.toml b/crates/uv-dev/Cargo.toml index 68ac33abd..71f3134b8 100644 --- a/crates/uv-dev/Cargo.toml +++ b/crates/uv-dev/Cargo.toml @@ -29,9 +29,11 @@ uv-dispatch = { workspace = true } uv-distribution = { workspace = true, features = ["schemars"] } uv-git = { workspace = true } uv-installer = { workspace = true } +uv-macros = { workspace = true } +uv-options-metadata = { workspace = true } +uv-python = { workspace = true } uv-resolver = { workspace = true } uv-settings = { workspace = true, features = ["schemars"] } -uv-python = { workspace = true } uv-types = { workspace = true } # Any dependencies that are exclusively used in `uv-dev` should be listed as non-workspace @@ -40,6 +42,7 @@ anstream = { workspace = true } anyhow = { workspace = true } clap = { workspace = true, features = ["derive", "wrap_help"] } fs-err = { workspace = true, features = ["tokio"] } +itertools = { workspace = true } owo-colors = { workspace = true } poloto = { version = "19.1.2", optional = true } pretty_assertions = { version = "1.4.0" } @@ -49,6 +52,7 @@ schemars = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } tagu = { version = "0.1.6", optional = true } +textwrap = { workspace = true } tokio = { workspace = true } tracing = { workspace = true } tracing-durations-export = { workspace = true, features = ["plot"] } diff --git a/crates/uv-dev/src/generate_json_schema.rs b/crates/uv-dev/src/generate_json_schema.rs index d3d342d0b..2a7253e17 100644 --- a/crates/uv-dev/src/generate_json_schema.rs +++ b/crates/uv-dev/src/generate_json_schema.rs @@ -6,7 +6,8 @@ use pretty_assertions::StrComparison; use schemars::{schema_for, JsonSchema}; use serde::Deserialize; -use uv_settings::Options; +use uv_distribution::pyproject::ToolUv as WorkspaceOptions; +use uv_settings::Options as SettingsOptions; use crate::ROOT_DIR; @@ -16,11 +17,11 @@ use crate::ROOT_DIR; // The names and docstrings of this struct and the types it contains are used as `title` and // `description` in uv.schema.json, see https://github.com/SchemaStore/schemastore/blob/master/editor-features.md#title-as-an-expected-object-type /// Metadata and configuration for uv. -struct ToolUv { +struct CombinedOptions { #[serde(flatten)] - options: Options, + options: SettingsOptions, #[serde(flatten)] - dep_spec: uv_distribution::pyproject::ToolUv, + workspace: WorkspaceOptions, } #[derive(clap::Args)] @@ -44,7 +45,7 @@ enum Mode { } pub(crate) fn main(args: &GenerateJsonSchemaArgs) -> Result<()> { - let schema = schema_for!(ToolUv); + let schema = schema_for!(CombinedOptions); let schema_string = serde_json::to_string_pretty(&schema).unwrap(); let filename = "uv.schema.json"; let schema_path = PathBuf::from(ROOT_DIR).join(filename); diff --git a/crates/uv-dev/src/generate_options_reference.rs b/crates/uv-dev/src/generate_options_reference.rs new file mode 100644 index 000000000..a6d245710 --- /dev/null +++ b/crates/uv-dev/src/generate_options_reference.rs @@ -0,0 +1,221 @@ +//! Generate a Markdown-compatible listing of configuration options for `pyproject.toml`. +//! +//! Based on: +use std::fmt::Write; + +use itertools::Itertools; +use schemars::JsonSchema; +use serde::Deserialize; + +use uv_distribution::pyproject::ToolUv as WorkspaceOptions; +use uv_macros::OptionsMetadata; +use uv_options_metadata::{OptionField, OptionSet, OptionsMetadata, Visit}; +use uv_settings::Options as SettingsOptions; + +#[derive(Deserialize, JsonSchema, OptionsMetadata)] +#[serde(deny_unknown_fields)] +#[allow(dead_code)] +// The names and docstrings of this struct and the types it contains are used as `title` and +// `description` in uv.schema.json, see https://github.com/SchemaStore/schemastore/blob/master/editor-features.md#title-as-an-expected-object-type +/// Metadata and configuration for uv. +struct CombinedOptions { + #[serde(flatten)] + options: SettingsOptions, + #[serde(flatten)] + workspace: WorkspaceOptions, +} + +pub(crate) fn generate() -> String { + let mut output = String::new(); + + generate_set( + &mut output, + Set::Global(CombinedOptions::metadata()), + &mut Vec::new(), + ); + + output +} + +fn generate_set(output: &mut String, set: Set, parents: &mut Vec) { + match &set { + Set::Global(_) => { + output.push_str("### Global\n"); + } + Set::Named { name, .. } => { + let title = parents + .iter() + .filter_map(|set| set.name()) + .chain(std::iter::once(name.as_str())) + .join("."); + writeln!(output, "#### `{title}`\n").unwrap(); + + if let Some(documentation) = set.metadata().documentation() { + output.push_str(documentation); + output.push('\n'); + output.push('\n'); + } + } + } + + let mut visitor = CollectOptionsVisitor::default(); + set.metadata().record(&mut visitor); + + let (mut fields, mut sets) = (visitor.fields, visitor.groups); + + fields.sort_unstable_by(|(name, _), (name2, _)| name.cmp(name2)); + sets.sort_unstable_by(|(name, _), (name2, _)| name.cmp(name2)); + + parents.push(set); + + // Generate the fields. + for (name, field) in &fields { + emit_field(output, name, field, parents.as_slice()); + output.push_str("---\n\n"); + } + + // Generate all the sub-sets. + for (set_name, sub_set) in &sets { + generate_set( + output, + Set::Named { + name: set_name.to_string(), + set: *sub_set, + }, + parents, + ); + } + + parents.pop(); +} + +enum Set { + Global(OptionSet), + Named { name: String, set: OptionSet }, +} + +impl Set { + fn name(&self) -> Option<&str> { + match self { + Set::Global(_) => None, + Set::Named { name, .. } => Some(name), + } + } + + fn metadata(&self) -> &OptionSet { + match self { + Set::Global(set) => set, + Set::Named { set, .. } => set, + } + } +} + +fn emit_field(output: &mut String, name: &str, field: &OptionField, parents: &[Set]) { + let header_level = if parents.is_empty() { "####" } else { "#####" }; + let parents_anchor = parents.iter().filter_map(|parent| parent.name()).join("_"); + + if parents_anchor.is_empty() { + output.push_str(&format!( + "{header_level} [`{name}`](#{name}) {{: #{name} }}\n" + )); + } else { + output.push_str(&format!( + "{header_level} [`{name}`](#{parents_anchor}_{name}) {{: #{parents_anchor}_{name} }}\n" + )); + + // the anchor used to just be the name, but now it's the group name + // for backwards compatibility, we need to keep the old anchor + output.push_str(&format!("\n")); + } + + output.push('\n'); + + if let Some(deprecated) = &field.deprecated { + output.push_str("!!! warning \"Deprecated\"\n"); + output.push_str(" This option has been deprecated"); + + if let Some(since) = deprecated.since { + write!(output, " in {since}").unwrap(); + } + + output.push('.'); + + if let Some(message) = deprecated.message { + writeln!(output, " {message}").unwrap(); + } + + output.push('\n'); + } + + output.push_str(field.doc); + output.push_str("\n\n"); + output.push_str(&format!("**Default value**: `{}`\n", field.default)); + output.push('\n'); + output.push_str(&format!("**Type**: `{}`\n", field.value_type)); + output.push('\n'); + output.push_str("**Example usage**:\n\n"); + output.push_str(&format_tab( + "pyproject.toml", + &format_header(field.scope, parents, ConfigurationFile::PyprojectToml), + field.example, + )); + output.push_str(&format_tab( + "uv.toml", + &format_header(field.scope, parents, ConfigurationFile::UvToml), + field.example, + )); + output.push('\n'); +} + +fn format_tab(tab_name: &str, header: &str, content: &str) -> String { + format!( + "=== \"{}\"\n\n ```toml\n {}\n{}\n ```\n", + tab_name, + header, + textwrap::indent(content, " ") + ) +} + +/// Format the TOML header for the example usage for a given option. +/// +/// For example: `[tool.uv.pip]`. +fn format_header(scope: Option<&str>, parents: &[Set], configuration: ConfigurationFile) -> String { + let tool_parent = match configuration { + ConfigurationFile::PyprojectToml => Some("tool.uv"), + ConfigurationFile::UvToml => None, + }; + + let header = tool_parent + .into_iter() + .chain(parents.iter().filter_map(|parent| parent.name())) + .chain(scope) + .join("."); + + if header.is_empty() { + String::new() + } else { + format!("[{header}]") + } +} + +#[derive(Debug, Copy, Clone)] +enum ConfigurationFile { + PyprojectToml, + UvToml, +} + +#[derive(Default)] +struct CollectOptionsVisitor { + groups: Vec<(String, OptionSet)>, + fields: Vec<(String, OptionField)>, +} + +impl Visit for CollectOptionsVisitor { + fn record_set(&mut self, name: &str, group: OptionSet) { + self.groups.push((name.to_owned(), group)); + } + + fn record_field(&mut self, name: &str, field: OptionField) { + self.fields.push((name.to_owned(), field)); + } +} diff --git a/crates/uv-dev/src/main.rs b/crates/uv-dev/src/main.rs index bacb98081..da3802152 100644 --- a/crates/uv-dev/src/main.rs +++ b/crates/uv-dev/src/main.rs @@ -44,6 +44,7 @@ mod build; mod clear_compile; mod compile; mod generate_json_schema; +mod generate_options_reference; mod render_benchmarks; mod wheel_metadata; @@ -61,6 +62,8 @@ enum Cli { ClearCompile(ClearCompileArgs), /// Generate JSON schema for the TOML configuration file. GenerateJSONSchema(GenerateJsonSchemaArgs), + /// Generate the options reference for the documentation. + GenerateOptionsReference, #[cfg(feature = "render")] /// Render the benchmarks. RenderBenchmarks(RenderBenchmarksArgs), @@ -78,6 +81,7 @@ async fn run() -> Result<()> { Cli::Compile(args) => compile::compile(args).await?, Cli::ClearCompile(args) => clear_compile::clear_compile(&args)?, Cli::GenerateJSONSchema(args) => generate_json_schema::main(&args)?, + Cli::GenerateOptionsReference => println!("{}", generate_options_reference::generate()), #[cfg(feature = "render")] Cli::RenderBenchmarks(args) => render_benchmarks::render_benchmarks(&args)?, } diff --git a/crates/uv-distribution/Cargo.toml b/crates/uv-distribution/Cargo.toml index b61f0512f..7a0f9c203 100644 --- a/crates/uv-distribution/Cargo.toml +++ b/crates/uv-distribution/Cargo.toml @@ -26,7 +26,9 @@ uv-configuration = { workspace = true } uv-extract = { workspace = true } uv-fs = { workspace = true, features = ["tokio"] } uv-git = { workspace = true } +uv-macros = { workspace = true } uv-normalize = { workspace = true } +uv-options-metadata = { workspace = true } uv-types = { workspace = true } uv-warnings = { workspace = true } @@ -61,3 +63,6 @@ regex = { workspace = true } [features] schemars = ["dep:schemars"] + +[package.metadata.cargo-shear] +ignored = ["uv-options-metadata"] diff --git a/crates/uv-distribution/src/pyproject.rs b/crates/uv-distribution/src/pyproject.rs index 144527d97..8266982e6 100644 --- a/crates/uv-distribution/src/pyproject.rs +++ b/crates/uv-distribution/src/pyproject.rs @@ -17,6 +17,7 @@ use url::Url; use pep440_rs::VersionSpecifiers; use pypi_types::{RequirementSource, VerbatimParsedUrl}; use uv_git::GitReference; +use uv_macros::OptionsMetadata; use uv_normalize::{ExtraName, PackageName}; /// A `pyproject.toml` as specified in PEP 517. @@ -69,15 +70,23 @@ pub struct Tool { pub uv: Option, } -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +#[derive(Serialize, Deserialize, OptionsMetadata, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "kebab-case")] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub struct ToolUv { pub sources: Option>, /// The workspace definition for the project, if any. + #[option_group] pub workspace: Option, /// Whether the project is managed by `uv`. If `false`, `uv` will ignore the project when /// `uv run` is invoked. + #[option( + default = r#"true"#, + value_type = "bool", + example = r#" + managed = false + "# + )] pub managed: Option, #[cfg_attr( feature = "schemars", @@ -97,10 +106,35 @@ pub struct ToolUv { pub override_dependencies: Option>>, } -#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq, Eq)] +#[derive(Serialize, Deserialize, OptionsMetadata, Default, Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub struct ToolUvWorkspace { + /// Packages to include as workspace members. + /// + /// Supports both globs and explicit paths. + /// + /// For more information on the glob syntax, refer to the [`glob` documentation](https://docs.rs/glob/latest/glob/struct.Pattern.html). + #[option( + default = r#"[]"#, + value_type = "list[str]", + example = r#" + members = ["member1", "path/to/member2", "libs/*"] + "# + )] pub members: Option>, + /// Packages to exclude as workspace members. If a package matches both `members` and + /// `exclude`, it will be excluded. + /// + /// Supports both globs and explicit paths. + /// + /// For more information on the glob syntax, refer to the [`glob` documentation](https://docs.rs/glob/latest/glob/struct.Pattern.html). + #[option( + default = r#"[]"#, + value_type = "list[str]", + example = r#" + exclude = ["member1", "path/to/member2", "libs/*"] + "# + )] pub exclude: Option>, } diff --git a/crates/uv-macros/src/options_metadata.rs b/crates/uv-macros/src/options_metadata.rs index e4fa7778c..255a2a457 100644 --- a/crates/uv-macros/src/options_metadata.rs +++ b/crates/uv-macros/src/options_metadata.rs @@ -91,8 +91,8 @@ pub(crate) fn derive_impl(input: DeriveInput) -> syn::Result { Ok(quote! { #[automatically_derived] - impl crate::options_base::OptionsMetadata for #ident { - fn record(visit: &mut dyn crate::options_base::Visit) { + impl uv_options_metadata::OptionsMetadata for #ident { + fn record(visit: &mut dyn uv_options_metadata::Visit) { #(#output);* } @@ -130,7 +130,7 @@ fn handle_option_group(field: &Field) -> syn::Result { let kebab_name = LitStr::new(&ident.to_string().replace('_', "-"), ident.span()); Ok(quote_spanned!( - ident.span() => (visit.record_set(#kebab_name, crate::options_base::OptionSet::of::<#path>())) + ident.span() => (visit.record_set(#kebab_name, uv_options_metadata::OptionSet::of::<#path>())) )) } _ => Err(syn::Error::new( @@ -219,14 +219,14 @@ fn handle_option(field: &Field, attr: &Attribute) -> syn::Result { - visit.record_field(#kebab_name, crate::options_base::OptionField{ + visit.record_field(#kebab_name, uv_options_metadata::OptionField{ doc: &#doc, default: &#default, value_type: &#value_type, diff --git a/crates/uv-options-metadata/Cargo.toml b/crates/uv-options-metadata/Cargo.toml new file mode 100644 index 000000000..24fc367c0 --- /dev/null +++ b/crates/uv-options-metadata/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "uv-options-metadata" +version = "0.0.1" +edition = { workspace = true } +rust-version = { workspace = true } +homepage = { workspace = true } +documentation = { workspace = true } +repository = { workspace = true } +authors = { workspace = true } +license = { workspace = true } + +[lints] +workspace = true + +[dependencies] +serde = { workspace = true } diff --git a/crates/uv-settings/src/options_base.rs b/crates/uv-options-metadata/src/lib.rs similarity index 96% rename from crates/uv-settings/src/options_base.rs rename to crates/uv-options-metadata/src/lib.rs index ba93ddde9..c5e9a34c9 100644 --- a/crates/uv-settings/src/options_base.rs +++ b/crates/uv-options-metadata/src/lib.rs @@ -1,6 +1,6 @@ //! Taken directly from Ruff. //! -//! See: +//! See: use serde::{Serialize, Serializer}; use std::collections::BTreeMap; @@ -106,7 +106,7 @@ impl OptionSet { /// ### Test for the existence of a child option /// /// ```rust - /// # use uv_settings::options_base::{OptionField, OptionsMetadata, Visit}; + /// # use uv_options_metadata::{OptionField, OptionsMetadata, Visit}; /// /// struct WithOptions; /// @@ -129,7 +129,7 @@ impl OptionSet { /// ### Test for the existence of a nested option /// /// ```rust - /// # use uv_settings::options_base::{OptionField, OptionsMetadata, Visit}; + /// # use uv_options_metadata::{OptionField, OptionsMetadata, Visit}; /// /// struct Root; /// @@ -180,7 +180,7 @@ impl OptionSet { /// ### Find a child option /// /// ```rust - /// # use uv_settings::options_base::{OptionEntry, OptionField, OptionsMetadata, Visit}; + /// # use uv_options_metadata::{OptionEntry, OptionField, OptionsMetadata, Visit}; /// /// struct WithOptions; /// @@ -205,7 +205,7 @@ impl OptionSet { /// ### Find a nested option /// /// ```rust - /// # use uv_settings::options_base::{OptionEntry, OptionField, OptionsMetadata, Visit}; + /// # use uv_options_metadata::{OptionEntry, OptionField, OptionsMetadata, Visit}; /// /// static HARD_TABS: OptionField = OptionField { /// doc: "Use hard tabs for indentation and spaces for alignment.", diff --git a/crates/uv-settings/Cargo.toml b/crates/uv-settings/Cargo.toml index e9540a1d3..2d99d6335 100644 --- a/crates/uv-settings/Cargo.toml +++ b/crates/uv-settings/Cargo.toml @@ -21,8 +21,9 @@ uv-configuration = { workspace = true, features = ["schemars"] } uv-fs = { workspace = true } uv-macros = { workspace = true } uv-normalize = { workspace = true, features = ["schemars"] } -uv-resolver = { workspace = true, features = ["schemars"] } +uv-options-metadata = { workspace = true } uv-python = { workspace = true, features = ["schemars"] } +uv-resolver = { workspace = true, features = ["schemars"] } uv-warnings = { workspace = true } dirs-sys = { workspace = true } @@ -32,3 +33,6 @@ serde = { workspace = true } thiserror = { workspace = true } toml = { workspace = true } tracing = { workspace = true } + +[package.metadata.cargo-shear] +ignored = ["uv-options-metadata"] diff --git a/crates/uv-settings/src/lib.rs b/crates/uv-settings/src/lib.rs index eb5ca350c..38c5d463e 100644 --- a/crates/uv-settings/src/lib.rs +++ b/crates/uv-settings/src/lib.rs @@ -10,7 +10,6 @@ pub use crate::combine::*; pub use crate::settings::*; mod combine; -pub mod options_base; mod settings; /// The [`Options`] as loaded from a configuration file on disk. diff --git a/crates/uv-settings/src/settings.rs b/crates/uv-settings/src/settings.rs index a6ab3430b..2394d7813 100644 --- a/crates/uv-settings/src/settings.rs +++ b/crates/uv-settings/src/settings.rs @@ -30,7 +30,7 @@ pub(crate) struct Tools { /// A `[tool.uv]` section. #[allow(dead_code)] -#[derive(Debug, Clone, Default, Deserialize, CombineOptions)] +#[derive(Debug, Clone, Default, Deserialize, CombineOptions, OptionsMetadata)] #[serde(rename_all = "kebab-case")] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub struct Options { @@ -38,6 +38,7 @@ pub struct Options { pub globals: GlobalOptions, #[serde(flatten)] pub top_level: ResolverInstallerOptions, + #[option_group] pub pip: Option, #[cfg_attr( feature = "schemars", @@ -120,6 +121,20 @@ pub struct ResolverOptions { #[serde(rename_all = "kebab-case")] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub struct ResolverInstallerOptions { + /// The URL of the Python package index (by default: ). + /// + /// Accepts either a repository compliant with PEP 503 (the simple repository API), or a local + /// directory laid out in the same format. + /// + /// The index provided by this setting is given lower priority than any indexes specified via + /// [`extra_index_url`](#extra-index-url). + #[option( + default = "https://pypi.org/simple", + value_type = "str", + example = r#" + index-url = "https://pypi.org/simple" + "# + )] pub index_url: Option, pub extra_index_url: Option>, pub no_index: Option, diff --git a/uv.schema.json b/uv.schema.json index 4fa71c265..c15c160ea 100644 --- a/uv.schema.json +++ b/uv.schema.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "ToolUv", + "title": "CombinedOptions", "description": "Metadata and configuration for uv.", "type": "object", "properties": { @@ -75,6 +75,7 @@ ] }, "index-url": { + "description": "The URL of the Python package index (by default: ).\n\nAccepts either a repository compliant with PEP 503 (the simple repository API), or a local directory laid out in the same format.\n\nThe index provided by this setting is given lower priority than any indexes specified via [`extra_index_url`](#extra-index-url).", "anyOf": [ { "$ref": "#/definitions/IndexUrl" @@ -1260,6 +1261,7 @@ "type": "object", "properties": { "exclude": { + "description": "Packages to exclude as workspace members. If a package matches both `members` and `exclude`, it will be excluded.\n\nSupports both globs and explicit paths.\n\nFor more information on the glob syntax, refer to the [`glob` documentation](https://docs.rs/glob/latest/glob/struct.Pattern.html).", "type": [ "array", "null" @@ -1269,6 +1271,7 @@ } }, "members": { + "description": "Packages to include as workspace members.\n\nSupports both globs and explicit paths.\n\nFor more information on the glob syntax, refer to the [`glob` documentation](https://docs.rs/glob/latest/glob/struct.Pattern.html).", "type": [ "array", "null"