Skip to content

Commit 2ff853f

Browse files
committed
Benchmark
1 parent 49109f7 commit 2ff853f

File tree

8 files changed

+508708
-5
lines changed

8 files changed

+508708
-5
lines changed

openapi_spec_validator/schemas/__init__.py

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
"""OpenAIP spec validator schemas module."""
1+
# openapi_spec_validator/schemas/__init__.py (POC version)
2+
"""OpenAIP spec validator schemas module - POC with Rust backend support."""
3+
import os
24
from functools import partial
35

46
from jsonschema.validators import Draft4Validator
@@ -7,7 +9,38 @@
79

810
from openapi_spec_validator.schemas.utils import get_schema_content
911

10-
__all__ = ["schema_v2", "schema_v3", "schema_v30", "schema_v31"]
12+
# Import Rust adapters
13+
try:
14+
from openapi_spec_validator.schemas.rust_adapters import (
15+
has_rust_validators,
16+
create_rust_validator,
17+
get_validator_backend,
18+
)
19+
_USE_RUST = has_rust_validators()
20+
except ImportError:
21+
_USE_RUST = False
22+
has_rust_validators = lambda: False # type: ignore
23+
get_validator_backend = lambda: "python (jsonschema)" # type: ignore
24+
25+
# Allow override via environment variable for A/B testing
26+
_FORCE_PYTHON = os.getenv("OPENAPI_FORCE_PYTHON_VALIDATOR", "").lower() in ("1", "true", "yes")
27+
_FORCE_RUST = os.getenv("OPENAPI_FORCE_RUST_VALIDATOR", "").lower() in ("1", "true", "yes")
28+
29+
if _FORCE_PYTHON:
30+
_USE_RUST = False
31+
elif _FORCE_RUST and not _USE_RUST:
32+
raise ImportError(
33+
"OPENAPI_FORCE_RUST_VALIDATOR is set but jsonschema-rs is not available. "
34+
"Install it with: pip install jsonschema-rs"
35+
)
36+
37+
__all__ = [
38+
"schema_v2",
39+
"schema_v3",
40+
"schema_v30",
41+
"schema_v31",
42+
"get_validator_backend",
43+
]
1144

1245
get_schema_content_v2 = partial(get_schema_content, "2.0")
1346
get_schema_content_v30 = partial(get_schema_content, "3.0")
@@ -20,9 +53,28 @@
2053
# alias to the latest v3 version
2154
schema_v3 = schema_v31
2255

23-
get_openapi_v2_schema_validator = partial(Draft4Validator, schema_v2)
24-
get_openapi_v30_schema_validator = partial(Draft4Validator, schema_v30)
25-
get_openapi_v31_schema_validator = partial(Draft202012Validator, schema_v31)
56+
57+
# Validator factory functions with Rust/Python selection
58+
def get_openapi_v2_schema_validator():
59+
"""Create OpenAPI 2.0 schema validator (Draft4)."""
60+
if _USE_RUST:
61+
return create_rust_validator(dict(schema_v2), draft="draft4")
62+
return Draft4Validator(schema_v2)
63+
64+
65+
def get_openapi_v30_schema_validator():
66+
"""Create OpenAPI 3.0 schema validator (Draft4)."""
67+
if _USE_RUST:
68+
return create_rust_validator(dict(schema_v30), draft="draft4")
69+
return Draft4Validator(schema_v30)
70+
71+
72+
def get_openapi_v31_schema_validator():
73+
"""Create OpenAPI 3.1 schema validator (Draft 2020-12)."""
74+
if _USE_RUST:
75+
return create_rust_validator(dict(schema_v31), draft="draft202012")
76+
return Draft202012Validator(schema_v31)
77+
2678

