Source code for src.xmlChecks.spanXmlCheck

# SPDX-FileCopyrightText: Copyright © 2026 BBC
#
# SPDX-License-Identifier: BSD-3-Clause

from src.validationLogging.validationCodes import ValidationCode
from src.validationLogging.validationLogger import ValidationLogger
from xml.etree.ElementTree import Element
from src.xmlUtils import xmlIdAttr, make_qname
from .xmlCheck import XmlCheck
from .ttmlUtils import ns_ttml
from .timingAttributeCheck import getTimingAttributes, \
    getParentTimingAttributes, pushParentTimingAttributes, \
    popParentTimingAttributes


[docs] class spanCheck(XmlCheck): """ Checks the span element """ _subChecks = [] def __init__(self, sub_checks: list[XmlCheck] = [], require_text_in_span: bool = True, permit_nested_spans: bool = False, ): super().__init__() self._subChecks = sub_checks self._require_text_in_span = require_text_in_span self._permit_nested_spans = permit_nested_spans
[docs] def run(self, input: Element, context: dict, validation_results: ValidationLogger) -> bool: tt_ns = \ context.get('root_ns', ns_ttml) valid = True p_el_tag = make_qname(tt_ns, 'p') span_el_tag = make_qname(tt_ns, 'span') spans = [el for el in input if el.tag == span_el_tag] if self._require_text_in_span and \ len(spans) == 0 and input.tag == p_el_tag: valid = False validation_results.error( location='{}/{} xml:id {}'.format( input.tag, span_el_tag, input.get(xmlIdAttr, 'omitted'), ), message='Found 0 span elements; ' 'text content needs to be in a styled span', code=ValidationCode.bbc_text_span_constraint ) if not self._permit_nested_spans and \ len(spans) > 0 and input.tag == span_el_tag: valid = False validation_results.error( location='{}/{} xml:id {}'.format( input.tag, span_el_tag, input.get(xmlIdAttr, 'omitted'), ), message='Found {} span element children of span, require 0' .format(len(spans)), code=ValidationCode.ebuttd_nested_span_constraint ) timing_attributes = getTimingAttributes(el=input) parent_timing_attributes = getParentTimingAttributes(context=context) pushParentTimingAttributes( timing_attributes=parent_timing_attributes.union( timing_attributes), context=context, ) for span in spans: for subCheck in self._subChecks: valid &= subCheck.run( input=span, context=context, validation_results=validation_results ) valid &= self.run( input=span, context=context, validation_results=validation_results ) popParentTimingAttributes(context=context) return valid