Пример #1
0
def resolve_column(column: Column,
                   in_row: Row,
                   definitions: dict,
                   content_resolver=None,
                   field_resolver=None) -> (str, ColumnResolutionData):
    """ Return the content of a column by recursively resolving any fields within. """

    column_references = []
    definition_references = []

    is_resolving_definition = in_row.data == definitions

    resolved_column_content = column.content

    for reference_field in fields(resolved_column_content):
        # determine whether this field is a row reference
        reference_column, reference_row, is_invalid_reference = get_row_reference(
            reference_field,
            in_reference_row=in_row,
            in_reference_column=column)

        if is_invalid_reference:
            # it was a row reference, but the reference was invalid- so we gotta move on
            continue

        if reference_column is None:
            # it was not a row reference
            reference_column = reference_field.inner_content

        # determine if the field occurs as a definition
        is_definition = reference_column in definitions
        # determine if the field occurs as a column in the current row- note that if
        # the current row is actually the definitions, then it actually *is* a column,
        # but it should not be treated as such
        is_column = reference_column in reference_row.data and not is_resolving_definition

        if not is_column and not is_definition:
            # the field is not a reference that can be resolved right now, so skip it
            # (it might be an image reference, an include field or similar)
            continue

        context = (os.path.basename(reference_row.data_path)
                   if reference_row.data_path is not None else
                   ('definitions' if is_resolving_definition else ''))

        # this field refers to the column in the same row that is already being resolved;
        # i.e. an infinite cycle (if it was another row it might not be infinite)
        is_infinite_column_ref = reference_column == column.name and reference_row is in_row
        # this definition field refers to itself; also leading to an infinite cycle
        is_infinite_definition_ref = (is_infinite_column_ref and is_definition
                                      and not is_column)

        if is_infinite_definition_ref:
            WarningDisplay.unresolved_infinite_definition_reference(
                WarningContext(context,
                               row_index=reference_row.row_index,
                               column=column.name),
                reference_field.inner_content)

            continue

        if is_infinite_column_ref:
            WarningDisplay.unresolved_infinite_column_reference(
                WarningContext(context,
                               row_index=reference_row.row_index,
                               column=column.name),
                reference_field.inner_content)

            continue

        # only resolve further if it would not lead to an infinite cycle
        use_column = is_column and not is_infinite_column_ref
        # however, the field might ambiguously refer to a definition too,
        # so if this could be resolved, use the definition value instead
        use_definition = (is_definition and not use_column
                          and not is_infinite_definition_ref)

        if not use_column and not use_definition:
            # could not resolve this field at all
            WarningDisplay.unresolved_reference(
                WarningContext(context,
                               row_index=reference_row.row_index,
                               column=column.name),
                reference_field.inner_content)

            continue

        if use_column:
            # prioritize the column reference by resolving it first,
            # even if it could also be a definition instead (but warn about that later)
            column_reference_content, resolution_data = get_column_contentd(
                reference_column, reference_row, definitions, content_resolver,
                field_resolver)
        elif use_definition:
            # resolve the definition reference, keeping track of any discovered references
            column_reference_content, resolution_data = get_definition_contentd(
                definition=reference_column,
                in_definitions=definitions,
                content_resolver=content_resolver,
                field_resolver=field_resolver)

        column_references.extend(list(resolution_data.column_references))
        definition_references.extend(
            list(resolution_data.definition_references))

        occurences = 0

        if field_resolver is not None:
            resolved_column_content, occurences = field_resolver(
                reference_field.inner_content, column_reference_content,
                resolved_column_content)

        if occurences > 0:
            if use_column:
                column_references.append(reference_column)
            elif use_definition:
                definition_references.append(reference_column)

            if is_definition and is_column and not is_resolving_definition:
                # the reference could point to both a column and a definition
                if use_column:
                    # the column data was preferred over the definition data
                    WarningDisplay.ambiguous_reference_used_column(
                        WarningContext(context), reference_column,
                        column_reference_content)
                elif use_definition:
                    # the definition data was preferred over the column data;
                    # this is likely because the column reference was an infinite reference
                    # don't inform about that detail, but do warn that the definition was used
                    WarningDisplay.ambiguous_reference_used_definition(
                        WarningContext(context), reference_column,
                        column_reference_content)

    resolution_data = ColumnResolutionData(set(column_references),
                                           set(definition_references))

    return resolved_column_content, resolution_data
