Skip to content

Commit 9fb2243

Browse files
committed
chore(python): Various typing fixes
Signed-off-by: Dmitry Dygalo <dmitry@dygalo.dev>
1 parent 5fa100c commit 9fb2243

File tree

3 files changed

+113
-11
lines changed

3 files changed

+113
-11
lines changed

crates/jsonschema-py/CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,17 @@
55
### Added
66

77
- `jsonschema_rs.evaluate()`, `Validator.evaluate()`, and the `Evaluation` type for retrieving JSON Schema Output v1 (flag/list/hierarchical) formats along with annotations and errors.
8+
- `Validator` type alias for union of all validator types (`Draft4Validator | Draft6Validator | Draft7Validator | Draft201909Validator | Draft202012Validator`).
9+
- TypedDict types in type stubs for evaluation outputs: `FlagOutput`, `ListOutput`, `OutputUnit`, `EvaluationAnnotation`, `EvaluationErrorEntry` for better type safety and IDE autocomplete.
10+
- `__eq__` and `__hash__` methods to `ValidationError` and `ReferencingError` for better testability.
11+
- `__repr__` method stubs to all validator classes, `Evaluation`, `Registry`, and regex options classes for better debugging experience.
12+
- Docstrings to key functions in type stubs (`is_valid`, `validate`, `iter_errors`, `evaluate`, `validator_for`).
13+
14+
### Fixed
15+
16+
- Missing `verbose_message` attribute in `ValidationError` type stub.
17+
- Missing `context` field in `ValidationErrorKind.OneOfMultipleValid` type stub.
18+
- Incorrect type for `ValidationErrorKind.MultipleOf.multiple_of` - now correctly typed as `int | float | Decimal` to support arbitrary precision numbers.
819

920
## [0.35.0] - 2025-11-16
1021

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

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Any
1+
from typing import Any, TypeAlias
22

33
from .jsonschema_rs import (
44
Draft4,
@@ -24,6 +24,10 @@
2424
validator_for,
2525
)
2626

27+
Validator: TypeAlias = (
28+
Draft4Validator | Draft6Validator | Draft7Validator | Draft201909Validator | Draft202012Validator
29+
)
30+
2731

2832
class ValidationError(ValueError):
2933
"""An instance is invalid under a provided schema."""
@@ -58,6 +62,18 @@ def __str__(self) -> str:
5862
def __repr__(self) -> str:
5963
return f"<ValidationError: '{self.message}'>"
6064

65+
def __eq__(self, other: object) -> bool:
66+
if not isinstance(other, ValidationError):
67+
return NotImplemented
68+
return (
69+
self.message == other.message
70+
and self.schema_path == other.schema_path
71+
and self.instance_path == other.instance_path
72+
)
73+
74+
def __hash__(self) -> int:
75+
return hash((self.message, tuple(self.schema_path), tuple(self.instance_path)))
76+
6177

6278
class ReferencingError(Exception):
6379
"""Errors that can occur during reference resolution and resource handling."""
@@ -74,6 +90,14 @@ def __str__(self) -> str:
7490
def __repr__(self) -> str:
7591
return f"<ReferencingError: '{self.message}'>"
7692

93+
def __eq__(self, other: object) -> bool:
94+
if not isinstance(other, ReferencingError):
95+
return NotImplemented
96+
return self.message == other.message
97+
98+
def __hash__(self) -> int:
99+
return hash(self.message)
100+
77101