2779
openapi_v2_schema_validator = Proxy(get_openapi_v2_schema_validator)
2880
openapi_v30_schema_validator = Proxy(get_openapi_v30_schema_validator)
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
# openapi_spec_validator/schemas/rust_adapters.py
2+
"""
3+
Proof-of-Concept: jsonschema-rs adapter for openapi-spec-validator.
4+
5+
This module provides a compatibility layer between jsonschema-rs (Rust)
6+
and the existing jsonschema (Python) validator interface.
7+
"""
8+
from typing import Any, Iterator
9+
10+
from jsonschema.exceptions import ValidationError as PyValidationError
11+
12+
# Try to import jsonschema-rs
13+
try:
14+
import jsonschema_rs
15+
HAS_JSONSCHEMA_RS = True
16+
except ImportError:
17+
HAS_JSONSCHEMA_RS = False
18+
jsonschema_rs = None # type: ignore
19+
20+
21+
class RustValidatorError(PyValidationError):
22+
"""ValidationError compatible with jsonschema, but originating from Rust validator."""
23+
pass
24+
25+
26+
class RustValidatorWrapper:
27+
"""
28+
Wrapper that makes jsonschema-rs validator compatible with jsonschema interface.
29+
30+
This allows drop-in replacement while maintaining the same API surface.
31+
"""
32+
33+
def __init__(self, schema: dict[str, Any], draft: str = "draft202012"):
34+
"""
35+
Initialize Rust validator wrapper.
36+
37+
Args:
38+
schema: JSON Schema to validate against
39+
draft: JSON Schema draft version ('draft4' or 'draft202012')
40+
"""
41+
if not HAS_JSONSCHEMA_RS:
42+
raise ImportError(
43+
"jsonschema-rs is not installed. Install it with: "
44+
"pip install jsonschema-rs"
45+
)
46+
47+
self.schema = schema
48+
self.draft = draft
49+
50+
# Create appropriate Rust validator based on draft
51+
if draft == "draft4":
52+
self._rs_validator = jsonschema_rs.Draft4Validator(schema)
53+
elif draft == "draft7":
54+
self._rs_validator = jsonschema_rs.Draft7Validator(schema)
55+
elif draft == "draft201909":
56+
self._rs_validator = jsonschema_rs.Draft201909Validator(schema)
57+
elif draft == "draft202012":
58+
self._rs_validator = jsonschema_rs.Draft202012Validator(schema)
59+
else:
60+
raise ValueError(f"Unsupported draft: {draft}")
61+
62+
def iter_errors(self, instance: Any) -> Iterator[PyValidationError]:
63+
"""
64+
Validate instance and yield errors in jsonschema format.
65+
66+
This method converts jsonschema-rs errors to jsonschema ValidationError
67+
format for compatibility with existing code.
68+
"""
69+
# Try to validate - jsonschema-rs returns ValidationError on failure
70+
result = self._rs_validator.validate(instance)
71+
72+
if result is not None:
73+
# result contains validation errors
74+
# jsonschema-rs returns an iterator of errors
75+
for error in self._rs_validator.iter_errors(instance):
76+
yield self._convert_rust_error(error, instance)
77+
78+
def validate(self, instance: Any) -> None:
79+
"""
80+
Validate instance and raise ValidationError if invalid.
81+
82+
Compatible with jsonschema Validator.validate() method.
83+
"""
84+
try:
85+
self._rs_validator.validate(instance)
86+
except jsonschema_rs.ValidationError as e:
87+
# Convert and raise as Python ValidationError
88+
py_error = self._convert_rust_error_exception(e, instance)
89+
raise py_error from e
90+
91+
def is_valid(self, instance: Any) -> bool:
92+
"""Check if instance is valid against schema."""
93+
return self._rs_validator.is_valid(instance)
94+
95+
def _convert_rust_error(
96+
self,
97+
rust_error: Any,
98+
instance: Any
99+
) -> PyValidationError:
100+
"""
101+
Convert jsonschema-rs error format to jsonschema ValidationError.
102+
103+
jsonschema-rs error structure:
104+
- message: str
105+
- instance_path: list
106+
- schema_path: list (if available)
107+
"""
108+
message = str(rust_error)
109+
110+
# Extract path information if available
111+
# Note: jsonschema-rs error format may differ - adjust as needed
112+
instance_path = getattr(rust_error, 'instance_path', [])
113+
schema_path = getattr(rust_error, 'schema_path', [])
114+
115+
return RustValidatorError(
116+
message=message,
117+
path=list(instance_path) if instance_path else [],
118+
schema_path=list(schema_path) if schema_path else [],
119+
instance=instance,
120+
schema=self.schema,
121+
)
122+
123+
def _convert_rust_error_exception(
124+
self,
125+
rust_error: 'jsonschema_rs.ValidationError',
126+
instance: Any
127+
) -> PyValidationError:
128+
"""Convert jsonschema-rs ValidationError exception to Python format."""
129+
message = str(rust_error)
130+
131+
return RustValidatorError(
132+
message=message,
133+
instance=instance,
134+
schema=self.schema,
135+
)
136+
137+
138+
def create_rust_validator(schema: dict[str, Any], draft: str = "draft202012") -> RustValidatorWrapper:
139+
"""
140+
Factory function to create Rust-backed validator.
141+
142+
Args:
143+
schema: JSON Schema to validate against
144+
draft: JSON Schema draft version
145+
146+
Returns:
147+
RustValidatorWrapper instance
148+
"""
149+
return RustValidatorWrapper(schema, draft=draft)
150+
151+
152+
# Convenience function to check if Rust validators are available
153+
def has_rust_validators() -> bool:
154+
"""Check if jsonschema-rs is available."""
155+
return HAS_JSONSCHEMA_RS
156+
157+
158+
def get_validator_backend() -> str:
159+
"""Get current validator backend (rust or python)."""
160+
if HAS_JSONSCHEMA_RS:
161+
return "rust (jsonschema-rs)"
162+
return "python (jsonschema)"

0 commit comments

Comments
 (0)