Пример #2
0
def resolve_column(column: Column,
                   in_row: Row,
                   definitions: dict,
                   content_resolver=None,
                   field_resolver=None) -> (str, ColumnResolutionData):
    """ Return the content of a column by recursively resolving any fields within. """

    column_references = []
    definition_references = []

    is_resolving_definition = in_row.data == definitions

    resolved_column_content = column.content

    for reference_field in fields(resolved_column_content):
        # determine whether this field is a row reference
        reference_column, reference_row, is_invalid_reference = get_row_reference(
            reference_field, in_reference_row=in_row, in_reference_column=column)

        if is_invalid_reference:
            # it was a row reference, but the reference was invalid- so we gotta move on
            continue

        if reference_column is None:
            # it was not a row reference
            reference_column = reference_field.inner_content

        # determine if the field occurs as a definition
        is_definition = reference_column in definitions
        # determine if the field occurs as a column in the current row- note that if
        # the current row is actually the definitions, then it actually *is* a column,
        # but it should not be treated as such
        is_column = reference_column in reference_row.data and not is_resolving_definition

        if not is_column and not is_definition:
            # the field is not a reference that can be resolved right now, so skip it
            # (it might be an image reference, an include field or similar)
            continue

        context = (os.path.basename(reference_row.data_path)
                   if reference_row.data_path is not None
                   else ('definitions' if is_resolving_definition else ''))

        # this field refers to the column in the same row that is already being resolved;
        # i.e. an infinite cycle (if it was another row it might not be infinite)
        is_infinite_column_ref = reference_column == column.name and reference_row is in_row
        # this definition field refers to itself; also leading to an infinite cycle
        is_infinite_definition_ref = (is_infinite_column_ref
                                      and is_definition
                                      and not is_column)

        if is_infinite_definition_ref:
            WarningDisplay.unresolved_infinite_definition_reference(
                WarningContext(context, row_index=reference_row.row_index, column=column.name),
                reference_field.inner_content)

            continue

        if is_infinite_column_ref:
            WarningDisplay.unresolved_infinite_column_reference(
                WarningContext(context, row_index=reference_row.row_index, column=column.name),
                reference_field.inner_content)

            continue

        # only resolve further if it would not lead to an infinite cycle
        use_column = is_column and not is_infinite_column_ref
        # however, the field might ambiguously refer to a definition too,
        # so if this could be resolved, use the definition value instead
        use_definition = (is_definition
                          and not use_column
                          and not is_infinite_definition_ref)

        if not use_column and not use_definition:
            # could not resolve this field at all
            WarningDisplay.unresolved_reference(
                WarningContext(context, row_index=reference_row.row_index, column=column.name),
                reference_field.inner_content)

            continue

        if use_column:
            # prioritize the column reference by resolving it first,
            # even if it could also be a definition instead (but warn about that later)
            column_reference_content, resolution_data = get_column_contentd(
                reference_column, reference_row, definitions,
                content_resolver, field_resolver)
        elif use_definition:
            # resolve the definition reference, keeping track of any discovered references
            column_reference_content, resolution_data = get_definition_contentd(
                definition=reference_column, in_definitions=definitions,
                content_resolver=content_resolver, field_resolver=field_resolver)

        column_references.extend(list(resolution_data.column_references))
        definition_references.extend(list(resolution_data.definition_references))

        occurences = 0

        if field_resolver is not None:
            resolved_column_content, occurences = field_resolver(
                reference_field.inner_content, column_reference_content, resolved_column_content)

        if occurences > 0:
            if use_column:
                column_references.append(reference_column)
            elif use_definition:
                definition_references.append(reference_column)

            if is_definition and is_column and not is_resolving_definition:
                # the reference could point to both a column and a definition
                if use_column:
                    # the column data was preferred over the definition data
                    WarningDisplay.ambiguous_reference_used_column(
                        WarningContext(context), reference_column, column_reference_content)
                elif use_definition:
                    # the definition data was preferred over the column data;
                    # this is likely because the column reference was an infinite reference
                    # don't inform about that detail, but do warn that the definition was used
                    WarningDisplay.ambiguous_reference_used_definition(
                        WarningContext(context), reference_column, column_reference_content)

    resolution_data = ColumnResolutionData(
        set(column_references), set(definition_references))

    return resolved_column_content, resolution_data