11import re
2- from dataclasses import dataclass
2+ from dataclasses import dataclass , field
33from functools import reduce
4- from typing import Any , Iterator , Optional
4+ from typing import Any , Callable , Iterator , Optional
55
66from jsonschema import Draft7Validator , FormatChecker , ValidationError
77
@@ -44,7 +44,7 @@ class for more details, especially about the default values.
4444 _set_should_fields_to_required (schema )
4545
4646 issues = _check_object_against_json_schema (properties , schema )
47- issues += apply_extensions (properties , config .custom_checks )
47+ issues += apply_extensions (properties , config .extensions )
4848 issues = exclude (issues , config .exclusions , properties )
4949
5050 return sorted (set (issues ))
@@ -137,46 +137,31 @@ def _validation_errors_to_issues(
137137 return _map (schema_errors , _create_issue )
138138
139139
140- def _handle_grouped_error (
141- schema_errors : list [SchemaError ], parent_error : SchemaError
142- ) -> list [SchemaError ]:
143- """Handle grouped schema errors that need special treatment.
144-
145- Args:
146- schema_errors: All remaining schema errors.
147- parent_error: The parent error of a group.
148-
149- Returns:
150- The schema errors after processing.
151- """
152- # Handle issues at $.resources[x]
153-
154- if parent_error .schema_path .endswith ("resources/items/oneOf" ):
155- schema_errors = _handle_S_resources_x (parent_error , schema_errors )
156-
157- # Handle issues at $.resources[x].path
158- if parent_error .schema_path .endswith ("resources/items/properties/path/oneOf" ):
159- schema_errors = _handle_S_resources_x_path (parent_error , schema_errors )
140+ @dataclass (frozen = True )
141+ class SchemaErrorEdits :
142+ """Expresses which errors to add to or remove from schema errors."""
160143
161- return schema_errors
144+ add : list [SchemaError ] = field (default_factory = list )
145+ remove : list [SchemaError ] = field (default_factory = list )
162146
163147
164148def _handle_S_resources_x (
165149 parent_error : SchemaError ,
166150 schema_errors : list [SchemaError ],
167- ) -> list [ SchemaError ] :
151+ ) -> SchemaErrorEdits :
168152 """Do not flag missing `path` and `data` separately."""
153+ edits = SchemaErrorEdits ()
169154 errors_in_group = _filter (schema_errors , lambda error : error .parent == parent_error )
170155 # If the parent error is caused by other errors, remove it
171156 if errors_in_group :
172- schema_errors .remove (parent_error )
157+ edits .remove . append (parent_error )
173158
174159 path_or_data_required_errors = _filter (
175160 errors_in_group , _path_or_data_required_error
176161 )
177162 # If path and data are both missing, add a more informative error
178163 if len (path_or_data_required_errors ) > 1 :
179- schema_errors .append (
164+ edits . add .append (
180165 SchemaError (
181166 message = (
182167 "This resource has no `path` or `data` field. "
@@ -189,31 +174,31 @@ def _handle_S_resources_x(
189174 )
190175
191176 # Remove all required errors on path and data
192- return _filter (
193- schema_errors , lambda error : error not in path_or_data_required_errors
194- )
177+ edits .remove .extend (path_or_data_required_errors )
178+ return edits
195179
196180
197181def _handle_S_resources_x_path (
198182 parent_error : SchemaError ,
199183 schema_errors : list [SchemaError ],
200- ) -> list [ SchemaError ] :
184+ ) -> SchemaErrorEdits :
201185 """Only flag errors for the relevant type.
202186
203187 If `path` is a string, flag errors for the string-based schema.
204188 If `path` is an array, flag errors for the array-based schema.
205189 """
190+ edits = SchemaErrorEdits ()
206191 errors_in_group = _filter (schema_errors , lambda error : error .parent == parent_error )
207192 type_errors = _filter (errors_in_group , _is_path_type_error )
208193 only_type_errors = len (errors_in_group ) == len (type_errors )
209194
210195 if type_errors :
211- schema_errors .remove (parent_error )
196+ edits .remove . append (parent_error )
212197
213198 # If the only error is that $.resources[x].path is of the wrong type,
214199 # add a more informative error
215200 if only_type_errors :
216- schema_errors .append (
201+ edits . add .append (
217202 SchemaError (
218203 message = "The `path` property must be either a string or an array." ,
219204 type = "type" ,
@@ -223,7 +208,52 @@ def _handle_S_resources_x_path(
223208 )
224209
225210 # Remove all original type errors on $.resources[x].path
226- return _filter (schema_errors , lambda error : error not in type_errors )
211+ edits .remove .extend (type_errors )
212+ return edits
213+
214+
215+ _schema_path_to_handler : list [
216+ tuple [str , Callable [[SchemaError , list [SchemaError ]], SchemaErrorEdits ]]
217+ ] = [
218+ ("resources/items/oneOf" , _handle_S_resources_x ),
219+ ("resources/items/properties/path/oneOf" , _handle_S_resources_x_path ),
220+ ]
221+
222+
223+ def _handle_grouped_error (
224+ schema_errors : list [SchemaError ], parent_error : SchemaError
225+ ) -> list [SchemaError ]:
226+ """Handle grouped schema errors that need special treatment.
227+
228+ Args:
229+ schema_errors: All remaining schema errors.
230+ parent_error: The parent error of a group.
231+
232+ Returns:
233+ The schema errors after processing.
234+ """
235+
236+ def _get_edits (
237+ handlers : list [
238+ tuple [str , Callable [[SchemaError , list [SchemaError ]], SchemaErrorEdits ]]
239+ ],
240+ ) -> SchemaErrorEdits :
241+ schema_path , handler = handlers [0 ]
242+ edits = SchemaErrorEdits ()
243+ if parent_error .schema_path .endswith (schema_path ):
244+ edits = handler (parent_error , schema_errors )
245+
246+ if len (handlers ) == 1 :
247+ return edits
248+
249+ next_edits = _get_edits (handlers [1 :])
250+ return SchemaErrorEdits (
251+ add = edits .add + next_edits .add ,
252+ remove = edits .remove + next_edits .remove ,
253+ )
254+
255+ edits = _get_edits (_schema_path_to_handler )
256+ return _filter (schema_errors , lambda error : error not in edits .remove ) + edits .add
227257
228258
229259def _validation_error_to_schema_errors (error : ValidationError ) -> list [SchemaError ]:
0 commit comments