Skip to content

Commit 6d99fd8

Browse files
committed
Support patternProperties in regex variants
1 parent 5cf5ae1 commit 6d99fd8

File tree

3 files changed

+79
-8
lines changed

3 files changed

+79
-8
lines changed

docs/usage.rst

+2-7
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,8 @@ Example usage:
187187
~~~~~~~~~~~~~~~~~~
188188

189189
Set a mode for handling of the ``"regex"`` value for ``"format"`` and the mode
190-
for ``"pattern"`` interpretation. The modes are as follows:
190+
for ``"pattern"`` and ``"patternProperties"`` interpretation.
191+
The modes are as follows:
191192

192193
.. list-table:: Regex Options
193194
:widths: 15 30
@@ -202,12 +203,6 @@ for ``"pattern"`` interpretation. The modes are as follows:
202203
* - python
203204
- Use Python regex syntax.
204205

205-
.. note::
206-
207-
This only controls the regex mode used for ``format`` and ``pattern``.
208-
``patternProperties`` is not currently controlled, and always uses the
209-
Python engine.
210-
211206
Other Options
212207
--------------
213208

src/check_jsonschema/regex_variants.py

+73
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ def pattern_keyword(
1919
self, validator: t.Any, pattern: str, instance: str, schema: t.Any
2020
) -> t.Iterator[jsonschema.ValidationError]: ...
2121

22+
def patternProperties_keyword(
23+
self,
24+
validator: t.Any,
25+
patternProperties: dict[str, t.Any],
26+
instance: dict[str, t.Any],
27+
schema: t.Any,
28+
) -> t.Iterator[jsonschema.ValidationError]: ...
29+
2230

2331
class RegexImplementation:
2432
"""
@@ -40,6 +48,9 @@ def __init__(self, variant: RegexVariantName) -> None:
4048

4149
self.check_format = self._real_implementation.check_format
4250
self.pattern_keyword = self._real_implementation.pattern_keyword
51+
self.patternProperties_keyword = (
52+
self._real_implementation.patternProperties_keyword
53+
)
4354

4455

4556
class _UnicodeRegressImplementation:
@@ -65,6 +76,27 @@ def pattern_keyword(
6576
if not regress_pattern.find(instance):
6677
yield jsonschema.ValidationError(f"{instance!r} does not match {pattern!r}")
6778

79+
def patternProperties_keyword(
80+
self,
81+
validator: t.Any,
82+
patternProperties: dict[str, t.Any],
83+
instance: dict[str, t.Any],
84+
schema: t.Any,
85+
) -> t.Iterator[jsonschema.ValidationError]:
86+
if not validator.is_type(instance, "object"):
87+
return
88+
89+
for pattern, subschema in patternProperties.items():
90+
regress_pattern = regress.Regex(pattern, flags="u")
91+
for k, v in instance.items():
92+
if regress_pattern.find(k):
93+
yield from validator.descend(
94+
v,
95+
subschema,
96+
path=k,
97+
schema_path=pattern,
98+
)
99+
68100

69101
class _NonunicodeRegressImplementation:
70102
def check_format(self, instance: t.Any) -> bool:
@@ -89,6 +121,27 @@ def pattern_keyword(
89121
if not regress_pattern.find(instance):
90122
yield jsonschema.ValidationError(f"{instance!r} does not match {pattern!r}")
91123

124+
def patternProperties_keyword(
125+
self,
126+
validator: t.Any,
127+
patternProperties: dict[str, t.Any],
128+
instance: dict[str, t.Any],
129+
schema: t.Any,
130+
) -> t.Iterator[jsonschema.ValidationError]:
131+
if not validator.is_type(instance, "object"):
132+
return
133+
134+
for pattern, subschema in patternProperties.items():
135+
regress_pattern = regress.Regex(pattern)
136+
for k, v in instance.items():
137+
if regress_pattern.find(k):
138+
yield from validator.descend(
139+
v,
140+
subschema,
141+
path=k,
142+
schema_path=pattern,
143+
)
144+
92145

93146
class _PythonImplementation:
94147
def check_format(self, instance: t.Any) -> bool:
@@ -112,3 +165,23 @@ def pattern_keyword(
112165
yield jsonschema.ValidationError(f"pattern {pattern!r} failed to compile")
113166
if not re_pattern.search(instance):
114167
yield jsonschema.ValidationError(f"{instance!r} does not match {pattern!r}")
168+
169+
def patternProperties_keyword(
170+
self,
171+
validator: t.Any,
172+
patternProperties: dict[str, t.Any],
173+
instance: dict[str, t.Any],
174+
schema: t.Any,
175+
) -> t.Iterator[jsonschema.ValidationError]:
176+
if not validator.is_type(instance, "object"):
177+
return
178+
179+
for pattern, subschema in patternProperties.items():
180+
for k, v in instance.items():
181+
if re.search(pattern, k):
182+
yield from validator.descend(
183+
v,
184+
subschema,
185+
path=k,
186+
schema_path=pattern,
187+
)

src/check_jsonschema/schema_loader/main.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,10 @@ def _extend_with_pattern_implementation(
5252
) -> type[jsonschema.Validator]:
5353
return jsonschema.validators.extend(
5454
validator_class,
55-
{"pattern": regex_impl.pattern_keyword},
55+
{
56+
"pattern": regex_impl.pattern_keyword,
57+
"patternProperties": regex_impl.patternProperties_keyword,
58+
},
5659
)
5760

5861

0 commit comments

Comments
 (0)