Skip to content

Commit 7d18a2e

Browse files
committed
Raise exception if validator mutates instance
Signed-off-by: Stephen Finucane <[email protected]> Closes: python-jsonschema#1338
1 parent dd1cd33 commit 7d18a2e

File tree

2 files changed

+25
-0
lines changed

2 files changed

+25
-0
lines changed

jsonschema/exceptions.py

+14
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,20 @@ def __str__(self):
292292
)
293293

294294

295+
class MutatingValidator(Exception):
296+
"""
297+
A validator mutated the instance.
298+
"""
299+
def __init__(self, validator: Any) -> None:
300+
self.validator = validator
301+
302+
def __str__(self) -> str:
303+
return (
304+
f"Validator {self.validator!r} mutated the instance. "
305+
f"This can cause failures for later validators."
306+
)
307+
308+
295309
class FormatError(Exception):
296310
"""
297311
Validating a format failed.

jsonschema/validators.py

+11
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from collections import deque
77
from collections.abc import Iterable, Mapping, Sequence
8+
import copy
89
from functools import lru_cache
910
from operator import methodcaller
1011
from typing import TYPE_CHECKING
@@ -380,7 +381,12 @@ def iter_errors(self, instance, _schema=None):
380381
return
381382

382383
for validator, k, v in validators:
384+
instance_clone = copy.deepcopy(instance)
383385
errors = validator(self, v, instance, _schema) or ()
386+
if instance != instance_clone:
387+
yield exceptions.MutatingValidator(validator)
388+
del instance_clone
389+
384390
for error in errors:
385391
# set details if not already set by the called fn
386392
error._set(
@@ -428,7 +434,12 @@ def descend(
428434
if validator is None:
429435
continue
430436

437+
instance_clone = copy.deepcopy(instance)
431438
errors = validator(evolved, v, instance, schema) or ()
439+
if instance != instance_clone:
440+
yield exceptions.MutatingValidator(validator)
441+
del instance_clone
442+
432443
for error in errors:
433444
# set details if not already set by the called fn
434445
error._set(

0 commit comments

Comments
 (0)