html_compose.base_types

 1import typing
 2from functools import lru_cache
 3from typing import Callable, Iterable, Mapping
 4
 5from . import util_funcs
 6
 7
 8# Note: base_element actually does runtime checking via hasattr
 9# This is for the type checker.
10@typing.runtime_checkable
11class _HasHtml(typing.Protocol):
12    def __html__(self) -> str:
13        """
14        Return unsafe HTML string
15        """
16        ...
17
18
19class ElementBase:
20    """
21    Base class for all HTML elements
22
23    Defined here to avoid circular imports for Node definition
24
25    Implementers define: __init__, render, __html__
26
27    See: BaseElement
28    """
29
30    FLOAT_PRECISION = 3  # Used when marshalling child floats into strings
31    ATTR_CACHE_SIZE = (
32        250  # Number of translated attributes to cache strings for
33    )
34
35    def __init__(self):
36        raise NotImplementedError
37
38    def get_attr_join(self) -> Callable[[str, str], str]:
39        """
40        Return join_attrs(key: str, value_trusted: str) function with lru cache
41        The returned function turns key, value into key="value"
42        """
43        cls = self.__class__
44
45        if hasattr(cls, "_join_lru_maxsize"):
46            live_size = cls._join_lru_maxsize  # pyright: ignore[reportAttributeAccessIssue]
47        else:
48            cls._join_lru_maxsize = cls.ATTR_CACHE_SIZE  # type: ignore[attr-defined]
49            live_size = None
50
51        if live_size != cls.ATTR_CACHE_SIZE:
52            cls.join_attrs = lru_cache(maxsize=cls.ATTR_CACHE_SIZE)(  # type: ignore[attr-defined]
53                util_funcs.join_attrs
54            )
55
56        return cls.join_attrs  # type: ignore[attr-defined]
57
58    def render(self, parent=None) -> str:
59        return "".join(self.resolve(parent))
60
61    def resolve(self, parent=None) -> Iterable[str]:
62        """
63        Yield all html as a generator of strings
64        """
65        raise NotImplementedError()
66
67    def __html__(self) -> str:
68        return self.render()
69
70
71# A node resolver is a callable that returns a Node,
72# possibly taking the calling element and parent element as arguments.
73NodeResolver = (
74    Callable[[], "Node"]
75    | Callable[[ElementBase], "Node"]
76    | Callable[[ElementBase, ElementBase], "Node"]
77)
78
79# The Node type is a union of all possible types that can be rendered
80Node = (
81    None  # None will not be appended to the output children
82    | str  # Text that needs to be escaped
83    | int  # Integer that needs to be converted to str
84    | float  # Float that needs to be converted to str
85    | bool  # Boolean that needs to be converted to str - usually a mistake
86    | ElementBase  # Base class for all HTML elements
87    | _HasHtml  # Returns HTML that does not need escaping
88    | Iterable["Node"]
89    | NodeResolver
90)
91
92# These types are used for attribute values
93StrLike = str | float | int | bool
94Resolvable = (
95    None
96    | StrLike
97    | Iterable[StrLike]
98    | Mapping[StrLike, bool]  # key-value pairs which resolve if .value is True
99)
class ElementBase:
20class ElementBase:
21    """
22    Base class for all HTML elements
23
24    Defined here to avoid circular imports for Node definition
25
26    Implementers define: __init__, render, __html__
27
28    See: BaseElement
29    """
30
31    FLOAT_PRECISION = 3  # Used when marshalling child floats into strings
32    ATTR_CACHE_SIZE = (
33        250  # Number of translated attributes to cache strings for
34    )
35
36    def __init__(self):
37        raise NotImplementedError
38
39    def get_attr_join(self) -> Callable[[str, str], str]:
40        """
41        Return join_attrs(key: str, value_trusted: str) function with lru cache
42        The returned function turns key, value into key="value"
43        """
44        cls = self.__class__
45
46        if hasattr(cls, "_join_lru_maxsize"):
47            live_size = cls._join_lru_maxsize  # pyright: ignore[reportAttributeAccessIssue]
48        else:
49            cls._join_lru_maxsize = cls.ATTR_CACHE_SIZE  # type: ignore[attr-defined]
50            live_size = None
51
52        if live_size != cls.ATTR_CACHE_SIZE:
53            cls.join_attrs = lru_cache(maxsize=cls.ATTR_CACHE_SIZE)(  # type: ignore[attr-defined]
54                util_funcs.join_attrs
55            )
56
57        return cls.join_attrs  # type: ignore[attr-defined]
58
59    def render(self, parent=None) -> str:
60        return "".join(self.resolve(parent))
61
62    def resolve(self, parent=None) -> Iterable[str]:
63        """
64        Yield all html as a generator of strings
65        """
66        raise NotImplementedError()
67
68    def __html__(self) -> str:
69        return self.render()

Base class for all HTML elements

Defined here to avoid circular imports for Node definition

Implementers define: __init__, render, __html__

See: BaseElement

FLOAT_PRECISION = 3
ATTR_CACHE_SIZE = 250
def get_attr_join(self) -> Callable[[str, str], str]:
39    def get_attr_join(self) -> Callable[[str, str], str]:
40        """
41        Return join_attrs(key: str, value_trusted: str) function with lru cache
42        The returned function turns key, value into key="value"
43        """
44        cls = self.__class__
45
46        if hasattr(cls, "_join_lru_maxsize"):
47            live_size = cls._join_lru_maxsize  # pyright: ignore[reportAttributeAccessIssue]
48        else:
49            cls._join_lru_maxsize = cls.ATTR_CACHE_SIZE  # type: ignore[attr-defined]
50            live_size = None
51
52        if live_size != cls.ATTR_CACHE_SIZE:
53            cls.join_attrs = lru_cache(maxsize=cls.ATTR_CACHE_SIZE)(  # type: ignore[attr-defined]
54                util_funcs.join_attrs
55            )
56
57        return cls.join_attrs  # type: ignore[attr-defined]

Return join_attrs(key: str, value_trusted: str) function with lru cache The returned function turns key, value into key="value"

def render(self, parent=None) -> str:
59    def render(self, parent=None) -> str:
60        return "".join(self.resolve(parent))
def resolve(self, parent=None) -> Iterable[str]:
62    def resolve(self, parent=None) -> Iterable[str]:
63        """
64        Yield all html as a generator of strings
65        """
66        raise NotImplementedError()

Yield all html as a generator of strings

NodeResolver = typing.Union[typing.Callable[[], ForwardRef('Node')], typing.Callable[[ElementBase], ForwardRef('Node')], typing.Callable[[ElementBase, ElementBase], ForwardRef('Node')]]
Node = typing.Union[NoneType, str, int, float, bool, ElementBase, html_compose.base_types._HasHtml, typing.Iterable[ForwardRef('Node')], typing.Callable[[], ForwardRef('Node')], typing.Callable[[ElementBase], ForwardRef('Node')], typing.Callable[[ElementBase, ElementBase], ForwardRef('Node')]]
StrLike = str | float | int | bool
Resolvable = typing.Union[NoneType, str, float, int, bool, typing.Iterable[str | float | int | bool], typing.Mapping[str | float | int | bool, bool]]