phml.utils.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 index is None or parent.children[index] != node:
54            return False
55
56    if isinstance(_test, str):
57        # If string then validate that the type is the same
58        return hasattr(node, "type") and node.type == _test
59
60    if isinstance(_test, dict):
61        # If dict validate all items with properties are the same
62        # Either in attributes or in
63        return bool(
64            isinstance(node, Element)
65            and all(
66                (hasattr(node, key) and value == getattr(node, key))
67                or (hasattr(node, "properties") and key in node.properties and value == node[key])
68                for key, value in _test.items()
69            )
70        )
71
72    if isinstance(_test, list):
73        # If list then recursively apply tests
74        if strict:
75            return bool(
76                all(isinstance(cond, Test) and check(node, cond, index, parent) for cond in _test)
77            )
78
79        return bool(
80            any(isinstance(cond, Test) and check(node, cond, index, parent) for cond in _test)
81        )
82
83    if isinstance(_test, Callable):
84        # If callable return result of collable after passing node, index, and parent
85        return _test(node, index, node.parent)
86
87    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 index is None or parent.children[index] != node:
55            return False
56
57    if isinstance(_test, str):
58        # If string then validate that the type is the same
59        return hasattr(node, "type") and node.type == _test
60
61    if isinstance(_test, dict):
62        # If dict validate all items with properties are the same
63        # Either in attributes or in
64        return bool(
65            isinstance(node, Element)
66            and all(
67                (hasattr(node, key) and value == getattr(node, key))
68                or (hasattr(node, "properties") and key in node.properties and value == node[key])
69                for key, value in _test.items()
70            )
71        )
72
73    if isinstance(_test, list):
74        # If list then recursively apply tests
75        if strict:
76            return bool(
77                all(isinstance(cond, Test) and check(node, cond, index, parent) for cond in _test)
78            )
79
80        return bool(
81            any(isinstance(cond, Test) and check(node, cond, index, parent) for cond in _test)
82        )
83
84    if isinstance(_test, Callable):
85        # If callable return result of collable after passing node, index, and parent
86        return _test(node, index, node.parent)
87
88    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.

Args
  • 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.