html_compose.base_attribute

  1from typing import Iterable, Tuple
  2
  3from markupsafe import Markup
  4
  5from .base_types import Resolvable
  6
  7
  8def unsafe_text(value) -> str:
  9    return Markup(str(value))
 10
 11
 12class BaseAttribute:
 13    """
 14    Base class for all HTML element attributes. It resolves to a string.
 15
 16    Attribute Reference: https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes
 17    """
 18
 19    __slots__ = ("name", "data", "delimiter")
 20
 21    def __init__(
 22        self, name: str, data: Resolvable = None, delimiter: str = " "
 23    ):
 24        self.name = name
 25        self.data = data
 26        self.delimiter = delimiter
 27
 28    def resolve_join(self, input_data: Iterable):
 29        """
 30        Join a list of strings
 31        Split out for implementors to override
 32        """
 33        return self.delimiter.join(
 34            x if isinstance(x, str) else str(x) for x in input_data
 35        )
 36
 37    def list_string_generator(self, data):
 38        """
 39        Resolve list into string list (generator)
 40        """
 41        for data_provider in data:
 42            # You're gonna be a string then
 43            if not isinstance(data_provider, str):
 44                raise ValueError(
 45                    f"Input data must be a string, got {type(data_provider)}"
 46                )
 47            yield data_provider
 48
 49    def dict_string_generator(self, data):
 50        """
 51        Resolve dictionary into string list (generator)
 52
 53        Keys are returned if value is truthy
 54        The value only determines if the key should be included
 55        """
 56        for key, value in data.items():
 57            if not value:
 58                continue
 59            yield key
 60
 61    def dict_style_string_generator(self, data):
 62        """
 63        Resolve dictionary key, value into css statement pairs
 64
 65
 66        The implementation is the simplest `<key>: <value>.
 67        User is therefore responsible for quoting.
 68        """
 69        for key, value in data.items():
 70            yield f"{key}: {value}"
 71
 72    def resolve_data(self) -> str | None:
 73        """
 74        Resolve right half of attribute into a string
 75
 76        A resolved string is returned from the input or resolved list/dict
 77        """
 78
 79        data = self.data
 80
 81        if data is True:
 82            return "true"
 83
 84        # Just a string
 85        if isinstance(data, str):
 86            return data
 87
 88        if isinstance(data, int):
 89            return str(data)
 90
 91        if data is None:
 92            return None
 93
 94        # a list which can be resolved via join
 95        _resolved = None
 96
 97        # List of strings
 98        if isinstance(data, list):
 99            _resolved = self.list_string_generator(data)
100        # dictionary of key value pairs
101        elif isinstance(data, dict):
102            if self.name == "style":
103                # Magic: Style attribute with key-value pairs procudes
104                # basic css statements.
105                _resolved = self.dict_style_string_generator(data)
106            else:
107                _resolved = self.dict_string_generator(data)
108        else:
109            raise ValueError(f"Input data type {data} not supported")
110
111        return self.resolve_join(_resolved)
112
113    def evaluate(self) -> Tuple[str, str] | None:
114        """
115        Evaluate attribute, return key, value as tuple
116        or None if attribute is falsey
117        """
118        if self.data is None or self.data is False:
119            return None
120
121        resolved = self.resolve_data()
122        if resolved is None:
123            return None
124
125        return (self.name, resolved)
126
127    def __repr__(self):
128        if self.delimiter != " ":
129            return f"BaseAttribute{{name={repr(self.name)}, data={repr(self.data)}, delimiter={repr(self.data)}}}"
130
131        return (
132            f"BaseAttribute{{name={repr(self.name)}, data={repr(self.data)}}}"
133        )
def unsafe_text(value) -> str:
 9def unsafe_text(value) -> str:
10    return Markup(str(value))
class BaseAttribute:
 13class BaseAttribute:
 14    """
 15    Base class for all HTML element attributes. It resolves to a string.
 16
 17    Attribute Reference: https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes
 18    """
 19
 20    __slots__ = ("name", "data", "delimiter")
 21
 22    def __init__(
 23        self, name: str, data: Resolvable = None, delimiter: str = " "
 24    ):
 25        self.name = name
 26        self.data = data
 27        self.delimiter = delimiter
 28
 29    def resolve_join(self, input_data: Iterable):
 30        """
 31        Join a list of strings
 32        Split out for implementors to override
 33        """
 34        return self.delimiter.join(
 35            x if isinstance(x, str) else str(x) for x in input_data
 36        )
 37
 38    def list_string_generator(self, data):
 39        """
 40        Resolve list into string list (generator)
 41        """
 42        for data_provider in data:
 43            # You're gonna be a string then
 44            if not isinstance(data_provider, str):
 45                raise ValueError(
 46                    f"Input data must be a string, got {type(data_provider)}"
 47                )
 48            yield data_provider
 49
 50    def dict_string_generator(self, data):
 51        """
 52        Resolve dictionary into string list (generator)
 53
 54        Keys are returned if value is truthy
 55        The value only determines if the key should be included
 56        """
 57        for key, value in data.items():
 58            if not value:
 59                continue
 60            yield key
 61
 62    def dict_style_string_generator(self, data):
 63        """
 64        Resolve dictionary key, value into css statement pairs
 65
 66
 67        The implementation is the simplest `<key>: <value>.
 68        User is therefore responsible for quoting.
 69        """
 70        for key, value in data.items():
 71            yield f"{key}: {value}"
 72
 73    def resolve_data(self) -> str | None:
 74        """
 75        Resolve right half of attribute into a string
 76
 77        A resolved string is returned from the input or resolved list/dict
 78        """
 79
 80        data = self.data
 81
 82        if data is True:
 83            return "true"
 84
 85        # Just a string
 86        if isinstance(data, str):
 87            return data
 88
 89        if isinstance(data, int):
 90            return str(data)
 91
 92        if data is None:
 93            return None
 94
 95        # a list which can be resolved via join
 96        _resolved = None
 97
 98        # List of strings
 99        if isinstance(data, list):