78102
__all__ = [
79103
"ReferencingError",
@@ -95,6 +119,7 @@ def __repr__(self) -> str:
95119
"Draft7Validator",
96120
"Draft201909Validator",
97121
"Draft202012Validator",
122+
"Validator",
98123
"Registry",
99124
"FancyRegexOptions",
100125
"RegexOptions",

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

Lines changed: 76 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from collections.abc import Iterator
2+
from decimal import Decimal
23
from typing import Any, Callable, List, Protocol, TypeAlias, TypeVar, TypedDict, Union
34

45
_SchemaT = TypeVar("_SchemaT", bool, dict[str, Any])
@@ -18,21 +19,47 @@ class EvaluationErrorEntry(TypedDict):
1819
instanceLocation: str
1920
error: str
2021

22+
class FlagOutput(TypedDict):
23+
"""JSON Schema Output v1 - Flag format."""
24+
25+
valid: bool
26+
27+
class OutputUnit(TypedDict, total=False):
28+
"""A single output unit in list/hierarchical formats."""
29+
30+
valid: bool
31+
evaluationPath: str
32+
schemaLocation: str
33+
instanceLocation: str
34+
errors: dict[str, str]
35+
annotations: JSONType
36+
droppedAnnotations: JSONType
37+
details: List["OutputUnit"]
38+
39+
class ListOutput(TypedDict):
40+
"""JSON Schema Output v1 - List format."""
41+
42+
valid: bool
43+
details: List[OutputUnit]
44+
2145
class Evaluation:
2246
valid: bool
23-
def flag(self) -> JSONType: ...
24-
def list(self) -> JSONType: ...
25-
def hierarchical(self) -> JSONType: ...
47+
def flag(self) -> FlagOutput: ...
48+
def list(self) -> ListOutput: ...
49+
def hierarchical(self) -> OutputUnit: ...
2650
def annotations(self) -> List[EvaluationAnnotation]: ...
2751
def errors(self) -> List[EvaluationErrorEntry]: ...
52+
def __repr__(self) -> str: ...
2853

2954
class FancyRegexOptions:
3055
def __init__(
3156
self, backtrack_limit: int | None = None, size_limit: int | None = None, dfa_size_limit: int | None = None
3257
) -> None: ...
58+
def __repr__(self) -> str: ...
3359

3460
class RegexOptions:
3561
def __init__(self, size_limit: int | None = None, dfa_size_limit: int | None = None) -> None: ...
62+
def __repr__(self) -> str: ...
3663

3764
PatternOptionsType = Union[FancyRegexOptions, RegexOptions]
3865

@@ -52,7 +79,13 @@ def is_valid(
5279
mask: str | None = None,
5380
base_uri: str | None = None,
5481
pattern_options: PatternOptionsType | None = None,
55-
) -> bool: ...
82+
) -> bool:
83+
"""Check if a JSON instance is valid against a schema.
84+
85+
Returns True if valid, False otherwise. Stops at the first error.
86+
"""
87+
...
88+
5689
def validate(
5790
schema: _SchemaT,
5891
instance: Any,
@@ -66,7 +99,13 @@ def validate(
6699
mask: str | None = None,
67100
base_uri: str | None = None,
68101
pattern_options: PatternOptionsType | None = None,
69-
) -> None: ...
102+
) -> None:
103+
"""Validate a JSON instance against a schema.
104+
105+
Raises ValidationError if invalid. Stops at the first error.
106+
"""
107+
...
108+
70109
def iter_errors(
71110
schema: _SchemaT,
72111
instance: Any,
@@ -80,7 +119,13 @@ def iter_errors(
80119
mask: str | None = None,
81120
base_uri: str | None = None,
82121
pattern_options: PatternOptionsType | None = None,
83-
) -> Iterator[ValidationError]: ...
122+
) -> Iterator[ValidationError]:
123+
"""Iterate over all validation errors.
124+
125+
Returns an iterator of ValidationError objects for all errors found.
126+
"""
127+
...
128+
84129
def evaluate(
85130
schema: _SchemaT,
86131
instance: Any,
@@ -92,7 +137,12 @@ def evaluate(
92137
registry: Registry | None = None,
93138
base_uri: str | None = None,
94139
pattern_options: PatternOptionsType | None = None,
95-
) -> Evaluation: ...
140+
) -> Evaluation:
141+
"""Evaluate an instance and return structured output.
142+
143+
Returns an Evaluation object with flag(), list(), and hierarchical() output formats.
144+
"""
145+
...
96146

