From 7d18a2eb1dd50318d43df5693e784c49fcd86490 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 5 Mar 2025 14:52:24 +0000 Subject: [PATCH 1/2] Raise exception if validator mutates instance Signed-off-by: Stephen Finucane Closes: #1338 --- jsonschema/exceptions.py | 14 ++++++++++++++ jsonschema/validators.py | 11 +++++++++++ 2 files changed, 25 insertions(+) diff --git a/jsonschema/exceptions.py b/jsonschema/exceptions.py index 3dcd29667..7278180fe 100644 --- a/jsonschema/exceptions.py +++ b/jsonschema/exceptions.py @@ -292,6 +292,20 @@ def __str__(self): ) +class MutatingValidator(Exception): + """ + A validator mutated the instance. + """ + def __init__(self, validator: Any) -> None: + self.validator = validator + + def __str__(self) -> str: + return ( + f"Validator {self.validator!r} mutated the instance. " + f"This can cause failures for later validators." + ) + + class FormatError(Exception): """ Validating a format failed. diff --git a/jsonschema/validators.py b/jsonschema/validators.py index b8ca3bd45..991bf74c7 100644 --- a/jsonschema/validators.py +++ b/jsonschema/validators.py @@ -5,6 +5,7 @@ from collections import deque from collections.abc import Iterable, Mapping, Sequence +import copy from functools import lru_cache from operator import methodcaller from typing import TYPE_CHECKING @@ -380,7 +381,12 @@ def iter_errors(self, instance, _schema=None): return for validator, k, v in validators: + instance_clone = copy.deepcopy(instance) errors = validator(self, v, instance, _schema) or () + if instance != instance_clone: + yield exceptions.MutatingValidator(validator) + del instance_clone + for error in errors: # set details if not already set by the called fn error._set( @@ -428,7 +434,12 @@ def descend( if validator is None: continue + instance_clone = copy.deepcopy(instance) errors = validator(evolved, v, instance, schema) or () + if instance != instance_clone: + yield exceptions.MutatingValidator(validator) + del instance_clone + for error in errors: # set details if not already set by the called fn error._set( From 46a083c702ffa5ddadc0a1c94ec5adad5f02d358 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 5 Mar 2025 15:29:20 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- jsonschema/exceptions.py | 1 + jsonschema/validators.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/jsonschema/exceptions.py b/jsonschema/exceptions.py index 7278180fe..089546a7d 100644 --- a/jsonschema/exceptions.py +++ b/jsonschema/exceptions.py @@ -296,6 +296,7 @@ class MutatingValidator(Exception): """ A validator mutated the instance. """ + def __init__(self, validator: Any) -> None: self.validator = validator diff --git a/jsonschema/validators.py b/jsonschema/validators.py index 991bf74c7..a8e270569 100644 --- a/jsonschema/validators.py +++ b/jsonschema/validators.py @@ -5,7 +5,6 @@ from collections import deque from collections.abc import Iterable, Mapping, Sequence -import copy from functools import lru_cache from operator import methodcaller from typing import TYPE_CHECKING @@ -13,6 +12,7 @@ from urllib.request import urlopen from warnings import warn import contextlib +import copy import json import reprlib import warnings