build: Update builds

Signed-off-by: Dmitry Dygalo <dmitry@dygalo.dev>
This commit is contained in:
Dmitry Dygalo 2024-04-14 17:25:13 +02:00 committed by Dmitry Dygalo
parent 79e35a2012
commit 9aae87e573
13 changed files with 251 additions and 209 deletions

View File

@ -10,26 +10,24 @@ jobs:
commitsar:
name: Verify commit messages
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- name: Check out code
uses: actions/checkout@v3.0.0
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Run commitsar
uses: aevea/commitsar@v0.18.0
- uses: aevea/commitsar@v0.20.2
pre-commit:
name: Generic pre-commit checks
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 1
- uses: actions/setup-python@v4
- uses: actions/setup-python@v5
with:
python-version: 3.9
python-version: 3.11
- run: pip install pre-commit
- run: pre-commit run --all-files
@ -39,30 +37,23 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-20.04, macos-12, windows-2022]
os: [ubuntu-22.04, macos-12, windows-2022]
draft: [draft201909, draft202012]
name: Test ${{ matrix.draft }} (stable) on ${{ matrix.os}}
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: true
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
- uses: dtolnay/rust-toolchain@stable
- name: Cache cargo
uses: actions/cache@v3
- uses: Swatinem/rust-cache@v2
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: stable-${{ matrix.os }}-${{ matrix.draft }}-cargo-cache
workspaces: jsonschema
cache-all-crates: "true"
key: ${{ matrix.os }}-${{ matrix.draft }}
- run: cargo test --no-fail-fast --features ${{ matrix.draft }}
working-directory: ./jsonschema
@ -74,78 +65,72 @@ jobs:
target: ['wasm32-unknown-unknown']
name: Build on ${{ matrix.target }}
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: true
- uses: actions-rs/toolchain@v1
- uses: dtolnay/rust-toolchain@stable
with:
profile: minimal
toolchain: stable
target: ${{ matrix.target }}
override: true
targets: ${{ matrix.target }}
- name: Cache cargo
uses: actions/cache@v3
- uses: Swatinem/rust-cache@v2
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: stable-${{ matrix.target }}-cargo-cache
workspaces: jsonschema
cache-all-crates: "true"
- run: cargo build --target ${{ matrix.target }} --no-default-features --features=cli
working-directory: ./jsonschema
coverage:
name: Run test coverage
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: true
- name: Toolchain setup
uses: actions-rs/toolchain@v1
with:
toolchain: nightly
override: true
- name: Install grcov
run: cargo install cargo-tarpaulin
- name: Cache cargo
uses: actions/cache@v3
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: coverage-cargo-cache
workspaces: jsonschema
cache-all-crates: "true"
- name: Install cargo-llvm-cov
uses: taiki-e/install-action@cargo-llvm-cov
- name: Run tests
run: cargo +nightly tarpaulin --verbose --all-features --out Xml
run: cargo llvm-cov --no-report --all-features
working-directory: ./jsonschema
- name: Generate coverage reports
run: cargo llvm-cov report --lcov --output-path lcov.info
working-directory: ./jsonschema
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
if: ${{ env.GITHUB_REPOSITORY }} == 'Stranger6667/jsonschema-rs'
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
name: coverage
files: lcov.info
fail_ci_if_error: true
test-python:
strategy:
fail-fast: false
matrix:
os: [ubuntu-20.04, macos-12, windows-2022]
os: [ubuntu-22.04, macos-12, windows-2022]
python-version: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12']
name: Python ${{ matrix.python-version }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: true
- uses: actions/setup-python@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
architecture: x64
@ -153,19 +138,14 @@ jobs:
- run: python -m pip install tox
working-directory: ./bindings/python
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true
- uses: dtolnay/rust-toolchain@stable
- name: Cache cargo
uses: actions/cache@v3
- uses: Swatinem/rust-cache@v2
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: python-${{ matrix.python-version }}-${{ matrix.os }}-cargo-cache
workspaces: |
jsonschema
bindings/python
key: ${{ matrix.python-version }}-${{ matrix.os }}
- name: Run ${{ matrix.python }} tox job
run: tox -e py
@ -173,31 +153,47 @@ jobs:
fmt:
name: Rustfmt
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
with:
profile: minimal
toolchain: stable
override: true
components: rustfmt
- run: cargo fmt --all -- --check
working-directory: ./jsonschema
- run: cargo fmt --all -- --check
working-directory: ./bindings/python
- run: cargo fmt --all -- --check
working-directory: ./bench_helpers
- run: cargo fmt --all -- --check
working-directory: ./perf-helpers
- run: cargo fmt --all -- --check
working-directory: ./jsonschema-test-suite
clippy:
name: Clippy
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: true
- uses: actions-rs/toolchain@v1
- uses: dtolnay/rust-toolchain@stable
with:
profile: minimal
toolchain: stable
override: true
components: clippy
- uses: Swatinem/rust-cache@v2
with:
workspaces: |
jsonschema
bindings/python
- run: cargo clippy --all-targets --all-features -- -D warnings
working-directory: ./jsonschema
@ -206,16 +202,20 @@ jobs:
features:
name: Check features
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: true
- uses: actions-rs/toolchain@v1
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
profile: minimal
toolchain: stable
override: true
workspaces: jsonschema
cache-all-crates: "true"
- uses: taiki-e/install-action@cargo-hack
- run: cargo hack check --feature-powerset --lib
working-directory: ./jsonschema

View File

@ -1,9 +1,9 @@
default_language_version:
python: python3.9
python: python3.11
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.1.0
rev: v4.6.0
hooks:
- id: check-yaml
- id: end-of-file-fixer
@ -15,23 +15,21 @@ repos:
- id: check-merge-conflict
- repo: https://github.com/jorisroovers/gitlint
rev: v0.17.0
rev: v0.19.1
hooks:
- id: gitlint
- repo: https://github.com/adrienverge/yamllint
rev: v1.26.3
rev: v1.35.1
hooks:
- id: yamllint
- repo: https://github.com/ambv/black
rev: 22.3.0
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.3.7
hooks:
- id: black
types: [python]
- id: ruff-format
- repo: https://github.com/pre-commit/mirrors-isort
rev: v5.10.1
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.3.7
hooks:
- id: isort
additional_dependencies: ["isort[pyproject]"]
- id: ruff

View File

@ -13,6 +13,7 @@
- Bump `percent-encoding` to `2.3`.
- Bump `regex` to `1.10`.
- Bump `url` to `2.5`.
- Build CLI only if the `cli` feature is enabled.
## [0.17.1] - 2023-07-05

View File

@ -0,0 +1,2 @@
imports_granularity = "Crate"
edition = "2021"

View File

@ -36,8 +36,22 @@ CANADA = load_from_benches("canada.json")
CITM_CATALOG_SCHEMA = load_from_benches("citm_catalog_schema.json")
CITM_CATALOG = load_from_benches("citm_catalog.json")
FAST_SCHEMA = load_from_benches("fast_schema.json")
FAST_INSTANCE_VALID = [9, "hello", [1, "a", True], {"a": "a", "b": "b", "d": "d"}, 42, 3]
FAST_INSTANCE_INVALID = [10, "world", [1, "a", True], {"a": "a", "b": "b", "c": "xy"}, "str", 5]
FAST_INSTANCE_VALID = [
9,
"hello",
[1, "a", True],
{"a": "a", "b": "b", "d": "d"},
42,
3,
]
FAST_INSTANCE_INVALID = [
10,
"world",
[1, "a", True],
{"a": "a", "b": "b", "c": "xy"},
"str",
5,
]
@pytest.fixture(params=[True, False], ids=("compiled", "raw"))
@ -46,7 +60,12 @@ def is_compiled(request):
if jsonschema_rs is not None:
variants = ["jsonschema-rs-is-valid", "jsonschema-rs-validate", "jsonschema", "fastjsonschema"]
variants = [
"jsonschema-rs-is-valid",
"jsonschema-rs-validate",
"jsonschema",
"fastjsonschema",
]
else:
variants = ["jsonschema", "fastjsonschema"]
@ -66,12 +85,20 @@ def args(request, variant, is_compiled):
if is_compiled:
return jsonschema_rs.JSONSchema(schema, with_meta_schemas=True).is_valid, instance
else:
return partial(jsonschema_rs.is_valid, with_meta_schemas=True), schema, instance
return (
partial(jsonschema_rs.is_valid, with_meta_schemas=True),
schema,
instance,
)
if variant == "jsonschema-rs-validate":
if is_compiled:
return jsonschema_rs.JSONSchema(schema, with_meta_schemas=True).validate, instance
else:
return partial(jsonschema_rs.validate, with_meta_schemas=True), schema, instance
return (
partial(jsonschema_rs.validate, with_meta_schemas=True),
schema,
instance,
)
if variant == "jsonschema":
if is_compiled:
return jsonschema.validators.validator_for(schema)(schema).is_valid, instance
@ -85,7 +112,14 @@ def args(request, variant, is_compiled):
@pytest.mark.parametrize(
"name", ("openapi.json", "swagger.json", "geojson.json", "citm_catalog_schema.json", "fast_schema.json")
"name",
(
"openapi.json",
"swagger.json",
"geojson.json",
"citm_catalog_schema.json",
"fast_schema.json",
),
)
@pytest.mark.parametrize(
"func",

View File

@ -36,18 +36,16 @@ Changelog = "https://github.com/Stranger6667/jsonschema-rs/blob/master/bindings/
Source = "https://github.com/Stranger6667/jsonschema-rs"
Funding = 'https://github.com/sponsors/Stranger6667'
[tool.black]
[tool.ruff]
line-length = 120
target_version = ["py37"]
target-version = "py37"
[tool.isort]
# config compatible with Black
line_length = 120
multi_line_output = 3
default_section = "THIRDPARTY"
include_trailing_comma = true
known_first_party = "jsonschema_rs"
known_third_party = []
[tool.ruff.lint.isort]
known-first-party = ["jsonschema_rs"]
known-third-party = ["hypothesis", "pytest"]
[tool.ruff.format]
skip-magic-trailing-comma = false
[tool.maturin]
python-source = "python"

View File

@ -1 +1 @@
from .jsonschema_rs import *
from .jsonschema_rs import * # noqa: F403

View File

@ -1,51 +1,48 @@
from typing import Any, TypeVar
from collections.abc import Iterator
_SchemaT = TypeVar('_SchemaT', bool, dict[str, Any])
_SchemaT = TypeVar("_SchemaT", bool, dict[str, Any])
def is_valid(
schema: _SchemaT,
instance: Any,
draft: int | None = None,
with_meta_schemas: bool | None = None
schema: _SchemaT,
instance: Any,
draft: int | None = None,
with_meta_schemas: bool | None = None,
) -> bool:
pass
def validate(
schema: _SchemaT,
instance: Any,
draft: int | None = None,
with_meta_schemas: bool | None = None
schema: _SchemaT,
instance: Any,
draft: int | None = None,
with_meta_schemas: bool | None = None,
) -> None:
pass
def iter_errors(
schema: _SchemaT,
instance: Any,
draft: int | None = None,
with_meta_schemas: bool | None = None
schema: _SchemaT,
instance: Any,
draft: int | None = None,
with_meta_schemas: bool | None = None,
) -> Iterator[ValidationError]:
pass
class JSONSchema:
def __init__(
self,
schema: _SchemaT,
draft: int | None = None,
with_meta_schemas: bool | None = None
self,
schema: _SchemaT,
draft: int | None = None,
with_meta_schemas: bool | None = None,
) -> None:
pass
@classmethod
def from_str(
cls,
schema: str,
draft: int | None = None,
with_meta_schemas: bool | None = None
) -> 'JSONSchema':
cls,
schema: str,
draft: int | None = None,
with_meta_schemas: bool | None = None,
) -> "JSONSchema":
pass
def is_valid(self, instance: Any) -> bool:
@ -57,13 +54,11 @@ class JSONSchema:
def iter_errors(self, instance: Any) -> Iterator[ValidationError]:
pass
class ValidationError(ValueError):
message: str
schema_path: list[str | int]
instance_path: list[str | int]
Draft4: int
Draft6: int
Draft7: int

View File

@ -0,0 +1,2 @@
imports_granularity = "Crate"
edition = "2021"

View File

@ -0,0 +1,2 @@
imports_granularity = "Crate"
edition = "2021"

2
jsonschema/rustfmt.toml Normal file
View File

@ -0,0 +1,2 @@
imports_granularity = "Crate"
edition = "2021"

View File

@ -1,33 +1,76 @@
use std::{
error::Error,
fs::File,
io::BufReader,
path::{Path, PathBuf},
process,
};
use clap::Parser;
use jsonschema::JSONSchema;
type BoxErrorResult<T> = Result<T, Box<dyn Error>>;
#[derive(Parser)]
#[command(name = "jsonschema")]
struct Cli {
/// A path to a JSON instance (i.e. filename.json) to validate (may be specified multiple times).
#[arg(short = 'i', long = "instance")]
instances: Option<Vec<PathBuf>>,
/// The JSON Schema to validate with (i.e. schema.json).
#[arg(value_parser, required_unless_present("version"))]
schema: Option<PathBuf>,
/// Show program's version number and exit.
#[arg(short = 'v', long = "version")]
version: bool,
#[cfg(not(feature = "cli"))]
fn main() {
eprintln!("`jsonschema` CLI is only available with the `cli` feature");
std::process::exit(1);
}
pub fn main() -> BoxErrorResult<()> {
#[cfg(feature = "cli")]
fn main() -> Result<(), Box<dyn std::error::Error>> {
use std::{
fs::File,
io::BufReader,
path::{Path, PathBuf},
process,
};
use clap::Parser;
use jsonschema::JSONSchema;
#[derive(Parser)]
#[command(name = "jsonschema")]
struct Cli {
/// A path to a JSON instance (i.e. filename.json) to validate (may be specified multiple times).
#[arg(short = 'i', long = "instance")]
instances: Option<Vec<PathBuf>>,
/// The JSON Schema to validate with (i.e. schema.json).
#[arg(value_parser, required_unless_present("version"))]
schema: Option<PathBuf>,
/// Show program's version number and exit.
#[arg(short = 'v', long = "version")]
version: bool,
}
fn read_json(path: &Path) -> serde_json::Result<serde_json::Value> {
let file = File::open(path).expect("Failed to open file");
let reader = BufReader::new(file);
serde_json::from_reader(reader)
}
fn validate_instances(
instances: &[PathBuf],
schema_path: PathBuf,
) -> Result<bool, Box<dyn std::error::Error>> {
let mut success = true;
let schema_json = read_json(&schema_path)?;
match JSONSchema::compile(&schema_json) {
Ok(schema) => {
for instance in instances {
let instance_json = read_json(instance)?;
let validation = schema.validate(&instance_json);
let filename = instance.to_string_lossy();
match validation {
Ok(_) => println!("{} - VALID", filename),
Err(errors) => {
success = false;
println!("{} - INVALID. Errors:", filename);
for (i, e) in errors.enumerate() {
println!("{}. {}", i + 1, e);
}
}
}
}
}
Err(error) => {
println!("Schema is invalid. Error: {}", error);
success = false;
}
}
Ok(success)
}
let config = Cli::parse();
if config.version {
@ -48,40 +91,3 @@ pub fn main() -> BoxErrorResult<()> {
Ok(())
}
fn read_json(path: &Path) -> serde_json::Result<serde_json::Value> {
let file = File::open(path).expect("Failed to open file");
let reader = BufReader::new(file);
serde_json::from_reader(reader)
}
fn validate_instances(instances: &[PathBuf], schema_path: PathBuf) -> BoxErrorResult<bool> {
let mut success = true;
let schema_json = read_json(&schema_path)?;
match JSONSchema::compile(&schema_json) {
Ok(schema) => {
for instance in instances {
let instance_json = read_json(instance)?;
let validation = schema.validate(&instance_json);
let filename = instance.to_string_lossy();
match validation {
Ok(_) => println!("{} - VALID", filename),
Err(errors) => {
success = false;
println!("{} - INVALID. Errors:", filename);
for (i, e) in errors.enumerate() {
println!("{}. {}", i + 1, e);
}
}
}
}
}
Err(error) => {
println!("Schema is invalid. Error: {}", error);
success = false;
}
}
Ok(success)
}

View File

@ -0,0 +1,2 @@
imports_granularity = "Crate"
edition = "2021"