Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/turbohtml/treebuild.py: 100%

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

17 statements  

1""" 

2turbohtml.treebuild: retarget the parser at your own tree. 

3 

4Where :func:`turbohtml.parse` builds a navigable document and :func:`turbohtml.saxparse.sax_parse` streams 

5events, :func:`parse_into` runs the same WHATWG tree builder and hands the constructed tree straight to a builder 

6object you supply -- one method per node kind, plus an ``append`` that links a child under its parent. The parser 

7never materializes a turbohtml :class:`~turbohtml.Node` and you never walk the result a second time: a single pass 

8over the spec-correct tree (implied ``<html>``/``<head>``/``<body>``, foster parenting, the adoption agency) drives 

9your builder, so an index, a diff tree, or an extraction struct is populated directly from the parse. 

10 

11This is the model Rust's html5ever exposes as its ``TreeSink`` trait and Node's parse5 as its ``TreeAdapter``: the 

12conformant parser is retargetable, the tree representation is yours. A builder returns an opaque *handle* for each 

13node it creates -- any object at all, since turbohtml only threads it back into a later ``append`` -- and the handle 

14your :meth:`TreeBuilder.create_document` returns is what :func:`parse_into` gives back. 

15 

16The methods mirror parse5's adapter: every element carries its namespace URI (``http://www.w3.org/1999/xhtml`` for 

17HTML, and the SVG and MathML URIs for foreign content) and its attributes as ``(name, value)`` pairs, a valueless 

18attribute pairing with None. A ``<template>``'s content is appended straight under the template handle. Processing 

19instructions arrive through :meth:`TreeBuilder.create_pi` with separate target and data strings. 

20""" 

21 

22from __future__ import annotations 

23 

24from typing import Protocol, TypeVar, cast 

25 

26from ._html import _parse_into 

27 

28__all__ = [ 

29 "TreeBuilder", 

30 "parse_into", 

31] 

32 

33H = TypeVar("H") 

34"""The builder's node handle: whatever :class:`TreeBuilder` returns for a node and takes back in ``append``.""" 

35 

36 

37class TreeBuilder(Protocol[H]): 

38 """ 

39 The tree-construction sink :func:`parse_into` drives, parse5's ``TreeAdapter`` in turbohtml shape. 

40 

41 Implement each method to create the matching node in your own representation and return a handle for it; the 

42 handle is opaque to turbohtml, threaded back only into :meth:`append`. The protocol is structural, so any object 

43 with these methods works -- no base class to inherit. 

44 """ 

45 

46 def create_document(self) -> H: 

47 """Create the document root and return its handle; :func:`parse_into` returns this handle.""" 

48 

49 def create_doctype(self, name: str, public_id: str | None, system_id: str | None) -> H: 

50 """Create a doctype node; ``public_id``/``system_id`` are None when the source omits them.""" 

51 

52 def create_element(self, name: str, namespace: str, attrs: tuple[tuple[str, str | None], ...]) -> H: 

53 """Create an element ``name`` in ``namespace`` (a URI) with ``attrs`` (each a ``(name, value)`` pair).""" 

54 

55 def create_text(self, data: str) -> H: 

56 """Create a text node holding ``data``.""" 

57 

58 def create_comment(self, data: str) -> H: 

59 """Create a comment node holding ``data``.""" 

60 

61 def create_pi(self, target: str, data: str) -> H: 

62 """Create a node for an HTML instruction whose target is not reserved for XML.""" 

63 

64 def append(self, parent: H, child: H) -> None: 

65 """Append the ``child`` handle under the ``parent`` handle, in document order.""" 

66 

67 

68def parse_into(html: str, builder: TreeBuilder[H]) -> H: 

69 """ 

70 Parse ``html`` and drive ``builder`` to construct the tree, returning its document handle. 

71 

72 The document is parsed with the full WHATWG tree-construction algorithm, then its nodes are handed to 

73 ``builder`` in document order: one ``create_*`` call per node followed by an :meth:`~TreeBuilder.append` linking 

74 it under its parent. No navigable :class:`~turbohtml.Node` is built and the tree is walked only once. 

75 

76 :param html: the markup to parse. 

77 :param builder: the :class:`TreeBuilder` (any object with its methods) that constructs your representation. 

78 :returns: the handle :meth:`~TreeBuilder.create_document` returned, now populated with the parsed tree. 

79 :raises TypeError: if ``html`` is not a str. 

80 :raises AttributeError: if ``builder`` is missing one of the :class:`TreeBuilder` methods. 

81 """ 

82 return cast("H", _parse_into(html, builder))