Validate against an XML schema

Parse the XML with turbohtml.parse_xml(), compile the schema once, then validate. XMLSchema takes XSD 1.0 text (or a parsed schema document) and RelaxNG takes RELAX NG XML syntax; both expose the same validate(), is_valid(), and assert_valid() surface.

Get every error

validate() returns a ValidationResult. It is truthy when the document is valid, and its errors tuple holds one ValidationError per violation – each with a message, the /root/child document path that located it, a line (0 when the source carries no positions), and a coarse type of "structure", "datatype", or "facet":

from turbohtml import parse_xml
from turbohtml.validate import XMLSchema

schema = XMLSchema(
    '<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">'
    '<xs:element name="book"><xs:complexType>'
    '<xs:sequence><xs:element name="title" type="xs:string"/></xs:sequence>'
    '<xs:attribute name="isbn" type="xs:string" use="required"/>'
    "</xs:complexType></xs:element></xs:schema>"
)
result = schema.validate(parse_xml("<book><title>One</title></book>"))
print(result.valid)
for error in result.errors:
    print(error.type, error.path, "--", error.message)
False
structure /book -- required attribute 'isbn' is missing

Fail fast

When you only care whether the document conforms, is_valid() returns the bool, and assert_valid() raises SchemaValidationError (carrying the errors) on the first invalid document, so it drops into a pipeline that expects an exception:

from turbohtml import parse_xml
from turbohtml.validate import RelaxNG, SchemaValidationError

schema = RelaxNG(
    '<element name="note" xmlns="http://relaxng.org/ns/structure/1.0">'
    '<oneOrMore><element name="line"><text/></element></oneOrMore></element>'
)
print(schema.is_valid(parse_xml("<note><line>hi</line></note>")))
try:
    schema.assert_valid(parse_xml("<note/>"))
except SchemaValidationError as error:
    print("rejected:", error.errors[0].path)
True
rejected: /note