Skip to content

Commit 2c01b5c

Browse files
committed
Rust backend
1 parent a093f9c commit 2c01b5c

File tree

12 files changed

+493
-28
lines changed

12 files changed

+493
-28
lines changed

.github/workflows/python-tests.yml

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,20 @@ on:
1010

1111
jobs:
1212
tests:
13-
name: "py${{ matrix.python-version }}-${{ matrix.os }}"
13+
name: "py${{ matrix.python-version }}-${{ matrix.os }}-${{ matrix.backend }}"
1414
runs-on: ${{ matrix.os }}
1515
strategy:
1616
matrix:
1717
python-version: ['3.10', '3.11', '3.12', '3.13', '3.14']
1818
os: [windows-latest, ubuntu-latest]
19+
backend: ['jsonschema']
20+
include:
21+
- python-version: '3.12'
22+
os: ubuntu-latest
23+
backend: 'jsonschema-rs'
24+
- python-version: '3.13'
25+
os: windows-latest
26+
backend: 'jsonschema-rs'
1927
fail-fast: false
2028
steps:
2129
- uses: actions/checkout@v4
@@ -49,9 +57,14 @@ jobs:
4957
- name: Install dependencies
5058
run: poetry install --all-extras
5159

60+
- name: Install jsonschema-rs
61+
if: matrix.backend != 'jsonschema'
62+
run: poetry run pip install ${{ matrix.backend }}
63+
5264
- name: Test
5365
env:
5466
PYTEST_ADDOPTS: "--color=yes"
67+
OPENAPI_SPEC_VALIDATOR_SCHEMA_VALIDATOR_BACKEND: ${{ matrix.backend }}
5568
run: poetry run pytest
5669

5770
- name: Static type check

README.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,16 @@ Rules:
131131
* Set ``0`` to disable the resolved cache.
132132
* Invalid values (non-integer or negative) fall back to ``128``.
133133

134+
You can also choose schema validator backend:
135+
136+
.. code-block:: bash
137+
138+
OPENAPI_SPEC_VALIDATOR_SCHEMA_VALIDATOR_BACKEND=jsonschema-rs
139+
140+
Allowed values are ``auto`` (default), ``jsonschema``, and
141+
``jsonschema-rs``.
142+
Invalid values raise a warning and fall back to ``auto``.
143+
134144
Related projects
135145
################
136146

docs/cli.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,7 @@ Performance note:
7373
You can tune resolved-path caching with
7474
``OPENAPI_SPEC_VALIDATOR_RESOLVED_CACHE_MAXSIZE``.
7575
Default is ``128``; set ``0`` to disable.
76+
77+
You can also select schema validator backend with
78+
``OPENAPI_SPEC_VALIDATOR_SCHEMA_VALIDATOR_BACKEND``
79+
(``auto``/``jsonschema``/``jsonschema-rs``).

docs/python.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,13 @@ Rules:
7575
* Default is ``128``.
7676
* Set ``0`` to disable the resolved cache.
7777
* Invalid values (non-integer or negative) fall back to ``128``.
78+
79+
Schema validator backend can be selected with:
80+
81+
.. code-block:: bash
82+
83+
OPENAPI_SPEC_VALIDATOR_SCHEMA_VALIDATOR_BACKEND=jsonschema-rs
84+
85+
Allowed values are ``auto`` (default), ``jsonschema``, and
86+
``jsonschema-rs``.
87+
Invalid values raise a warning and fall back to ``auto``.

openapi_spec_validator/__main__.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from jsonschema.exceptions import best_match
99

1010
from openapi_spec_validator import __version__
11+
from openapi_spec_validator import schemas
1112
from openapi_spec_validator.readers import read_from_filename
1213
from openapi_spec_validator.readers import read_from_stdin
1314
from openapi_spec_validator.shortcuts import get_validator_cls
@@ -38,6 +39,7 @@ def print_validationerror(
3839
exc: ValidationError,
3940
subschema_errors: str = "best-match",
4041
index: int | None = None,
42+
supports_subschema_details: bool = True,
4143
) -> None:
4244
if index is None:
4345
print(f"{filename}: Validation Error: {exc}")
@@ -48,6 +50,13 @@ def print_validationerror(
4850
print(exc.cause)
4951
if not exc.context:
5052
return
53+
if not supports_subschema_details:
54+
print("\n\n# Subschema details\n")
55+
print(
56+
"Subschema error details are not available "
57+
"with jsonschema-rs backend."
58+
)
59+
return
5160
if subschema_errors == "all":
5261
print("\n\n# Due to one of those errors\n")
5362
print("\n\n\n".join("## " + str(e) for e in exc.context))
@@ -139,6 +148,10 @@ def main(args: Sequence[str] | None = None) -> None:
139148
if subschema_errors is None:
140149
subschema_errors = "best-match"
141150

151+
supports_subschema_details = (
152+
schemas.get_validator_backend() != "jsonschema-rs"
153+
)
154+
142155
for filename in args_parsed.file:
143156
# choose source
144157
reader = read_from_filename
@@ -181,6 +194,9 @@ def main(args: Sequence[str] | None = None) -> None:
181194
err,
182195
subschema_errors,
183196
index=idx,
197+
supports_subschema_details=(
198+
supports_subschema_details
199+
),
184200
)
185201
print(f"{filename}: {len(errors)} validation errors found")
186202
sys.exit(1)
@@ -189,7 +205,12 @@ def main(args: Sequence[str] | None = None) -> None:
189205

