phml.validate.check

phml.utils.validate.test

Logic that allows nodes to be tested against a series of conditions.

 1"""phml.utils.validate.test
 2
 3Logic that allows nodes to be tested against a series of conditions.
 4"""
 5
 6from __future__ import annotations
 7
 8from typing import TYPE_CHECKING, Callable, Optional
 9
10from phml.nodes import Element
11
12if TYPE_CHECKING:
13    from phml.nodes import All_Nodes, Root
14
15Test = None | str | list | dict | Callable
16
17
18def check(
19    node: All_Nodes,
20    _test: Test,
21    index: Optional[int] = None,
22    parent: Optional[Root | Element] = None,
23    strict: bool = True,
24) -> bool:
25    """Test if a node passes the given test(s).
26
27    Test Types:
28        - `None`: Just checks that the node is a valid node.
29        - `str`: Checks that the test value is == the `node.type`.
30        - `dict`: Checks all items are valid attributes on the node.
31        and that the values are strictly equal.
32        - `Callable`: Passes the given function the node and it's index, if provided,
33        and checks if the callable returned true.
34        - `list[Test]`: Apply all the rules above for each Test in the list.
35
36    If the `parent` arg is passed so should the `index` arg.
37
38    Args:
39        node (All_Nodes): Node to test. Can be any phml node.
40        test (Test): Test to apply to the node. See previous section
41        for more info.
42        index (Optional[int], optional): Index in the parent where the
43        node exists. Defaults to None.
44        parent (Optional[Root | Element], optional): The nodes parent. Defaults to None.
45
46    Returns:
47        True if all tests pass.
48    """
49
50    if parent is not None:
51        # If parent is given then index has to be also.
52        #   Validate index is correct in parent.children
53        if (
54            index is None
55            or len(parent.children) == 0
56            or index >= len(parent.children)
57            or parent.children[index] != node
58        ):
59            return False
60
61    if isinstance(_test, str):
62        # If string then validate that the type is the same
63        return hasattr(node, "type") and node.type == _test
64
65    if isinstance(_test, dict):
66        # If dict validate all items with properties are the same
67        # Either in attributes or in
68        return bool(
69            isinstance(node, Element)
70            and all(
71                (hasattr(node, key) and value == getattr(node, key))
72                or (hasattr(node, "properties") and key in node.properties and value == node[key])
73                for key, value in _test.items()
74            )
75        )
76
77    if isinstance(_test, list):
78        # If list then recursively apply tests
79        if strict:
80            return bool(
81                all(isinstance(cond, Test) and check(node, cond, index, parent) for cond in _test)
82            )
83
84        return bool(
85            any(isinstance(cond, Test) and check(node, cond, index, parent) for cond in _test)
86        )
87
88    if isinstance(_test, Callable):
89        # If callable return result of collable after passing node, index, and parent
90        return _test(node, index, node.parent)
91
92    raise Exception("Invalid test condition")
def check( node: phml.nodes.root.Root | phml.nodes.element.Element | phml.nodes.text.Text | phml.nodes.comment.Comment | phml.nodes.doctype.DocType | phml.nodes.parent.Parent | phml.nodes.node.Node | phml.nodes.literal.Literal, _test: Union[NoneType, str, list, dict, Callable], index: Optional[int] = None, parent: Union[phml.nodes.element.Element, phml.nodes.root.Root, NoneType] = None, strict: bool = True) -> bool:
19def check(
20    node: All_Nodes,
21    _test: Test,
22    index: Optional[int] = None,
23    parent: Optional[Root | Element] = None,
24    strict: bool = True,
25) -> bool:
26    """Test if a node passes the given test(s).
27
28    Test Types:
29        - `None`: Just checks that the node is a valid node.
30        - `str`: Checks that the test value is == the `node.type`.
31        - `dict`: Checks all items are valid attributes on the node.
32        and that the values are strictly equal.
33        - `Callable`: Passes the given function the node and it's index, if provided,
34        and checks if the callable returned true.
35        - `list[Test]`: Apply all the rules above for each Test in the list.
36
37    If the `parent` arg is passed so should the `index` arg.
38
39    Args:
40        node (All_Nodes): Node to test. Can be any phml node.
41        test (Test): Test to apply to the node. See previous section
42        for more info.
43        index (Optional[int], optional): Index in the parent where the
44        node exists. Defaults to None.
45        parent (Optional[Root | Element], optional): The nodes parent. Defaults to None.
46
47    Returns:
48        True if all tests pass.
49    """
50
51    if parent is not None:
52        # If parent is given then index has to be also.
53        #   Validate index is correct in parent.children
54        if (
55            index is None
56            or len(parent.children) == 0
57            or index >= len(parent.children)
58            or parent.children[index] != node
59        ):
60            return False
61
62    if isinstance(_test, str):
63        # If string then validate that the type is the same
64        return hasattr(node, "type") and node.type == _test
65
66    if isinstance(_test, dict):
67        # If dict validate all items with properties are the same
68        # Either in attributes or in
69        return bool(
70            isinstance(node, Element)
71            and all(
72                (hasattr(node, key) and value == getattr(node, key))
73                or (hasattr(node, "properties") and key in node.properties and value == node[key])
74                for key, value in _test.items()
75            )
76        )
77
78    if isinstance(_test, list):
79        # If list then recursively apply tests
80        if strict:
81            return bool(
82                all(isinstance(cond, Test) and check(node, cond, index, parent) for cond in _test)
83            )
84
85        return bool(
86            any(isinstance(cond, Test) and check(node, cond, index, parent) for cond in _test)
87        )
88
89    if isinstance(_test, Callable):
90        # If callable return result of collable after passing node, index, and parent
91        return _test(node, index, node.parent)
92
93    raise Exception("Invalid test condition")

Test if a node passes the given test(s).

Test Types:
  • None: Just checks that the node is a valid node.
  • str: Checks that the test value is == the node.type.
  • dict: Checks all items are valid attributes on the node. and that the values are strictly equal.
  • Callable: Passes the given function the node and it's index, if provided, and checks if the callable returned true.
  • list[Test]: Apply all the rules above for each Test in the list.

If the parent arg is passed so should the index arg.

Arguments:
  • node (All_Nodes): Node to test. Can be any phml node.
  • test (Test): Test to apply to the node. See previous section
  • for more info.
  • index (Optional[int], optional): Index in the parent where the
  • node exists. Defaults to None.
  • parent (Optional[Root | Element], optional): The nodes parent. Defaults to None.
Returns:

True if all tests pass.