97147
class ReferencingError:
98148
message: str
@@ -166,12 +216,13 @@ class ValidationErrorKind:
166216
limit: int
167217

168218
class MultipleOf:
169-
multiple_of: float
219+
multiple_of: int | float | Decimal
170220

171221
class Not:
172222
schema: JSONType
173223

174-
class OneOfMultipleValid: ...
224+
class OneOfMultipleValid:
225+
context: list[list["ValidationError"]]
175226

176227
class OneOfNotValid:
177228
context: list[list["ValidationError"]]
@@ -201,6 +252,7 @@ class ValidationErrorKind:
201252

202253
class ValidationError(ValueError):
203254
message: str
255+
verbose_message: str
204256
schema_path: list[str | int]
205257
instance_path: list[str | int]
206258
kind: ValidationErrorKind
@@ -229,6 +281,7 @@ class Draft4Validator:
229281
def validate(self, instance: Any) -> None: ...
230282
def iter_errors(self, instance: Any) -> Iterator[ValidationError]: ...
231283
def evaluate(self, instance: Any) -> Evaluation: ...
284+
def __repr__(self) -> str: ...
232285

233286
class Draft6Validator:
234287
def __init__(
@@ -247,6 +300,7 @@ class Draft6Validator:
247300
def validate(self, instance: Any) -> None: ...
248301
def iter_errors(self, instance: Any) -> Iterator[ValidationError]: ...
249302
def evaluate(self, instance: Any) -> Evaluation: ...
303+
def __repr__(self) -> str: ...
250304

251305
class Draft7Validator:
252306
def __init__(
@@ -265,6 +319,7 @@ class Draft7Validator:
265319
def validate(self, instance: Any) -> None: ...
266320
def iter_errors(self, instance: Any) -> Iterator[ValidationError]: ...
267321
def evaluate(self, instance: Any) -> Evaluation: ...
322+
def __repr__(self) -> str: ...
268323

269324
class Draft201909Validator:
270325
def __init__(
@@ -283,6 +338,7 @@ class Draft201909Validator:
283338
def validate(self, instance: Any) -> None: ...
284339
def iter_errors(self, instance: Any) -> Iterator[ValidationError]: ...
285340
def evaluate(self, instance: Any) -> Evaluation: ...
341+
def __repr__(self) -> str: ...
286342

287343
class Draft202012Validator:
288344
def __init__(
@@ -301,6 +357,9 @@ class Draft202012Validator:
301357
def validate(self, instance: Any) -> None: ...
302358
def iter_errors(self, instance: Any) -> Iterator[ValidationError]: ...
303359
def evaluate(self, instance: Any) -> Evaluation: ...
360+
def __repr__(self) -> str: ...
361+
362+
Validator: TypeAlias = Draft4Validator | Draft6Validator | Draft7Validator | Draft201909Validator | Draft202012Validator
304363

305364
def validator_for(
306365
schema: _SchemaT,
@@ -312,7 +371,13 @@ def validator_for(
312371
mask: str | None = None,
313372
base_uri: str | None = None,
314373
pattern_options: PatternOptionsType | None = None,
315-
) -> Draft4Validator | Draft6Validator | Draft7Validator | Draft201909Validator | Draft202012Validator: ...
374+
) -> Validator:
375+
"""Create a validator for the given schema.
376+
377+
Automatically detects the JSON Schema draft from the $schema keyword.
378+
Returns a Draft-specific validator instance.
379+
"""
380+
...
316381

317382
class Registry:
318383
def __init__(
@@ -321,6 +386,7 @@ class Registry:
321386
draft: int | None = None,
322387
retriever: RetrieverProtocol | None = None,
323388
) -> None: ...
389+
def __repr__(self) -> str: ...
324390

325391
class _Meta:
326392
def is_valid(self, schema: _SchemaT, registry: Registry | None = None) -> bool: ...

0 commit comments

Comments
 (0)