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

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

35 statements  

1""" 

2HTML5 authoring-conformance checking with a severity model. 

3 

4:func:`check` runs the WHATWG document-conformance requirements the parser does not raise 

5as a :class:`~turbohtml.ParseError` -- a missing ``img`` alt, obsolete presentational 

6markup, a duplicate ``id``, an invalid or redundant ARIA role, an empty heading, a 

7``section`` without a heading, a document with no title or language -- over a tree parsed 

8with :func:`turbohtml.parse`. It returns a :class:`ConformanceReport`: a ``valid`` verdict 

9(the document is valid exactly when nothing is an error) and every :class:`ConformanceMessage` 

10found. Each message carries a stable ``code``, a ``severity`` (``"error"``, ``"warning"``, or 

11``"info"``), a human-readable ``message``, and the source ``line``/``column``, the way the Nu 

12Html Checker (validator.nu) classifies its findings by type and subType. 

13 

14The walk, the verdict and the severity views run in the C core; the Python layer only 

15wraps the C findings into the report records. :func:`check_html` is the shorthand that parses a markup 

16string first. 

17""" 

18 

19from __future__ import annotations 

20 

21from itertools import starmap 

22from typing import TYPE_CHECKING, Literal, NamedTuple 

23 

24from ._html import _conformance_check, _conformance_filter, parse 

25 

26if TYPE_CHECKING: 

27 from ._html import Document, Node 

28 

29__all__ = [ 

30 "ConformanceMessage", 

31 "ConformanceReport", 

32 "Severity", 

33 "check", 

34 "check_html", 

35] 

36 

37Severity = Literal["error", "warning", "info"] 

38"""The three severities a :class:`ConformanceMessage` carries, ordered most to least serious.""" 

39 

40 

41class ConformanceMessage(NamedTuple): 

42 """ 

43 One conformance finding. 

44 

45 :param code: a stable machine-readable identifier for the rule, such as ``"img-missing-alt"``. 

46 :param severity: ``"error"`` for a violated requirement, ``"warning"`` for an authoring 

47 recommendation, ``"info"`` for an advisory note. 

48 :param message: a human-readable description of what was found. 

49 :param line: the 1-based source line of the offending node, or ``0`` when unknown. 

50 :param column: the 0-based source column of the offending node, or ``0`` when unknown. 

51 """ 

52 

53 code: str 

54 severity: Severity 

55 message: str 

56 line: int 

57 column: int 

58 

59 

60class ConformanceReport(NamedTuple): 

61 """ 

62 The outcome of a conformance check: a ``valid`` verdict and every message found. 

63 

64 The report is truthy exactly when the document is valid (no message is an error), so 

65 ``if check(doc):`` reads naturally. Warnings and info messages never affect the verdict. 

66 """ 

67 

68 valid: bool 

69 messages: tuple[ConformanceMessage, ...] 

70 

71 def __bool__(self) -> bool: 

72 """Whether the document is valid, so the report reads as a truth value.""" 

73 return self.valid 

74 

75 @property 

76 def errors(self) -> tuple[ConformanceMessage, ...]: 

77 """Only the ``"error"`` messages -- the findings that make the document invalid.""" 

78 return _conformance_filter(self.messages, "error") 

79 

80 @property 

81 def warnings(self) -> tuple[ConformanceMessage, ...]: 

82 """Only the ``"warning"`` messages -- the authoring recommendations.""" 

83 return _conformance_filter(self.messages, "warning") 

84 

85 @property 

86 def infos(self) -> tuple[ConformanceMessage, ...]: 

87 """Only the ``"info"`` messages -- the advisory notes.""" 

88 return _conformance_filter(self.messages, "info") 

89 

90 

91def check(document: Document | Node) -> ConformanceReport: 

92 """ 

93 Check a parsed document (or any subtree) against the HTML5 authoring rules. 

94 

95 The document-level rules (a missing title or ``lang``) apply only when ``document`` is a 

96 whole :class:`~turbohtml.Document`; on a fragment or a subtree only the per-element rules run. 

97 

98 :param document: a tree parsed with :func:`turbohtml.parse` (or an element within one). 

99 :returns: the :class:`ConformanceReport` with the verdict and every message. 

100 """ 

101 valid, findings = _conformance_check(document) 

102 return ConformanceReport(valid, tuple(starmap(ConformanceMessage, findings))) 

103 

104 

105def check_html(markup: str) -> ConformanceReport: 

106 """ 

107 Parse a markup string with :func:`turbohtml.parse` and check the resulting document. 

108 

109 :param markup: the HTML to parse and check. 

110 :returns: the :class:`ConformanceReport` for the parsed document. 

111 """ 

112 return check(parse(markup))