phml.utils.locate.index

 1# pylint: disable=missing-module-docstring
 2from typing import Callable, Optional
 3
 4from phml.nodes import AST, Element, Root
 5from phml.utils.validate.check import Test
 6
 7
 8class Index:
 9    """Uses the given key or key generator and creates a mutable dict of key value pairs
10    that can be easily indexed.
11
12    Nodes that don't match the condition or don't have a valid key are not indexed.
13    """
14
15    indexed_tree: dict[str, list[Element]]
16    """The indexed collection of elements"""
17
18    def __init__(
19        self, key: str | Callable, start: AST | Root | Element, condition: Optional[Test] = None
20    ):
21        """
22        Args:
23            `key` (str | Callable): Str represents the property to use as an index. Callable
24            represents a function to call on each element to generate a key. The returned key
25            must be able to be converted to a string. If none then element is skipped.
26            `start` (AST | Root | Element): The root or node to start at while indexing
27            `test` (Test): The test to apply to each node. Only valid/passing nodes
28            will be indexed
29        """
30        from phml.utils import check, walk  # pylint: disable=import-outside-toplevel
31
32        if isinstance(start, AST):
33            start = start.tree
34
35        self.indexed_tree = {}
36        self.key = key
37
38        for node in walk(start):
39            if isinstance(node, Element):
40                if condition is not None:
41                    if check(node, condition):
42                        self.add(node)
43                else:
44                    self.add(node)
45
46    def __iter__(self):
47        return iter(self.indexed_tree)
48
49    def items(self) -> tuple[str, list]:
50        """Get the key value pairs of all indexes."""
51        return self.indexed_tree.items()
52
53    def values(self) -> list[list]:
54        """Get all the values in the collection."""
55        return self.indexed_tree.values()
56
57    def keys(self) -> list[str]:
58        """Get all the keys in the collection."""
59        return self.indexed_tree.keys()
60
61    def add(self, node: Element):
62        """Adds element to indexed collection if not already there."""
63
64        key = node[self.key] if isinstance(self.key, str) else self.key(node)
65        if key not in self.indexed_tree:
66            self.indexed_tree[key] = [node]
67
68        if node not in self.indexed_tree[key]:
69            self.indexed_tree[key].append(node)
70
71    def remove(self, node: Element):
72        """Removes element from indexed collection if there."""
73
74        key = node[self.key] if isinstance(self.key, str) else self.key(node)
75        if key in self.indexed_tree and node in self.indexed_tree[key]:
76            self.indexed_tree[key].remove(node)
77            if len(self.indexed_tree[key]) == 0:
78                self.indexed_tree.pop(key, None)
79
80    def get(self, _key: str) -> Optional[list[Element]]:
81        """Get a specific index from the indexed tree."""
82        return self.indexed_tree.get(_key)
83
84    # Built in key functions
85
86    @classmethod
87    def key_by_tag(cls, node) -> str:
88        """Builds the key from an elements tag. If the node is not an element
89        then the node's type is returned."""
90
91        if isinstance(node, Element):
92            return node.tag
93        return node.type
class Index:
 9class Index:
10    """Uses the given key or key generator and creates a mutable dict of key value pairs
11    that can be easily indexed.
12
13    Nodes that don't match the condition or don't have a valid key are not indexed.
14    """
15
16    indexed_tree: dict[str, list[Element]]
17    """The indexed collection of elements"""
18
19    def __init__(
20        self, key: str | Callable, start: AST | Root | Element, condition: Optional[Test] = None
21    ):
22        """
23        Args:
24            `key` (str | Callable): Str represents the property to use as an index. Callable
25            represents a function to call on each element to generate a key. The returned key
26            must be able to be converted to a string. If none then element is skipped.
27            `start` (AST | Root | Element): The root or node to start at while indexing
28            `test` (Test): The test to apply to each node. Only valid/passing nodes
29            will be indexed
30        """
31        from phml.utils import check, walk  # pylint: disable=import-outside-toplevel
32
33        if isinstance(start, AST):
34            start = start.tree
35
36        self.indexed_tree = {}
37        self.key = key
38
39        for node in walk(start):
40            if isinstance(node, Element):
41                if condition is not None:
42                    if check(node, condition):
43                        self.add(node)
44                else:
45                    self.add(node)
46
47    def __iter__(self):
48        return iter(self.indexed_tree)
49
50    def items(self) -> tuple[str, list]:
51        """Get the key value pairs of all indexes."""
52        return self.indexed_tree.items()
53
54    def values(self) -> list[list]:
55        """Get all the values in the collection."""
56        return self.indexed_tree.values()
57
58    def keys(self) -> list[str]:
59        """Get all the keys in the collection."""
60        return self.indexed_tree.keys()
61
62    def add(self, node: Element):
63        """Adds element to indexed collection if not already there."""
64
65        key = node[self.key] if isinstance(self.key, str) else self.key(node)
66        if key not in self.indexed_tree:
67            self.indexed_tree[key] = [node]
68
69        if node not in self.indexed_tree[key]:
70            self.indexed_tree[key].append(node)
71
72    def remove(self, node: Element):
73        """Removes element from indexed collection if there."""
74
75        key = node[self.key] if isinstance(self.key, str) else self.key(node)
76        if key in self.indexed_tree and node in self.indexed_tree[key]:
77            self.indexed_tree[key].remove(node)
78            if len(self.indexed_tree[key]) == 0:
79                self.indexed_tree.pop(key, None)
80
81    def get(self, _key: str) -> Optional[list[Element]]:
82        """Get a specific index from the indexed tree."""
83        return self.indexed_tree.get(_key)
84
85    # Built in key functions
86
87    @classmethod
88    def key_by_tag(cls, node) -> str:
89        """Builds the key from an elements tag. If the node is not an element
90        then the node's type is returned."""
91
92        if isinstance(node, Element):
93            return node.tag
94        return node.type

