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

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

18 statements  

1""" 

2turbohtml.transform: XSLT 1.0 transformation, the job ``lxml.etree.XSLT`` does. 

3 

4Parse an XSLT stylesheet as XML with :func:`turbohtml.parse_xml`; use any parsed tree as the source document. 

5:class:`Transform` compiles the stylesheet and applies it to a source, mirroring lxml's compile-once, apply-many shape:: 

6 

7 from turbohtml import parse_xml 

8 from turbohtml.transform import Transform 

9 

10 style = parse_xml(stylesheet_source) 

11 convert = Transform(style) 

12 result = convert(parse_xml(document_source)) 

13 

14The whole transform runs in the C extension, reusing turbohtml's XPath 1.0 engine for every match pattern and select 

15expression. It covers XSLT 1.0: ``xsl:template`` (match, name, mode, priority), ``xsl:apply-templates``, 

16``xsl:call-template``, ``xsl:for-each``, ``xsl:if``, ``xsl:choose``, ``xsl:value-of``, ``xsl:copy``/``xsl:copy-of``, 

17``xsl:element``/``xsl:attribute``/``xsl:text``, ``xsl:variable``/``xsl:param``, ``xsl:sort``, multi-level 

18``xsl:number``, ``xsl:key`` and the ``key()`` function, ``xsl:strip-space``/``xsl:preserve-space``, 

19``xsl:attribute-set``, ``xsl:namespace-alias``, ``xsl:import`` with import precedence, ``xsl:fallback``, simplified 

20stylesheets, ``cdata-section-elements`` and the ``xml``/``html``/``text`` output methods (html is auto-selected for a 

21null-namespace ``html`` document element). The documented boundaries are locale-aware ``xsl:sort`` collation (a locale 

22layer turbohtml does not carry) and ``id()`` over DTD-declared IDs (no DTD layer). 

23 

24``base_url`` supplies the stylesheet path or file URL used to resolve each ``xsl:import``. Disable imports for an 

25untrusted stylesheet, or constrain them to ``import_root``. The libxslt XSLT 1.0 Recommendation test corpus covers the 

26processor (see ``tests/conformance/test_xslt_conformance.py``). 

27""" 

28 

29from __future__ import annotations 

30 

31from typing import TYPE_CHECKING 

32 

33from ._html import _xpath_literal, _xslt_compile, _xslt_resolve_imports, _xslt_transform 

34 

35if TYPE_CHECKING: 

36 from pathlib import Path 

37 

38 from ._html import Node 

39 

40__all__ = ["Transform", "strparam", "transform"] 

41 

42 

43class Transform: 

44 """ 

45 A compiled XSLT 1.0 stylesheet, callable over source documents (lxml's ``etree.XSLT``). 

46 

47 :param stylesheet: the stylesheet, a tree parsed with :func:`turbohtml.parse_xml`. 

48 :param base_url: the stylesheet's path or file URL, against which ``xsl:import`` hrefs resolve; required only when 

49 the stylesheet imports. 

50 :param allow_imports: set to :data:`False` when a stylesheet must not read other files. 

51 :param import_root: when set, imported files must resolve inside this directory, including through nested imports. 

52 """ 

53 

54 __slots__ = ("_compiled",) 

55 

56 def __init__( 

57 self, 

58 stylesheet: Node, 

59 *, 

60 base_url: str | None = None, 

61 allow_imports: bool = True, 

62 import_root: str | Path | None = None, 

63 ) -> None: 

64 """Compile an immutable stylesheet snapshot.""" 

65 self._compiled = _xslt_compile( 

66 stylesheet, 

67 _xslt_resolve_imports(stylesheet, base_url, allow_imports, import_root), 

68 ) 

69 

70 def __call__(self, source: Node, /, **params: str) -> str: 

71 """ 

72 Transform a source document and return the serialized result. 

73 

74 :param source: the document to transform, a parsed tree. 

75 :param params: top-level ``xsl:param`` values, each an XPath expression string; wrap a plain string in 

76 :func:`strparam` (``convert(doc, title=strparam(title))``) so its quotes cannot end the literal. 

77 :raises ValueError: if the stylesheet or an expression is malformed, a referenced key or named template is 

78 undeclared, or the html output method would write a processing instruction that holds ``>``. 

79 :raises RuntimeError: on an ``xsl:message`` with ``terminate="yes"``. 

80 :returns: the transformed document serialized under the stylesheet's ``xsl:output`` method. 

81 """ 

82 return _xslt_transform(self._compiled, source, params or None) 

83 

84 

85def transform( 

86 stylesheet: Node, 

87 source: Node, 

88 /, 

89 *, 

90 base_url: str | None = None, 

91 allow_imports: bool = True, 

92 import_root: str | Path | None = None, 

93 **params: str, 

94) -> str: 

95 """ 

96 Apply an XSLT 1.0 stylesheet to a source document in one call. 

97 

98 Equivalent to constructing and calling :class:`Transform`; retain a :class:`Transform` when one stylesheet applies 

99 to many documents so it is compiled once. 

100 

101 :param stylesheet: the stylesheet, a tree parsed with :func:`turbohtml.parse_xml`. 

102 :param source: the document to transform, a parsed tree. 

103 :param base_url: the stylesheet's path or file URL, against which ``xsl:import`` hrefs resolve; required only when 

104 the stylesheet imports. 

105 :param allow_imports: set to :data:`False` when a stylesheet must not read other files. 

106 :param import_root: when set, imported files must resolve inside this directory, including through nested imports. 

107 :param params: top-level ``xsl:param`` values, each an XPath expression string; pass text through 

108 :func:`strparam`. 

109 :returns: the transformed document serialized under the stylesheet's ``xsl:output`` method. 

110 """ 

111 return Transform( 

112 stylesheet, 

113 base_url=base_url, 

114 allow_imports=allow_imports, 

115 import_root=import_root, 

116 )(source, **params) 

117 

118 

119def strparam(value: str, /) -> str: 

120 """ 

121 Quote a string as an XPath string literal, for passing text as an ``xsl:param`` value. 

122 

123 A parameter value is an XPath expression, so an unquoted or hand-quoted string that holds a quote runs as a query 

124 over the source document. The literal this returns reads back as ``value`` whatever quotes it holds. 

125 

126 :param value: the text the stylesheet should receive. 

127 :raises TypeError: if ``value`` is not a str. 

128 :returns: an XPath expression that evaluates to ``value``. 

129 """ 

130 return _xpath_literal(value)