Add a generate-all step and auto-generate settings.md (#5080)

## Summary

Ensures that `generate-all` generates both the JSON Schema and the
`settings.md` API reference.
This commit is contained in:
Charlie Marsh 2024-07-15 15:58:53 -04:00 committed by GitHub
parent 6275b54d51
commit 41cd4bee58
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 287 additions and 25 deletions

View file

@ -4,9 +4,9 @@ use std::path::PathBuf;
use anyhow::{Context, Result};
use clap::Parser;
use fs_err as fs;
use rustc_hash::FxHashMap;
use distribution_types::IndexLocations;
use rustc_hash::FxHashMap;
use uv_build::{SourceBuild, SourceBuildContext};
use uv_cache::{Cache, CacheArgs};
use uv_client::RegistryClientBuilder;

View file

@ -2,6 +2,7 @@ use std::path::PathBuf;
use clap::Parser;
use tracing::info;
use uv_cache::{Cache, CacheArgs};
use uv_python::{EnvironmentPreference, PythonEnvironment, PythonRequest};

View file

@ -0,0 +1,30 @@
//! Run all code and documentation generation steps.
use anyhow::Result;
use crate::{generate_json_schema, generate_options_reference};
#[derive(clap::Args)]
pub(crate) struct Args {
#[arg(long, default_value_t, value_enum)]
mode: Mode,
}
#[derive(Copy, Clone, PartialEq, Eq, clap::ValueEnum, Default)]
pub(crate) enum Mode {
/// Update the content in the `configuration.md`.
#[default]
Write,
/// Don't write to the file, check if the file is up-to-date and error if not.
Check,
/// Write the generated help to stdout.
DryRun,
}
pub(crate) fn main(args: &Args) -> Result<()> {
generate_json_schema::main(&generate_json_schema::Args { mode: args.mode })?;
generate_options_reference::main(&generate_options_reference::Args { mode: args.mode })?;
Ok(())
}

View file

@ -9,6 +9,7 @@ use serde::Deserialize;
use uv_distribution::pyproject::ToolUv as WorkspaceOptions;
use uv_settings::Options as SettingsOptions;
use crate::generate_all::Mode;
use crate::ROOT_DIR;
#[derive(Deserialize, JsonSchema)]
@ -25,26 +26,13 @@ struct CombinedOptions {
}
#[derive(clap::Args)]
pub(crate) struct GenerateJsonSchemaArgs {
/// Write the generated table to stdout (rather than to `uv.schema.json`).
pub(crate) struct Args {
/// Write the generated output to stdout (rather than to `uv.schema.json`).
#[arg(long, default_value_t, value_enum)]
mode: Mode,
pub(crate) mode: Mode,
}
#[derive(Copy, Clone, PartialEq, Eq, clap::ValueEnum, Default)]
enum Mode {
/// Update the content in the `configuration.md`.
#[default]
Write,
/// Don't write to the file, check if the file is up-to-date and error if not.
Check,
/// Write the generated help to stdout.
DryRun,
}
pub(crate) fn main(args: &GenerateJsonSchemaArgs) -> Result<()> {
pub(crate) fn main(args: &Args) -> Result<()> {
let schema = schema_for!(CombinedOptions);
let schema_string = serde_json::to_string_pretty(&schema).unwrap();
let filename = "uv.schema.json";
@ -98,9 +86,9 @@ mod tests {
use anyhow::Result;
use crate::generate_json_schema::Mode;
use crate::generate_all::Mode;
use super::{main, GenerateJsonSchemaArgs};
use super::{main, Args};
#[test]
fn test_generate_json_schema() -> Result<()> {
@ -109,6 +97,6 @@ mod tests {
} else {
Mode::Check
};
main(&GenerateJsonSchemaArgs { mode })
main(&Args { mode })
}
}

View file

@ -2,8 +2,12 @@
//!
//! Based on: <https://github.com/astral-sh/ruff/blob/dc8db1afb08704ad6a788c497068b01edf8b460d/crates/ruff_dev/src/generate_options.rs>
use std::fmt::Write;
use std::path::PathBuf;
use anstream::println;
use anyhow::{bail, Result};
use itertools::Itertools;
use pretty_assertions::StrComparison;
use schemars::JsonSchema;
use serde::Deserialize;
@ -12,6 +16,9 @@ use uv_macros::OptionsMetadata;
use uv_options_metadata::{OptionField, OptionSet, OptionsMetadata, Visit};
use uv_settings::Options as SettingsOptions;
use crate::generate_all::Mode;
use crate::ROOT_DIR;
#[derive(Deserialize, JsonSchema, OptionsMetadata)]
#[serde(deny_unknown_fields)]
#[allow(dead_code)]
@ -25,7 +32,65 @@ struct CombinedOptions {
workspace: WorkspaceOptions,
}
pub(crate) fn generate() -> String {
#[derive(clap::Args)]
pub(crate) struct Args {
/// Write the generated output to stdout (rather than to `settings.md`).
#[arg(long, default_value_t, value_enum)]
pub(crate) mode: Mode,
}
pub(crate) fn main(args: &Args) -> Result<()> {
let reference_string = generate();
let filename = "settings.md";
let reference_path = PathBuf::from(ROOT_DIR).join("docs").join(filename);
match args.mode {
Mode::DryRun => {
println!("{reference_string}");
}
Mode::Check => match fs_err::read_to_string(reference_path) {
Ok(current) => {
if current == reference_string {
println!("Up-to-date: {filename}");
} else {
let comparison = StrComparison::new(&current, &reference_string);
bail!("{filename} changed, please run `cargo dev generate-options-reference`:\n{comparison}");
}
}
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
bail!("{filename} not found, please run `cargo dev generate-options-reference`");
}
Err(err) => {
bail!(
"{filename} changed, please run `cargo dev generate-options-reference`:\n{err}"
);
}
},
Mode::Write => {
match fs_err::read_to_string(&reference_path) {
Ok(current) => {
if current == reference_string {
println!("Up-to-date: {filename}");
} else {
println!("Updating: {filename}");
fs_err::write(reference_path, reference_string.as_bytes())?;
}
}
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
println!("Updating: {filename}");
fs_err::write(reference_path, reference_string.as_bytes())?;
}
Err(err) => {
bail!("{filename} changed, please run `cargo dev generate-options-reference`:\n{err}");
}
}
}
}
Ok(())
}
fn generate() -> String {
let mut output = String::new();
generate_set(
@ -219,3 +284,24 @@ impl Visit for CollectOptionsVisitor {
self.fields.push((name.to_owned(), field));
}
}
#[cfg(test)]
mod tests {
use std::env;
use anyhow::Result;
use crate::generate_all::Mode;
use super::{main, Args};
#[test]
fn test_generate_options_reference() -> Result<()> {
let mode = if env::var("UV_UPDATE_SCHEMA").as_deref() == Ok("1") {
Mode::Write
} else {
Mode::Check
};
main(&Args { mode })
}
}

View file

@ -19,7 +19,9 @@ use tracing_subscriber::{EnvFilter, Layer};
use crate::build::{build, BuildArgs};
use crate::clear_compile::ClearCompileArgs;
use crate::compile::CompileArgs;
use crate::generate_json_schema::GenerateJsonSchemaArgs;
use crate::generate_all::Args as GenerateAllArgs;
use crate::generate_json_schema::Args as GenerateJsonSchemaArgs;
use crate::generate_options_reference::Args as GenerateOptionsReferenceArgs;
#[cfg(feature = "render")]
use crate::render_benchmarks::RenderBenchmarksArgs;
use crate::wheel_metadata::WheelMetadataArgs;
@ -43,6 +45,7 @@ static GLOBAL: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
mod build;
mod clear_compile;
mod compile;
mod generate_all;
mod generate_json_schema;
mod generate_options_reference;
mod render_benchmarks;
@ -60,10 +63,12 @@ enum Cli {
Compile(CompileArgs),
/// Remove all `.pyc` in the tree.
ClearCompile(ClearCompileArgs),
/// Run all code and documentation generation steps.
GenerateAll(GenerateAllArgs),
/// Generate JSON schema for the TOML configuration file.
GenerateJSONSchema(GenerateJsonSchemaArgs),
/// Generate the options reference for the documentation.
GenerateOptionsReference,
GenerateOptionsReference(GenerateOptionsReferenceArgs),
#[cfg(feature = "render")]
/// Render the benchmarks.
RenderBenchmarks(RenderBenchmarksArgs),
@ -80,8 +85,9 @@ async fn run() -> Result<()> {
Cli::WheelMetadata(args) => wheel_metadata::wheel_metadata(args).await?,
Cli::Compile(args) => compile::compile(args).await?,
Cli::ClearCompile(args) => clear_compile::clear_compile(&args)?,
Cli::GenerateAll(args) => generate_all::main(&args)?,
Cli::GenerateJSONSchema(args) => generate_json_schema::main(&args)?,
Cli::GenerateOptionsReference => println!("{}", generate_options_reference::generate()),
Cli::GenerateOptionsReference(args) => generate_options_reference::main(&args)?,
#[cfg(feature = "render")]
Cli::RenderBenchmarks(args) => render_benchmarks::render_benchmarks(&args)?,
}

151
docs/settings.md Normal file
View file

@ -0,0 +1,151 @@
### Global
##### [`index-url`](#index-url) {: #index-url }
The URL of the Python package index (by default: <https://pypi.org/simple>).
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).
**Default value**: `https://pypi.org/simple`
**Type**: `str`
**Example usage**:
=== "pyproject.toml"
```toml
[tool.uv]
index-url = "https://pypi.org/simple"
```
=== "uv.toml"
```toml
index-url = "https://pypi.org/simple"
```
---
##### [`managed`](#managed) {: #managed }
Whether the project is managed by `uv`. If `false`, `uv` will ignore the project when
`uv run` is invoked.
**Default value**: `true`
**Type**: `bool`
**Example usage**:
=== "pyproject.toml"
```toml
[tool.uv]
managed = false
```
=== "uv.toml"
```toml
managed = false
```
---
#### `pip`
A `[tool.uv.pip]` section.
##### [`no-header`](#pip_no-header) {: #pip_no-header }
<span id="no-header"></span>
Exclude the comment header at the top of output file generated by `uv pip compile`.
**Default value**: `false`
**Type**: `bool`
**Example usage**:
=== "pyproject.toml"
```toml
[tool.uv.pip]
no-header = true
```
=== "uv.toml"
```toml
[pip]
no-header = true
```
---
#### `workspace`
##### [`exclude`](#workspace_exclude) {: #workspace_exclude }
<span id="exclude"></span>
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).
**Default value**: `[]`
**Type**: `list[str]`
**Example usage**:
=== "pyproject.toml"
```toml
[tool.uv.workspace]
exclude = ["member1", "path/to/member2", "libs/*"]
```
=== "uv.toml"
```toml
[workspace]
exclude = ["member1", "path/to/member2", "libs/*"]
```
---
##### [`members`](#workspace_members) {: #workspace_members }
<span id="members"></span>
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).
**Default value**: `[]`
**Type**: `list[str]`
**Example usage**:
=== "pyproject.toml"
```toml
[tool.uv.workspace]
members = ["member1", "path/to/member2", "libs/*"]
```
=== "uv.toml"
```toml
[workspace]
members = ["member1", "path/to/member2", "libs/*"]
```
---