190206
validate(spec, base_uri=base_uri, cls=validator_cls)
191207
except ValidationError as exc:
192-
print_validationerror(filename, exc, subschema_errors)
208+
print_validationerror(
209+
filename,
210+
exc,
211+
subschema_errors,
212+
supports_subschema_details=supports_subschema_details,
213+
)
193214
sys.exit(1)
194215
except Exception as exc:
195216
print_error(filename, exc)

openapi_spec_validator/schemas/__init__.py

Lines changed: 81 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,51 @@
1-
"""OpenAIP spec validator schemas module."""
1+
"""OpenAPI spec validator schemas module."""
22

33
from functools import partial
4+
from typing import Any
45

56
from jsonschema.validators import Draft4Validator
67
from jsonschema.validators import Draft202012Validator
78
from lazy_object_proxy import Proxy
89

910
from openapi_spec_validator.schemas.utils import get_schema_content
11+
from openapi_spec_validator.settings import get_schema_validator_backend
1012

11-
__all__ = ["schema_v2", "schema_v3", "schema_v30", "schema_v31", "schema_v32"]
13+
from openapi_spec_validator.schemas.jsonschema_rs_adapters import (
14+
create_validator as _create_jsonschema_rs_validator_impl,
15+
)
16+
from openapi_spec_validator.schemas.jsonschema_rs_adapters import (
17+
has_jsonschema_rs_validators,
18+
)
19+
20+
_USE_JSONSCHEMA_RS = has_jsonschema_rs_validators()
21+
22+
23+
_BACKEND_MODE = get_schema_validator_backend()
24+
25+
if _BACKEND_MODE == "jsonschema":
26+
_USE_JSONSCHEMA_RS = False
27+
elif _BACKEND_MODE == "jsonschema-rs" and not _USE_JSONSCHEMA_RS:
28+
raise ImportError(
29+
"OPENAPI_SPEC_VALIDATOR_SCHEMA_VALIDATOR_BACKEND=jsonschema-rs "
30+
"is set but jsonschema-rs is not available. "
31+
"Install it with: pip install jsonschema-rs"
32+
)
33+
34+
35+
def get_validator_backend() -> str:
36+
if _USE_JSONSCHEMA_RS:
37+
return "jsonschema-rs"
38+
return "jsonschema"
39+
40+
41+
__all__ = [
42+
"schema_v2",
43+
"schema_v3",
44+
"schema_v30",
45+
"schema_v31",
46+
"schema_v32",
47+
"get_validator_backend",
48+
]
1249

1350
get_schema_content_v2 = partial(get_schema_content, "2.0")
1451
get_schema_content_v30 = partial(get_schema_content, "3.0")
@@ -23,10 +60,48 @@
2360
# alias to the latest v3 version
2461
schema_v3 = schema_v32
2562

26-
get_openapi_v2_schema_validator = partial(Draft4Validator, schema_v2)
27-
get_openapi_v30_schema_validator = partial(Draft4Validator, schema_v30)
28-
get_openapi_v31_schema_validator = partial(Draft202012Validator, schema_v31)
29-
get_openapi_v32_schema_validator = partial(Draft202012Validator, schema_v32)
63+
64+
def _create_jsonschema_rs_schema_validator(
65+
schema: dict[str, Any],
66+
draft: str,
67+
) -> Any:
68+
return _create_jsonschema_rs_validator_impl(schema, draft)
69+
70+
71+
# Validator factory functions with Rust/Python selection
72+
def get_openapi_v2_schema_validator() -> Any:
73+
"""Create OpenAPI 2.0 schema validator (Draft4)."""
74+
if _USE_JSONSCHEMA_RS:
75+
return _create_jsonschema_rs_schema_validator(dict(schema_v2), draft="draft4")
76+
return Draft4Validator(schema_v2)
77+
78+
79+
def get_openapi_v30_schema_validator() -> Any:
80+
"""Create OpenAPI 3.0 schema validator (Draft4)."""
81+
if _USE_JSONSCHEMA_RS:
82+
return _create_jsonschema_rs_schema_validator(dict(schema_v30), draft="draft4")
83+
return Draft4Validator(schema_v30)
84+
85+
86+
def get_openapi_v31_schema_validator() -> Any:
87+
"""Create OpenAPI 3.1 schema validator (Draft 2020-12)."""
88+
if _USE_JSONSCHEMA_RS:
89+
return _create_jsonschema_rs_schema_validator(
90+
dict(schema_v31),
91+
draft="draft202012",
92+
)
93+
return Draft202012Validator(schema_v31)
94+
95+
96+
def get_openapi_v32_schema_validator() -> Any:
97+
"""Create OpenAPI 3.2 schema validator (Draft 2020-12)."""
98+
if _USE_JSONSCHEMA_RS:
99+
return _create_jsonschema_rs_schema_validator(
100+
dict(schema_v32),
101+
draft="draft202012",
102+
)
103+
return Draft202012Validator(schema_v32)
104+
30105

31106
openapi_v2_schema_validator = Proxy(get_openapi_v2_schema_validator)
32107
openapi_v30_schema_validator = Proxy(get_openapi_v30_schema_validator)

0 commit comments

Comments
 (0)