def merge_errors(self, errors_local, errors_remote):
        """
        Merge errors
        Recursively traverses error graph to merge remote errors into local
        errors to return a new joined graph.

        :param errors_local: dict, local errors, will be updated
        :param errors_remote: dict, remote errors, provides updates
        :return: dict
        """
        for prop in errors_remote:

            # create if doesn't exist
            if prop not in errors_local:
                errors_local[prop] = errors_remote[prop]
                continue

            local = errors_local[prop]
            local = local.errors if isinstance(local, Result) else local
            remote = errors_remote[prop]
            remote = remote.errors if isinstance(remote, Result) else remote

            # check compatibility
            if not isinstance(local, type(remote)):
                msg = 'Type mismatch on property [{}] when merging errors. '
                msg += 'Unable to merge [{}] into [{}]'
                raise x.UnableToMergeResultsType(msg.format(
                    prop,
                    type(errors_remote[prop]),
                    type(self.errors[prop])
                ))

            mismatch = 'Unable to merge nested entity errors with nested '
            mismatch += 'collection errors on property [{}]'
            if 'schema' in local and 'collection' in remote:
                raise x.UnableToMergeResultsType(mismatch.format(prop))
            if 'collection' in local and 'schema' in remote:
                raise x.UnableToMergeResultsType(mismatch.format(prop))

            # merge simple & state
            if type(remote) is list:
                errors_local[prop].extend(remote)
                continue

            # merge direct errors on nested entities and collection
            if 'direct' in remote and 'direct' in local:
                errors_local[prop]['direct'].extend(remote['direct'])

            # merge nested schema errors
            if 'schema' in remote and 'schema' in local:
                errors_local[prop]['schema'] = self.merge_errors(
                    errors_local[prop]['schema'],
                    remote['schema']
                )

            # merge nested collections errors
            if 'collection' in remote and 'collection' in local:
                for index, result in remote['collection'].items():
                    if index not in local['collection']:
                        errors_local[prop]['collection'][index] = result
                    else:
                        merged = self.merge_errors(
                            errors_local[prop]['collection'][index].errors,
                            errors_remote[prop]['collection'][index].errors,
                        )
                        errors_local[prop]['collection'][index] = merged

        # and return
        return errors_local
def merge_errors ( self , errors_local , errors_remote ) : for prop in errors_remote : if prop not in errors_local : errors_local [ prop ] = errors_remote [ prop ] continue local = errors_local [ prop ] local = local . errors if isinstance ( local , Result ) else local remote = errors_remote [ prop ] remote = remote . errors if isinstance ( remote , Result ) else remote if not isinstance ( local , type ( remote ) ) : msg = 'Type mismatch on property [{}] when merging errors. ' msg += 'Unable to merge [{}] into [{}]' raise x . UnableToMergeResultsType ( msg . format ( prop , type ( errors_remote [ prop ] ) , type ( self . errors [ prop ] ) ) ) mismatch = 'Unable to merge nested entity errors with nested ' mismatch += 'collection errors on property [{}]' if 'schema' in local and 'collection' in remote : raise x . UnableToMergeResultsType ( mismatch . format ( prop ) ) if 'collection' in local and 'schema' in remote : raise x . UnableToMergeResultsType ( mismatch . format ( prop ) ) if type ( remote ) is list : errors_local [ prop ] . extend ( remote ) continue if 'direct' in remote and 'direct' in local : errors_local [ prop ] [ 'direct' ] . extend ( remote [ 'direct' ] ) if 'schema' in remote and 'schema' in local : errors_local [ prop ] [ 'schema' ] = self . merge_errors ( errors_local [ prop ] [ 'schema' ] , remote [ 'schema' ] ) if 'collection' in remote and 'collection' in local : for index , result in remote [ 'collection' ] . items ( ) : if index not in local [ 'collection' ] : errors_local [ prop ] [ 'collection' ] [ index ] = result else : merged = self . merge_errors ( errors_local [ prop ] [ 'collection' ] [ index ] . errors , errors_remote [ prop ] [ 'collection' ] [ index ] . errors , ) errors_local [ prop ] [ 'collection' ] [ index ] = merged return errors_local
Merge errors
        Recursively traverses error graph to merge remote errors into local
        errors to return a new joined graph.

        :param errors_local: dict, local errors, will be updated
        :param errors_remote: dict, remote errors, provides updates
        :return: dict