Example #1
0
def strip_styles(template: Template) -> str:
    """ Strip and return any embedded <style></style> content from a template. """

    pattern = r'<style.*?>(.+?)</style>'
    stripped_styles = ''

    search = re.compile(pattern, re.DOTALL)

    # find all style matches and extract embedded styles
    for style_match in re.finditer(pattern, template.content, re.DOTALL):
        # note that we strip the entire style- not the inner content
        style = style_match.group(0).strip()
        # separating each style block for good measure
        stripped_styles = stripped_styles + '\n' + style if len(stripped_styles) > 0 else style

    # finally remove all style matches
    # note that this removes the <style></style> tags too
    template.content = re.sub(search, '', template.content).strip()

    # make sure we keep it clean- no unnecessary newlines or excess whitespace
    stripped_styles = stripped_styles.strip()

    template_field_names = list((field.name for field in fields(stripped_styles)))

    if len(template_field_names) > 0:
        context = template.path

        # if there's any fields in the styles, display a warning about it
        WarningDisplay.fields_in_styles(
            WarningContext(context), template_field_names)

    return stripped_styles
Example #2
0
def strip_styles(template: Template) -> str:
    """ Strip and return any embedded <style></style> content from a template. """

    pattern = r'<style.*?>(.+?)</style>'
    stripped_styles = ''

    search = re.compile(pattern, re.DOTALL)

    # find all style matches and extract embedded styles
    for style_match in re.finditer(pattern, template.content, re.DOTALL):
        # note that we strip the entire style- not the inner content
        style = style_match.group(0).strip()
        # separating each style block for good measure
        stripped_styles = stripped_styles + '\n' + style if len(
            stripped_styles) > 0 else style

    # finally remove all style matches
    # note that this removes the <style></style> tags too
    template.content = re.sub(search, '', template.content).strip()

    # make sure we keep it clean- no unnecessary newlines or excess whitespace
    stripped_styles = stripped_styles.strip()

    template_field_names = list(
        (field.name for field in fields(stripped_styles)))

    if len(template_field_names) > 0:
        context = template.path

        # if there's any fields in the styles, display a warning about it
        WarningDisplay.fields_in_styles(WarningContext(context),
                                        template_field_names)

    return stripped_styles
Example #3
0
    def next_partial_definition_field():
        """ Return the next field likely to contain a partial definition. """

        return first(fields(
            template.content,
            with_name_like=pattern,
            with_context_like=pattern,
            strictly_matching=False))  # match either name or context, or both
Example #4
0
    def next_partial_definition_field():
        """ Return the next field likely to contain a partial definition. """

        return first(
            fields(template.content,
                   with_name_like=pattern,
                   with_context_like=pattern,
                   strictly_matching=False)
        )  # match either name or context, or both
Example #5
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
Example #6
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
Example #7
0
    def next_include_field():
        """ Return the next probable include/inline field. """

        return first(fields(template.content, with_name_like='include|inline'))
Example #8
0
 def __iter__(self):
     return fields(self.content)
Example #9
0
    def next_date_field():
        """ Return the next probable date field. """

        return first(fields(template.content, with_name_like='date'))
Example #10
0
    def next_image_field() -> TemplateField:
        """ Return the next probable image field. """

        return first(fields(template.content, with_name_like=supported_images_pattern))
Example #11
0
    def next_include_field():
        """ Return the next probable include/inline field. """

        return first(fields(template.content, with_name_like='include|inline'))
Example #12
0
    def next_date_field():
        """ Return the next probable date field. """

        return first(fields(template.content, with_name_like='date'))
Example #13
0
 def __iter__(self):
     return fields(self.content)
Example #14
0
    def next_image_field() -> TemplateField:
        """ Return the next probable image field. """

        return first(
            fields(template.content, with_name_like=supported_images_pattern))