Skip to content

Commit e69cb7a

Browse files
committed
Warn at runtime when subclassing validator classes.
Doing so was not intended to be public API, though it seems some downstream libraries do so. A future version will make this an error, as it is brittle and better served by composing validator objects instead. Feel free to reach out if there are any cases where changing existing code seems difficult and I can try to provide guidance. Refs: #982
1 parent 549f9e5 commit e69cb7a

File tree

3 files changed

+45
-4
lines changed

3 files changed

+45
-4
lines changed

jsonschema/tests/test_deprecations.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,3 +121,28 @@ def test_Validator_iter_errors_two_arguments(self):
121121
"Passing a schema to Validator.iter_errors is deprecated ",
122122
),
123123
)
124+
125+
def test_Validator_subclassing(self):
126+
"""
127+
As of v4.12.0, subclassing a validator class produces an explicit
128+
deprecation warning.
129+
130+
This was never intended to be public API (and some comments over the
131+
years in issues said so, but obviously that's not a great way to make
132+
sure it's followed).
133+
134+
A future version will explicitly raise an error.
135+
"""
136+
137+
with self.assertWarns(DeprecationWarning) as w:
138+
class Subclass(validators.Draft202012Validator):
139+
pass
140+
141+
self.assertEqual(w.filename, __file__)
142+
self.assertTrue(
143+
str(w.warning).startswith("Subclassing validator classes is "),
144+
)
145+
146+
with self.assertWarns(DeprecationWarning) as w:
147+
class AnotherSubclass(validators.create(meta_schema={})):
148+
pass

jsonschema/tests/test_validators.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1485,10 +1485,11 @@ def test_evolve_with_subclass(self):
14851485
the interim, we haven't broken those users.
14861486
"""
14871487

1488-
@attr.s
1489-
class OhNo(self.Validator):
1490-
foo = attr.ib(factory=lambda: [1, 2, 3])
1491-
_bar = attr.ib(default=37)
1488+
with self.assertWarns(DeprecationWarning):
1489+
@attr.s
1490+
class OhNo(self.Validator):
1491+
foo = attr.ib(factory=lambda: [1, 2, 3])
1492+
_bar = attr.ib(default=37)
14921493

14931494
validator = OhNo({}, bar=12)
14941495
self.assertEqual(validator.foo, [1, 2, 3])

jsonschema/validators.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,21 @@ class Validator:
191191
resolver = attr.ib(default=None, repr=False)
192192
format_checker = attr.ib(default=None)
193193

194+
def __init_subclass__(cls):
195+
warnings.warn(
196+
(
197+
"Subclassing validator classes is not intended to "
198+
"be part of their public API. A future version "
199+
"will make doing so an error, as the behavior of "
200+
"subclasses isn't guaranteed to stay the same "
201+
"between releases of jsonschema. Instead, prefer "
202+
"composition of validators, wrapping them in an object "
203+
"owned entirely by the downstream library."
204+
),
205+
DeprecationWarning,
206+
stacklevel=2,
207+
)
208+
194209
def __attrs_post_init__(self):
195210
if self.resolver is None:
196211
self.resolver = RefResolver.from_schema(

0 commit comments

Comments
 (0)