Skip to content

Commit

Permalink
Add JSON Schema support (astral-sh#3046)
Browse files Browse the repository at this point in the history
## Summary

This PR adds JSON Schema support. The setup mirrors Ruff's own.
  • Loading branch information
charliermarsh authored Apr 17, 2024
1 parent 7c5b13c commit 7fb2bf8
Show file tree
Hide file tree
Showing 31 changed files with 818 additions and 26 deletions.
2 changes: 2 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[alias]
dev = "run --package uv-dev"
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
* text=auto eol=lf

uv.schema.json linguist-generated=true text=auto eol=lf
13 changes: 7 additions & 6 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ We have issues labeled as [Good First Issue](https://github.com/astral-sh/uv/iss

### Linux

On Ubuntu and other Debian-based distributions, you can install the C compiler and CMake with
On Ubuntu and other Debian-based distributions, you can install the C compiler and CMake with:

```shell
sudo apt install build-essential cmake
```

### macOS

CMake may be installed with Homebrew:
You can install CMake with Homebrew:

```shell
brew install cmake
Expand All @@ -26,13 +26,14 @@ See the [Python](#python) section for instructions on installing the Python vers

### Windows

You can install CMake from the [installers](https://cmake.org/download/) or with `pipx install cmake`
(make sure that the pipx install path is in `PATH`, pipx complains if it isn't).
You can install CMake from the [installers](https://cmake.org/download/) or with `pipx install cmake`.

## Testing

For running tests, we recommend [nextest](https://nexte.st/).

If tests fail due to a mismatch in the JSON Schema, run: `cargo dev generate-json-schema`.

### Python

Testing uv requires multiple specific Python versions. You can install them into
Expand Down Expand Up @@ -87,7 +88,7 @@ python -m scripts.bench \
./scripts/requirements/jupyter.in --benchmark resolve-cold --min-runs 20
```

### Analysing concurrency
### Analyzing concurrency

You can use [tracing-durations-export](https://github.com/konstin/tracing-durations-export) to visualize parallel requests and find any spots where uv is CPU-bound. Example usage, with `uv` and `uv-dev` respectively:

Expand All @@ -104,7 +105,7 @@ RUST_LOG=uv=info TRACING_DURATIONS_FILE=target/traces/jupyter.ndjson cargo run -
You can enable `trace` level logging using the `RUST_LOG` environment variable, i.e.

```shell
RUST_LOG=trace uv
RUST_LOG=trace uv
```

## Releases
Expand Down
35 changes: 33 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion crates/distribution-types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,18 @@ distribution-filename = { workspace = true, features = ["serde"] }
pep440_rs = { workspace = true }
pep508_rs = { workspace = true }
platform-tags = { workspace = true }
pypi-types = { workspace = true }
uv-fs = { workspace = true }
uv-git = { workspace = true, features = ["vendored-openssl"] }
uv-normalize = { workspace = true }
pypi-types = { workspace = true }

anyhow = { workspace = true }
fs-err = { workspace = true }
itertools = { workspace = true }
once_cell = { workspace = true }
rkyv = { workspace = true }
rustc-hash = { workspace = true }
schemars = { workspace = true, optional = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
thiserror = { workspace = true }
Expand Down
30 changes: 30 additions & 0 deletions crates/distribution-types/src/index_url.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,21 @@ pub enum IndexUrl {
Path(VerbatimUrl),
}

#[cfg(feature = "schemars")]
impl schemars::JsonSchema for IndexUrl {
fn schema_name() -> String {
"IndexUrl".to_string()
}

fn json_schema(_gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
schemars::schema::SchemaObject {
instance_type: Some(schemars::schema::InstanceType::String.into()),
..schemars::schema::SchemaObject::default()
}
.into()
}
}

impl IndexUrl {
/// Return the raw URL for the index.
pub fn url(&self) -> &Url {
Expand Down Expand Up @@ -113,6 +128,21 @@ pub enum FlatIndexLocation {
Url(Url),
}

#[cfg(feature = "schemars")]
impl schemars::JsonSchema for FlatIndexLocation {
fn schema_name() -> String {
"FlatIndexLocation".to_string()
}

fn json_schema(_gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
schemars::schema::SchemaObject {
instance_type: Some(schemars::schema::InstanceType::String.into()),
..schemars::schema::SchemaObject::default()
}
.into()
}
}

impl FromStr for FlatIndexLocation {
type Err = url::ParseError;

Expand Down
1 change: 1 addition & 0 deletions crates/install-wheel-rs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ platform-info = { workspace = true }
reflink-copy = { workspace = true }
regex = { workspace = true }
rustc-hash = { workspace = true }
schemars = { workspace = true, optional = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
sha2 = { workspace = true }
Expand Down
1 change: 1 addition & 0 deletions crates/install-wheel-rs/src/linker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ fn parse_scripts(
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub enum LinkMode {
/// Clone (i.e., copy-on-write) packages from the wheel into the site packages.
Clone,
Expand Down
1 change: 1 addition & 0 deletions crates/uv-auth/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ once_cell = { workspace = true }
reqwest = { workspace = true }
reqwest-middleware = { workspace = true }
rust-netrc = { workspace = true }
schemars = { workspace = true, optional = true }
serde = { workspace = true, optional = true }
thiserror = { workspace = true }
tracing = { workspace = true }
Expand Down
1 change: 1 addition & 0 deletions crates/uv-configuration/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ anyhow = { workspace = true }
clap = { workspace = true, features = ["derive"], optional = true }
itertools = { workspace = true }
rustc-hash = { workspace = true }
schemars = { workspace = true, optional = true }
serde = { workspace = true, optional = true }
serde_json = { workspace = true, optional = true }

Expand Down
1 change: 1 addition & 0 deletions crates/uv-configuration/src/authentication.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use uv_auth::{self, KeyringProvider};
feature = "serde",
serde(deny_unknown_fields, rename_all = "kebab-case")
)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub enum KeyringProviderType {
/// Do not use keyring for credential lookup.
#[default]
Expand Down
1 change: 1 addition & 0 deletions crates/uv-configuration/src/build_options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ impl NoBuild {
feature = "serde",
serde(deny_unknown_fields, rename_all = "kebab-case")
)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub enum IndexStrategy {
/// Only use results from the first index that returns a match for a given package name.
///
Expand Down
2 changes: 2 additions & 0 deletions crates/uv-configuration/src/config_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ impl FromStr for ConfigSettingEntry {
}

#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
enum ConfigSettingValue {
/// The value consists of a single string.
String(String),
Expand Down Expand Up @@ -82,6 +83,7 @@ impl<'de> serde::Deserialize<'de> for ConfigSettingValue {
///
/// See: <https://peps.python.org/pep-0517/#config-settings>
#[derive(Debug, Default, Clone)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(not(feature = "serde"), allow(dead_code))]
pub struct ConfigSettings(BTreeMap<String, ConfigSettingValue>);

Expand Down
23 changes: 23 additions & 0 deletions crates/uv-configuration/src/name_specifiers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,29 @@ impl<'de> serde::Deserialize<'de> for PackageNameSpecifier {
}
}

#[cfg(feature = "schemars")]
impl schemars::JsonSchema for PackageNameSpecifier {
fn schema_name() -> String {
"PackageNameSpecifier".to_string()
}

fn json_schema(_gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
schemars::schema::SchemaObject {
instance_type: Some(schemars::schema::InstanceType::String.into()),
string: Some(Box::new(schemars::schema::StringValidation {
// See: https://packaging.python.org/en/latest/specifications/name-normalization/#name-format
pattern: Some(
r"^(:none:|:all:|([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9]))$"
.to_string(),
),
..schemars::schema::StringValidation::default()
})),
..schemars::schema::SchemaObject::default()
}
.into()
}
}

/// Package name specification.
///
/// Consumes both package names and selection directives for compatibility with pip flags
Expand Down
3 changes: 3 additions & 0 deletions crates/uv-dev/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ uv-normalize = { workspace = true }
uv-resolver = { workspace = true }
uv-toolchain = { workspace = true }
uv-types = { workspace = true }
uv-workspace = { workspace = true, features = ["schemars"] }

# Any dependencies that are exclusively used in `uv-dev` should be listed as non-workspace
# dependencies, to ensure that we're forced to think twice before including them in other crates.
Expand All @@ -47,8 +48,10 @@ itertools = { workspace = true }
owo-colors = { workspace = true }
petgraph = { workspace = true }
poloto = { version = "19.1.2" }
pretty_assertions = { version = "1.4.0" }
resvg = { version = "0.29.0" }
rustc-hash = { workspace = true }
schemars = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
tagu = { version = "0.1.6" }
Expand Down
Loading

0 comments on commit 7fb2bf8

Please sign in to comment.