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