Repository navigation
fix(validation): isolate cached validators across threads - #1442
Conversation
There was a problem hiding this comment.
Ladon verdict: Approve
Approve — thread-safety fix for #1434 with no blocking or medium findings.
What I checked:
schema_loader.pynow caches_ValidatorSpecinputs rather than a sharedDraft7Validator, so eachget_validator/get_named_validatorlookup mints a fresh validator + RefResolver. This correctly eliminates the concurrent-corruption of the mutable scope stack that caused #1434.- RefResolver's class is imported once under lock; jsonschema's deprecation warning is on attribute access, not instantiation, so per-call
make()stays clean under-W error. - Compact-store branch is gated on
_has_external_refsand only applied to flattened bundled schemas; internal callers use the returned validator immediately, so the singleton→fresh-instance change breaks nothing. - Public signatures unchanged;
fix(validation):is the correct semver signal. No public-surface break, no generated-code hand-edits, no CI-gate tampering, no credential-in-metadata concern. - New tests added for per-version loading and thread safety covering the fixed branch.
No findings. high_risk false, gated_paths false, no author-team gate, no prior decision. Rows 1–8 do not fire; falls through to row 9.
There was a problem hiding this comment.
Ladon verdict: Approve
Approve — clean subsequent pass.
This PR adds _reachable_registry_store (a reachable-subset optimization for the offline validator store) plus a fragment-$id guard in get_validator, with three accompanying test additions (equivalence, per-version, thread-safety). The function is conservative: any relative ref, nested $id, fragment root ID, non-http scheme, or missing target falls back to the full registry, and urlsplit().geturl() normalization plus insertion-order preservation mirror jsonschema's URIDict so full-vs-compact resolution is equivalent (asserted by the new equivalence tests).
No Critical/High/Medium findings in this run. The prior decision was also approve and the delta maintains that clean state. No high-risk paths, no gated paths, no author-team gate. review_decision is REVIEW_REQUIRED but gated_paths is false, so row 2 does not fire. Falls through to row 9 → approve.
Threaded callers could receive the same validator from either schema loader and corrupt its mutable reference-resolution scope stack, causing intermittent
_RefResolutionErrorexceptions on valid payloads.Cache schema inputs and reference stores, then create a fresh
Draft7ValidatorandRefResolverfor each lookup. Schema reads and reference discovery remain cached. Normalized bundles with fragment-only references use a root store; modular schemas use a prepared closure that preserves all reachable document aliases and ID precedence. Ambiguous scopes and missing targets retain the full registry and existing offline failure behavior. The resolver class is imported lazily once under a lock so concurrent lookups do not race on process warning filters.Public signatures and validation semantics remain unchanged. Callers should obtain a validator per request or thread, as documented in both loader docstrings.
Validation:
Fixes #1434.