100            _resolved = self.list_string_generator(data)
101        # dictionary of key value pairs
102        elif isinstance(data, dict):
103            if self.name == "style":
104                # Magic: Style attribute with key-value pairs procudes
105                # basic css statements.
106                _resolved = self.dict_style_string_generator(data)
107            else:
108                _resolved = self.dict_string_generator(data)
109        else:
110            raise ValueError(f"Input data type {data} not supported")
111
112        return self.resolve_join(_resolved)
113
114    def evaluate(self) -> Tuple[str, str] | None:
115        """
116        Evaluate attribute, return key, value as tuple
117        or None if attribute is falsey
118        """
119        if self.data is None or self.data is False:
120            return None
121
122        resolved = self.resolve_data()
123        if resolved is None:
124            return None
125
126        return (self.name, resolved)
127
128    def __repr__(self):
129        if self.delimiter != " ":
130            return f"BaseAttribute{{name={repr(self.name)}, data={repr(self.data)}, delimiter={repr(self.data)}}}"
131
132        return (
133            f"BaseAttribute{{name={repr(self.name)}, data={repr(self.data)}}}"
134        )

Base class for all HTML element attributes. It resolves to a string.

Attribute Reference: https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes

BaseAttribute( name: str, data: Union[NoneType, str, float, int, bool, Iterable[str | float | int | bool], Mapping[str | float | int | bool, bool]] = None, delimiter: str = ' ')
22    def __init__(
23        self, name: str, data: Resolvable = None, delimiter: str = " "
24    ):
25        self.name = name
26        self.data = data
27        self.delimiter = delimiter
name
data
delimiter
def resolve_join(self, input_data: Iterable):
29    def resolve_join(self, input_data: Iterable):
30        """
31        Join a list of strings
32        Split out for implementors to override
33        """
34        return self.delimiter.join(
35            x if isinstance(x, str) else str(x) for x in input_data
36        )

Join a list of strings Split out for implementors to override

def list_string_generator(self, data):
38    def list_string_generator(self, data):
39        """
40        Resolve list into string list (generator)
41        """
42        for data_provider in data:
43            # You're gonna be a string then
44            if not isinstance(data_provider, str):
45                raise ValueError(
46                    f"Input data must be a string, got {type(data_provider)}"
47                )
48            yield data_provider

Resolve list into string list (generator)

def dict_string_generator(self, data):
50    def dict_string_generator(self, data):
51        """
52        Resolve dictionary into string list (generator)
53
54        Keys are returned if value is truthy
55        The value only determines if the key should be included
56        """
57        for key, value in data.items():
58            if not value:
59                continue
60            yield key

Resolve dictionary into string list (generator)

Keys are returned if value is truthy The value only determines if the key should be included

def dict_style_string_generator(self, data):
62    def dict_style_string_generator(self, data):
63        """
64        Resolve dictionary key, value into css statement pairs
65
66
67        The implementation is the simplest `<key>: <value>.
68        User is therefore responsible for quoting.
69        """
70        for key, value in data.items():
71            yield f"{key}: {value}"

Resolve dictionary key, value into css statement pairs

The implementation is the simplest `: . User is therefore responsible for quoting.

def resolve_data(self) -> str | None:
 73    def resolve_data(self) -> str | None:
 74        """
 75        Resolve right half of attribute into a string
 76
 77        A resolved string is returned from the input or resolved list/dict
 78        """
 79
 80        data = self.data
 81
 82        if data is True:
 83            return "true"
 84
 85        # Just a string
 86        if isinstance(data, str):
 87            return data
 88
 89        if isinstance(data, int):
 90            return str(data)
 91
 92        if data is None:
 93            return None
 94
 95        # a list which can be resolved via join
 96        _resolved = None
 97
 98        # List of strings
 99        if isinstance(data, list):
100            _resolved = self.list_string_generator(data)
101        # dictionary of key value pairs
102        elif isinstance(data, dict):
103            if self.name == "style":
104                # Magic: Style attribute with key-value pairs procudes
105                # basic css statements.
106                _resolved = self.dict_style_string_generator(data)
107            else:
108                _resolved = self.dict_string_generator(data)
109        else:
110            raise ValueError(f"Input data type {data} not supported")
111
112        return self.resolve_join(_resolved)

Resolve right half of attribute into a string

A resolved string is returned from the input or resolved list/dict

def evaluate(self) -> Optional[Tuple[str, str]]:
114    def evaluate(self) -> Tuple[str, str] | None:
115        """
116        Evaluate attribute, return key, value as tuple
117        or None if attribute is falsey
118        """
119        if self.data is None or self.data is False:
120            return None
121
122        resolved = self.resolve_data()
123        if resolved is None:
124            return None
125
126        return (self.name, resolved)

Evaluate attribute, return key, value as tuple or None if attribute is falsey