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

49 statements  

1"""Keep generated source contexts separate from qualified export ownership.""" 

2 

3from __future__ import annotations 

4 

5import codecs 

6from importlib import import_module 

7from typing import TYPE_CHECKING, Final 

8 

9import html5lib 

10 

11from turbohtml import Node, parse 

12 

13from .atheris_registry import Target 

14from .markdown_structure_generators import MarkdownProfileError, markdown_html_check, markdown_source_check 

15 

16if TYPE_CHECKING: 

17 from collections.abc import Callable, Iterator 

18 from xml.etree.ElementTree import Element 

19 

20 

21# Standalone adapters cannot depend on registry import order for codec registration. 

22import_module("turbohtml.detect") 

23 

24 

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}") 

30 

31 def source(data: bytes) -> None: 

32 if error := markdown_source_check(data.decode("utf-8"), render): 

33 raise AssertionError(error) 

34 

35 def markup(data: bytes) -> None: 

36 if error := markdown_html_check(data.decode("utf-8"), render): 

37 raise AssertionError(error) 

38 

39 def encoded(data: bytes) -> None: 

40 encoding_observation(data, encoding, sniff=sniff) 

41 

42 return ( 

43 Target("markdown-source", source, (), (UnicodeDecodeError, MarkdownProfileError)), 

44 Target("markdown-html", markup, (), (UnicodeDecodeError, MarkdownProfileError)), 

45 Target("encoding-bytes", encoded, ()), 

46 ) 

47 

48 

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 

65 

66 

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) 

84 

85 

86__all__ = ["encoding_observation", "generation_targets"]