From 3b35731082e75d686024342e2507ea50f2ef7657 Mon Sep 17 00:00:00 2001 From: Julian Berman Date: Mon, 17 Apr 2023 11:29:08 -0400 Subject: Wrap raised referencing exceptions in a RefResolutionError subclass. Even though RefResolutionError is deprecated (and already raises a suitable warning), this should make some additional code warn rather than blow up (i.e. if someone is catching RefResolutionError, that code should continue to work though it will raise a warning). Users can transition to non-deprecated APIs by directly catching referencing.exceptions.Unresolvable. Closes: #1069 --- jsonschema/exceptions.py | 10 ++++++++++ jsonschema/tests/test_deprecations.py | 34 ++++++++++++++++++++++++++++++++++ jsonschema/validators.py | 7 ++++++- 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/jsonschema/exceptions.py b/jsonschema/exceptions.py index d1ee50a..2ad9d3c 100644 --- a/jsonschema/exceptions.py +++ b/jsonschema/exceptions.py @@ -11,6 +11,7 @@ import heapq import itertools import warnings +from referencing.exceptions import Unresolvable as _Unresolvable import attr from jsonschema import _utils @@ -210,6 +211,15 @@ class _RefResolutionError(Exception): return str(self._cause) +class _WrappedReferencingError(_RefResolutionError, _Unresolvable): + def __init__(self, cause: _Unresolvable): + object.__setattr__(self, "_cause", cause) + + def __getattribute__(self, attr): + cause = object.__getattribute__(self, "_cause") + return getattr(cause, attr) + + class UndefinedTypeCheck(Exception): """ A type checker was asked to check a type it did not have registered. diff --git a/jsonschema/tests/test_deprecations.py b/jsonschema/tests/test_deprecations.py index 2beb02f..cf5538f 100644 --- a/jsonschema/tests/test_deprecations.py +++ b/jsonschema/tests/test_deprecations.py @@ -3,6 +3,8 @@ import importlib import subprocess import sys +import referencing.exceptions + from jsonschema import FormatChecker, exceptions, validators @@ -169,6 +171,38 @@ class TestDeprecations(TestCase): self.assertEqual(RefResolutionError, exceptions._RefResolutionError) self.assertEqual(w.filename, __file__) + def test_catching_Unresolvable_directly(self): + """ + This behavior is the intended behavior (i.e. it's not deprecated), but + given we do "tricksy" things in the iterim to wrap exceptions in a + multiple inheritance subclass, we need to be extra sure it works and + stays working. + """ + validator = validators.Draft202012Validator({"$ref": "http://foo.com"}) + + with self.assertRaises(referencing.exceptions.Unresolvable) as e: + validator.validate(12) + + expected = referencing.exceptions.Unresolvable(ref="http://foo.com") + self.assertEqual(e.exception, expected) + + def test_catching_Unresolvable_via_RefResolutionError(self): + """ + Until RefResolutionError is removed, it is still possible to catch + exceptions from reference resolution using it, even though they may + have been raised by referencing. + """ + with self.assertWarns(DeprecationWarning): + from jsonschema import RefResolutionError + + validator = validators.Draft202012Validator({"$ref": "http://foo.com"}) + + with self.assertRaises(referencing.exceptions.Unresolvable): + validator.validate(12) + + with self.assertRaises(RefResolutionError): + validator.validate(12) + def test_Validator_subclassing(self): """ As of v4.12.0, subclassing a validator class produces an explicit diff --git a/jsonschema/validators.py b/jsonschema/validators.py index 23ea17c..72af8fc 100644 --- a/jsonschema/validators.py +++ b/jsonschema/validators.py @@ -19,6 +19,7 @@ from attrs import define, field, fields from jsonschema_specifications import REGISTRY as SPECIFICATIONS from referencing import Specification from rpds import HashTrieMap +import referencing.exceptions import referencing.jsonschema from jsonschema import ( @@ -401,7 +402,11 @@ def create( def _validate_reference(self, ref, instance): if self._ref_resolver is None: - resolved = self._resolver.lookup(ref) + try: + resolved = self._resolver.lookup(ref) + except referencing.exceptions.Unresolvable as err: + raise exceptions._WrappedReferencingError(err) + return self.descend( instance, resolved.contents, -- cgit v1.2.1