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
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
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
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
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
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
def next_include_field(): """ Return the next probable include/inline field. """ return first(fields(template.content, with_name_like='include|inline'))
def __iter__(self): return fields(self.content)
def next_date_field(): """ Return the next probable date field. """ return first(fields(template.content, with_name_like='date'))
def next_image_field() -> TemplateField: """ Return the next probable image field. """ return first(fields(template.content, with_name_like=supported_images_pattern))
def next_image_field() -> TemplateField: """ Return the next probable image field. """ return first( fields(template.content, with_name_like=supported_images_pattern))