1"""Utilities for storing collections of error messages. 2 3.. warning:: 4 5 This module is treated as private API. 6 Users should not need to use this module directly. 7""" 8 9from marshmallow.exceptions import SCHEMA 10 11 12class ErrorStore: 13 def __init__(self): 14 #: Dictionary of errors stored during serialization 15 self.errors = {} 16 17 def store_error(self, messages, field_name=SCHEMA, index=None): 18 # field error -> store/merge error messages under field name key 19 # schema error -> if string or list, store/merge under _schema key 20 # -> if dict, store/merge with other top-level keys 21 if field_name != SCHEMA or not isinstance(messages, dict): 22 messages = {field_name: messages} 23 if index is not None: 24 messages = {index: messages} 25 self.errors = merge_errors(self.errors, messages) 26 27 28def merge_errors(errors1, errors2): 29 """Deeply merge two error messages. 30 31 The format of ``errors1`` and ``errors2`` matches the ``message`` 32 parameter of :exc:`marshmallow.exceptions.ValidationError`. 33 """ 34 if not errors1: 35 return errors2 36 if not errors2: 37 return errors1 38 if isinstance(errors1, list): 39 if isinstance(errors2, list): 40 return errors1 + errors2 41 if isinstance(errors2, dict): 42 return dict(errors2, **{SCHEMA: merge_errors(errors1, errors2.get(SCHEMA))}) 43 return errors1 + [errors2] 44 if isinstance(errors1, dict): 45 if isinstance(errors2, list): 46 return dict(errors1, **{SCHEMA: merge_errors(errors1.get(SCHEMA), errors2)}) 47 if isinstance(errors2, dict): 48 errors = dict(errors1) 49 for key, val in errors2.items(): 50 if key in errors: 51 errors[key] = merge_errors(errors[key], val) 52 else: 53 errors[key] = val 54 return errors 55 return dict(errors1, **{SCHEMA: merge_errors(errors1.get(SCHEMA), errors2)}) 56 if isinstance(errors2, list): 57 return [errors1] + errors2 58 if isinstance(errors2, dict): 59 return dict(errors2, **{SCHEMA: merge_errors(errors1, errors2.get(SCHEMA))}) 60 return [errors1, errors2] 61