Skip to content

Commit 4806e5c

Browse files
committed
feat(python): Extend ValidationErrorKind with error-specific context
Signed-off-by: Dmitry Dygalo <[email protected]>
1 parent cfd6702 commit 4806e5c

File tree

7 files changed

+461
-130
lines changed

7 files changed

+461
-130
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## [Unreleased]
44

5+
### Added
6+
7+
- Implement `ExactSizeIterator` for `PrimitiveTypesBitMapIterator`.
8+
59
## [0.27.0] - 2024-12-23
610

711
### Added

crates/jsonschema-py/CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
## [Unreleased]
44

5+
### Added
6+
7+
- Extend `ValidationErrorKind` with error-specific context data.
8+
- Missing type annotations for `retriever` & `mask` arguments.
9+
510
## [0.27.0] - 2024-12-23
611

712
### Added

crates/jsonschema-py/README.md

+26-1
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,32 @@ validator.is_valid({
197197
}) # False
198198
```
199199

200-
## Error Message Masking
200+
## Error Handling
201+
202+
`jsonschema-rs` provides detailed validation errors through the `ValidationError` class, which includes both basic error information and specific details about what caused the validation to fail:
203+
204+
```python
205+
import jsonschema_rs
206+
207+
schema = {"type": "string", "maxLength": 5}
208+
209+
try:
210+
jsonschema_rs.validate(schema, "too long")
211+
except jsonschema_rs.ValidationError as error:
212+
# Basic error information
213+
print(error.message) # '"too long" is longer than 5 characters'
214+
print(error.instance_path) # Location in the instance that failed
215+
print(error.schema_path) # Location in the schema that failed
216+
217+
# Detailed error information via `kind`
218+
if isinstance(error.kind, jsonschema_rs.ValidationErrorKind.MaxLength):
219+
assert error.kind.limit == 5
220+
print(f"Exceeded maximum length of {error.kind.limit}")
221+
```
222+
223+
For a complete list of all error kinds and their attributes, see the [type definitions file](https://github.com/Stranger6667/jsonschema/blob/master/crates/jsonschema-py/python/jsonschema_rs/__init__.pyi)
224+
225+
### Error Message Masking
201226

202227
When working with sensitive data, you might want to hide actual values from error messages.
203228
You can mask instance values in error messages by providing a placeholder:

crates/jsonschema-py/python/jsonschema_rs/__init__.pyi

+126-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
from collections.abc import Iterator
2-
from typing import Any, Callable, TypeVar
2+
from typing import Any, Callable, Protocol, TypeAlias, TypeVar
33

44
_SchemaT = TypeVar("_SchemaT", bool, dict[str, Any])
55
_FormatFunc = TypeVar("_FormatFunc", bound=Callable[[str], bool])
6+
JSONType: TypeAlias = dict[str, Any] | list | str | int | float | bool | None
7+
JSONPrimitive: TypeAlias = str | int | float | bool | None
8+
9+
class RetrieverProtocol(Protocol):
10+
def __call__(self, uri: str) -> JSONType: ...
611

712
def is_valid(
813
schema: _SchemaT,
@@ -12,6 +17,8 @@ def is_valid(
1217
formats: dict[str, _FormatFunc] | None = None,
1318
validate_formats: bool | None = None,
1419
ignore_unknown_formats: bool = True,
20+
retriever: RetrieverProtocol | None = None,
21+
mask: str | None = None,
1522
) -> bool: ...
1623
def validate(
1724
schema: _SchemaT,
@@ -21,6 +28,8 @@ def validate(
2128
formats: dict[str, _FormatFunc] | None = None,
2229
validate_formats: bool | None = None,
2330
ignore_unknown_formats: bool = True,
31+
retriever: RetrieverProtocol | None = None,
32+
mask: str | None = None,
2433
) -> None: ...
2534
def iter_errors(
2635
schema: _SchemaT,
@@ -30,16 +39,118 @@ def iter_errors(
3039
formats: dict[str, _FormatFunc] | None = None,
3140
validate_formats: bool | None = None,
3241
ignore_unknown_formats: bool = True,
42+
retriever: RetrieverProtocol | None = None,
43+
mask: str | None = None,
3344
) -> Iterator[ValidationError]: ...
3445

35-
class ValidationErrorKind: ...
46+
class ReferencingError:
47+
message: str
48+
49+
class ValidationErrorKind:
50+
class AdditionalItems:
51+
limit: int
52+
53+
class AdditionalProperties:
54+
unexpected: list[str]
55+
56+
class AnyOf: ...
57+
58+
class BacktrackLimitExceeded:
59+
error: str
60+
61+
class Constant:
62+
expected_value: JSONType
63+
64+
class Contains: ...
65+
66+
class ContentEncoding:
67+
content_encoding: str
68+
69+
class ContentMediaType:
70+
content_media_type: str
71+
72+
class Custom:
73+
message: str
74+
75+
class Enum:
76+
options: list[JSONType]
77+
78+
class ExclusiveMaximum:
79+
limit: JSONPrimitive
80+
81+
class ExclusiveMinimum:
82+
limit: JSONPrimitive
83+
84+
class FalseSchema: ...
85+
86+
class Format:
87+
format: str
88+
89+
class FromUtf8:
90+
error: str
91+
92+
class MaxItems:
93+
limit: int
94+
95+
class Maximum:
96+
limit: JSONPrimitive
97+
98+
class MaxLength:
99+
limit: int
100+
101+
class MaxProperties:
102+
limit: int
103+
104+
class MinItems:
105+
limit: int
106+
107+
class Minimum:
108+
limit: JSONPrimitive
109+
110+
class MinLength:
111+
limit: int
112+
113+
class MinProperties:
114+
limit: int
115+
116+
class MultipleOf:
117+
multiple_of: float
118+
119+
class Not:
120+
schema: JSONType
121+
122+
class OneOfMultipleValid: ...
123+
class OneOfNotValid: ...
124+
125+
class Pattern:
126+
pattern: str
127+
128+
class PropertyNames:
129+
error: "ValidationError"
130+
131+
class Required:
132+
property: str
133+
134+
class Type:
135+
types: list[str]
136+
137+
class UnevaluatedItems:
138+
unexpected: list[int]
139+
140+
class UnevaluatedProperties:
141+
unexpected: list[str]
142+
143+
class UniqueItems: ...
144+
145+
class Referencing:
146+
error: ReferencingError
36147

37148
class ValidationError(ValueError):
38149
message: str
39150
schema_path: list[str | int]
40151
instance_path: list[str | int]
41152
kind: ValidationErrorKind
42-
instance: list | dict | str | int | float | bool | None
153+
instance: JSONType
43154

44155
Draft4: int
45156
Draft6: int
@@ -54,6 +165,8 @@ class Draft4Validator:
54165
formats: dict[str, _FormatFunc] | None = None,
55166
validate_formats: bool | None = None,
56167
ignore_unknown_formats: bool = True,
168+
retriever: RetrieverProtocol | None = None,
169+
mask: str | None = None,
57170
) -> None: ...
58171
def is_valid(self, instance: Any) -> bool: ...
59172
def validate(self, instance: Any) -> None: ...
@@ -66,6 +179,8 @@ class Draft6Validator:
66179
formats: dict[str, _FormatFunc] | None = None,
67180
validate_formats: bool | None = None,
68181
ignore_unknown_formats: bool = True,
182+
retriever: RetrieverProtocol | None = None,
183+
mask: str | None = None,
69184
) -> None: ...
70185
def is_valid(self, instance: Any) -> bool: ...
71186
def validate(self, instance: Any) -> None: ...
@@ -78,6 +193,8 @@ class Draft7Validator:
78193
formats: dict[str, _FormatFunc] | None = None,
79194
validate_formats: bool | None = None,
80195
ignore_unknown_formats: bool = True,
196+
retriever: RetrieverProtocol | None = None,
197+
mask: str | None = None,
81198
) -> None: ...
82199
def is_valid(self, instance: Any) -> bool: ...
83200
def validate(self, instance: Any) -> None: ...
@@ -90,6 +207,8 @@ class Draft201909Validator:
90207
formats: dict[str, _FormatFunc] | None = None,
91208
validate_formats: bool | None = None,
92209
ignore_unknown_formats: bool = True,
210+
retriever: RetrieverProtocol | None = None,
211+
mask: str | None = None,
93212
) -> None: ...
94213
def is_valid(self, instance: Any) -> bool: ...
95214
def validate(self, instance: Any) -> None: ...
@@ -102,6 +221,8 @@ class Draft202012Validator:
102221
formats: dict[str, _FormatFunc] | None = None,
103222
validate_formats: bool | None = None,
104223
ignore_unknown_formats: bool = True,
224+
retriever: RetrieverProtocol | None = None,
225+
mask: str | None = None,
105226
) -> None: ...
106227
def is_valid(self, instance: Any) -> bool: ...
107228
def validate(self, instance: Any) -> None: ...
@@ -112,4 +233,6 @@ def validator_for(
112233
formats: dict[str, _FormatFunc] | None = None,
113234
validate_formats: bool | None = None,
114235
ignore_unknown_formats: bool = True,
236+
retriever: RetrieverProtocol | None = None,
237+
mask: str | None = None,
115238
) -> Draft4Validator | Draft6Validator | Draft7Validator | Draft201909Validator | Draft202012Validator: ...

0 commit comments

Comments
 (0)