@@ -217,6 +217,23 @@ def __init_subclass__(cls):
217
217
stacklevel = 2 ,
218
218
)
219
219
220
+ def evolve (self , ** changes ):
221
+ cls = self .__class__
222
+ schema = changes .setdefault ("schema" , self .schema )
223
+ NewValidator = validator_for (schema , default = cls )
224
+
225
+ for field in attr .fields (cls ):
226
+ if not field .init :
227
+ continue
228
+ attr_name = field .name
229
+ init_name = field .alias
230
+ if init_name not in changes :
231
+ changes [init_name ] = getattr (self , attr_name )
232
+
233
+ return NewValidator (** changes )
234
+
235
+ cls .evolve = evolve
236
+
220
237
def __attrs_post_init__ (self ):
221
238
if self ._resolver is None :
222
239
self ._resolver = self ._registry .resolver_with_root (
@@ -257,18 +274,10 @@ def resolver(self):
257
274
return self ._ref_resolver
258
275
259
276
def evolve (self , ** changes ):
260
- # Essentially reproduces attr.evolve, but may involve instantiating
261
- # a different class than this one.
262
- cls = self .__class__
263
-
264
277
schema = changes .setdefault ("schema" , self .schema )
265
- NewValidator = validator_for (schema , default = cls )
278
+ NewValidator = validator_for (schema , default = self . __class__ )
266
279
267
- for field in attr .fields (cls ):
268
- if not field .init :
269
- continue
270
- attr_name = field .name # To deal with private attributes.
271
- init_name = field .alias
280
+ for (attr_name , init_name ) in evolve_fields :
272
281
if init_name not in changes :
273
282
changes [init_name ] = getattr (self , attr_name )
274
283
@@ -328,17 +337,46 @@ def descend(
328
337
schema_path = None ,
329
338
resolver = None ,
330
339
):
340
+ if schema is True :
341
+ return
342
+ elif schema is False :
343
+ yield exceptions .ValidationError (
344
+ f"False schema does not allow { instance !r} " ,
345
+ validator = None ,
346
+ validator_value = None ,
347
+ instance = instance ,
348
+ schema = schema ,
349
+ )
350
+ return
351
+
331
352
if resolver is None :
332
353
resolver = self ._resolver .in_subresource (
333
354
specification .create_resource (schema ),
334
355
)
335
- validator = self .evolve (schema = schema , _resolver = resolver )
336
- for error in validator .iter_errors (instance ):
337
- if path is not None :
338
- error .path .appendleft (path )
339
- if schema_path is not None :
340
- error .schema_path .appendleft (schema_path )
341
- yield error
356
+ evolved = self .evolve (schema = schema , _resolver = resolver )
357
+
358
+ for k , v in applicable_validators (schema ):
359
+ validator = evolved .VALIDATORS .get (k )
360
+ if validator is None :
361
+ continue
362
+
363
+ errors = validator (evolved , v , instance , schema ) or ()
364
+ for error in errors :
365
+ # set details if not already set by the called fn
366
+ error ._set (
367
+ validator = k ,
368
+ validator_value = v ,
369
+ instance = instance ,
370
+ schema = schema ,
371
+ type_checker = evolved .TYPE_CHECKER ,
372
+ )
373
+ if k not in {"if" , "$ref" }:
374
+ error .schema_path .appendleft (k )
375
+ if path is not None :
376
+ error .path .appendleft (path )
377
+ if schema_path is not None :
378
+ error .schema_path .appendleft (schema_path )
379
+ yield error
342
380
343
381
def validate (self , * args , ** kwargs ):
344
382
for error in self .iter_errors (* args , ** kwargs ):
@@ -389,6 +427,12 @@ def is_valid(self, instance, _schema=None):
389
427
error = next (self .iter_errors (instance ), None )
390
428
return error is None
391
429
430
+ evolve_fields = [
431
+ (field .name , field .alias )
432
+ for field in attr .fields (Validator )
433
+ if field .init
434
+ ]
435
+
392
436
if version is not None :
393
437
safe = version .title ().replace (" " , "" ).replace ("-" , "" )
394
438
Validator .__name__ = Validator .__qualname__ = f"{ safe } Validator"
0 commit comments