@@ -378,9 +378,9 @@ def deep_iterable(member_validator, iterable_validator=None):
378378
379379@attrs (repr = False , slots = True , unsafe_hash = True ) 
380380class  _DeepMapping :
381-     key_validator  =  attrib (validator = is_callable ())
382-     value_validator  =  attrib (validator = is_callable ())
383-     mapping_validator  =  attrib (default = None ,  validator = optional (is_callable ()))
381+     key_validator  =  attrib (validator = optional ( is_callable () ))
382+     value_validator  =  attrib (validator = optional ( is_callable () ))
383+     mapping_validator  =  attrib (validator = optional (is_callable ()))
384384
385385    def  __call__ (self , inst , attr , value ):
386386        """ 
@@ -390,30 +390,51 @@ def __call__(self, inst, attr, value):
390390            self .mapping_validator (inst , attr , value )
391391
392392        for  key  in  value :
393-             self .key_validator (inst , attr , key )
394-             self .value_validator (inst , attr , value [key ])
393+             if  self .key_validator  is  not None :
394+                 self .key_validator (inst , attr , key )
395+             if  self .value_validator  is  not None :
396+                 self .value_validator (inst , attr , value [key ])
395397
396398    def  __repr__ (self ):
397399        return  f"<deep_mapping validator for objects mapping { self .key_validator !r} { self .value_validator !r}  
398400
399401
400- def  deep_mapping (key_validator , value_validator , mapping_validator = None ):
402+ def  deep_mapping (
403+     key_validator = None , value_validator = None , mapping_validator = None 
404+ ):
401405    """ 
402406    A validator that performs deep validation of a dictionary. 
403407
408+     All validators are optional, but at least one of *key_validator* or 
409+     *value_validator* must be provided. 
410+ 
404411    Args: 
405412        key_validator: Validator to apply to dictionary keys. 
406413
407414        value_validator: Validator to apply to dictionary values. 
408415
409416        mapping_validator: 
410-             Validator to apply to top-level mapping attribute (optional) . 
417+             Validator to apply to top-level mapping attribute. 
411418
412419    .. versionadded:: 19.1.0 
413420
421+     .. versionchanged:: 25.4.0 
422+        *key_validator* and *value_validator* are now optional, but at least one 
423+        of them must be provided. 
424+ 
414425    Raises: 
415-         TypeError: if any sub-validators fail 
426+         TypeError: If any sub-validator fails on validation. 
427+ 
428+         ValueError: 
429+             If neither *key_validator* nor *value_validator* is provided on 
430+             instantiation. 
416431    """ 
432+     if  key_validator  is  None  and  value_validator  is  None :
433+         msg  =  (
434+             "At least one of key_validator or value_validator must be provided" 
435+         )
436+         raise  ValueError (msg )
437+ 
417438    return  _DeepMapping (key_validator , value_validator , mapping_validator )
418439
419440
0 commit comments