phml.utilities.validate.check

phml.utilities.validate.test

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

 1"""phml.utilities.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.core.nodes import Element, NODE
11
12if TYPE_CHECKING:
13    from phml.core.nodes import Root
14
15Test = None | str | list | dict | Callable | NODE
16
17
18def check(
19    node: NODE,
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 (NODE): 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, NODE):
62        return node == _test
63
64    if isinstance(_test, str):
65        # If string then validate that the type is the same
66        return hasattr(node, "type") and node.type == _test
67
68    if isinstance(_test, dict):
69        # If dict validate all items with properties are the same
70        # Either in attributes or in
71        return bool(
72            isinstance(node, Element)
73            and all(
74                (hasattr(node, key) and value == getattr(node, key))
75                or (hasattr(node, "properties") and key in node.properties and (value == True or value == node[key]))
76                for key, value in _test.items()
77            )
78        )
79
80    if isinstance(_test, list):
81        # If list then recursively apply tests
82        if strict:
83            return bool(
84                all(isinstance(cond, Test) and check(node, cond, index, parent) for cond in _test)
85            )
86
87        return bool(
88            any(isinstance(cond, Test) and check(node, cond, index, parent) for cond in _test)
89        )
90
91    if isinstance(_test, Callable):
92        # If callable return result of collable after passing node, index, and parent
93        return _test(node, index, node.parent)
94
95    raise Exception("Invalid test condition")
19def check(
20    node: NODE,
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 (NODE): 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, NODE):
63        return node == _test
64
65    if isinstance(_test, str):
66        # If string then validate that the type is the same
67        return hasattr(node, "type") and node.type == _test
68
69    if isinstance(_test, dict):
70        # If dict validate all items with properties are the same
71        # Either in attributes or in
72        return bool(
73            isinstance(node, Element)
74            and all(
75                (hasattr(node, key) and value == getattr(node, key))
76                or (hasattr(node, "properties") and key in node.properties and (value == True or value == node[key]))
77                for key, value in _test.items()
78            )
79        )
80
81    if isinstance(_test, list):
82        # If list then recursively apply tests
83        if strict:
84            return bool(
85                all(isinstance(cond, Test) and check(node, cond, index, parent) for cond in _test)
86            )
87
88        return bool(
89            any(isinstance(cond, Test) and check(node, cond, index, parent) for cond in _test)
90        )
91
92    if isinstance(_test, Callable):
93        # If callable return result of collable after passing node, index, and parent
94        return _test(node, index, node.parent)
95
96    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 (NODE): 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.