Coverage for /pythoncovmergedfiles/medio/medio/src/turbohtml/tools/fuzz/generation_targets.py: 86%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""Keep generated source contexts separate from qualified export ownership."""
3from __future__ import annotations
5import codecs
6from importlib import import_module
7from typing import TYPE_CHECKING, Final
9import html5lib
11from turbohtml import Node, parse
13from .atheris_registry import Target
14from .markdown_structure_generators import MarkdownProfileError, markdown_html_check, markdown_source_check
16if TYPE_CHECKING:
17 from collections.abc import Callable, Iterator
18 from xml.etree.ElementTree import Element
21# Standalone adapters cannot depend on registry import order for codec registration.
22import_module("turbohtml.detect")
25def generation_targets(
26 encoding: str = "UTF-8", *, sniff: bool = False, render: Callable[[Node], str] = Node.to_markdown
27) -> tuple[Target, ...]:
28 """Fix execution contexts without assigning a second qualified export owner."""
29 codecs.lookup(f"whatwg-{encoding}")
31 def source(data: bytes) -> None:
32 if error := markdown_source_check(data.decode("utf-8"), render):
33 raise AssertionError(error)
35 def markup(data: bytes) -> None:
36 if error := markdown_html_check(data.decode("utf-8"), render):
37 raise AssertionError(error)
39 def encoded(data: bytes) -> None:
40 encoding_observation(data, encoding, sniff=sniff)
42 return (
43 Target("markdown-source", source, (), (UnicodeDecodeError, MarkdownProfileError)),
44 Target("markdown-html", markup, (), (UnicodeDecodeError, MarkdownProfileError)),
45 Target("encoding-bytes", encoded, ()),
46 )
49def encoding_observation(
50 data: bytes,
51 encoding: str = "UTF-8",
52 *,
53 sniff: bool = False,
54 decode: Callable[[bytes, str], str] = bytes.decode,
55) -> str:
56 """Compare decoded text with an independent HTML parser before admitting bytes."""
57 document: Final = parse(data, detect_encoding=True) if sniff else parse(data, encoding=encoding)
58 # BOMs override explicit codecs; late declarations can restart sniffed parsing.
59 decoded: Final = decode(data, f"whatwg-{document.encoding}").removeprefix("\ufeff")
60 expected: Final = _reference_text(html5lib.parse(decoded, namespaceHTMLElements=False))
61 if (document.text, parse(decoded).text) != (expected, expected):
62 msg = f"Encoded parser text differs: {document.text!r} != {expected!r}"
63 raise AssertionError(msg)
64 return document.text
67def _reference_text(element: Element) -> str:
68 parts: list[str] = [element.text or ""]
69 stack: list[tuple[Element, Iterator[Element]]] = [(element, iter(element))]
70 while stack:
71 try:
72 child = next(stack[-1][1])
73 except StopIteration:
74 finished, _ = stack.pop()
75 if stack:
76 parts.append(finished.tail or "")
77 continue
78 if isinstance(child.tag, str):
79 parts.append(child.text or "")
80 stack.append((child, iter(child)))
81 else:
82 parts.append(child.tail or "")
83 return "".join(parts)
86__all__ = ["encoding_observation", "generation_targets"]