Uses the given key or key generator and creates a mutable dict of key value pairs that can be easily indexed.

Nodes that don't match the condition or don't have a valid key are not indexed.

Index( key: Union[str, Callable], start: phml.nodes.AST.AST | phml.nodes.root.Root | phml.nodes.element.Element, condition: Union[NoneType, str, list, dict, Callable] = None)
19    def __init__(
20        self, key: str | Callable, start: AST | Root | Element, condition: Optional[Test] = None
21    ):
22        """
23        Args:
24            `key` (str | Callable): Str represents the property to use as an index. Callable
25            represents a function to call on each element to generate a key. The returned key
26            must be able to be converted to a string. If none then element is skipped.
27            `start` (AST | Root | Element): The root or node to start at while indexing
28            `test` (Test): The test to apply to each node. Only valid/passing nodes
29            will be indexed
30        """
31        from phml.utils import check, walk  # pylint: disable=import-outside-toplevel
32
33        if isinstance(start, AST):
34            start = start.tree
35
36        self.indexed_tree = {}
37        self.key = key
38
39        for node in walk(start):
40            if isinstance(node, Element):
41                if condition is not None:
42                    if check(node, condition):
43                        self.add(node)
44                else:
45                    self.add(node)
Args
  • key (str | Callable): Str represents the property to use as an index. Callable
  • represents a function to call on each element to generate a key. The returned key
  • must be able to be converted to a string. If none then element is skipped.
  • start (AST | Root | Element): The root or node to start at while indexing
  • test (Test): The test to apply to each node. Only valid/passing nodes
  • will be indexed
indexed_tree: dict[str, list[phml.nodes.element.Element]]

The indexed collection of elements

def items(self) -> tuple[str, list]:
50    def items(self) -> tuple[str, list]:
51        """Get the key value pairs of all indexes."""
52        return self.indexed_tree.items()

Get the key value pairs of all indexes.

def values(self) -> list[list]:
54    def values(self) -> list[list]:
55        """Get all the values in the collection."""
56        return self.indexed_tree.values()

Get all the values in the collection.

def keys(self) -> list[str]:
58    def keys(self) -> list[str]:
59        """Get all the keys in the collection."""
60        return self.indexed_tree.keys()

Get all the keys in the collection.

def add(self, node: phml.nodes.element.Element):
62    def add(self, node: Element):
63        """Adds element to indexed collection if not already there."""
64
65        key = node[self.key] if isinstance(self.key, str) else self.key(node)
66        if key not in self.indexed_tree:
67            self.indexed_tree[key] = [node]
68
69        if node not in self.indexed_tree[key]:
70            self.indexed_tree[key].append(node)

Adds element to indexed collection if not already there.

def remove(self, node: phml.nodes.element.Element):
72    def remove(self, node: Element):
73        """Removes element from indexed collection if there."""
74
75        key = node[self.key] if isinstance(self.key, str) else self.key(node)
76        if key in self.indexed_tree and node in self.indexed_tree[key]:
77            self.indexed_tree[key].remove(node)
78            if len(self.indexed_tree[key]) == 0:
79                self.indexed_tree.pop(key, None)

Removes element from indexed collection if there.

def get(self, _key: str) -> Optional[list[phml.nodes.element.Element]]:
81    def get(self, _key: str) -> Optional[list[Element]]:
82        """Get a specific index from the indexed tree."""
83        return self.indexed_tree.get(_key)

Get a specific index from the indexed tree.

@classmethod
def key_by_tag(cls, node) -> str:
87    @classmethod
88    def key_by_tag(cls, node) -> str:
89        """Builds the key from an elements tag. If the node is not an element
90        then the node's type is returned."""
91
92        if isinstance(node, Element):
93            return node.tag
94        return node.type

Builds the key from an elements tag. If the node is not an element then the node's type is returned.