phml.utils.locate.find
Collection of utility methods to find one or many of a specific node.
1"""phml.utils.locate.find 2 3Collection of utility methods to find one or many of a specific node. 4""" 5 6from typing import Optional 7 8from phml.nodes import AST, All_Nodes, Element, Root 9from phml.utils.travel import path, walk 10from phml.utils.validate import Test, test 11 12__all__ = [ 13 "ancestor", 14 "find", 15 "find_all", 16 "find_after", 17 "find_all_after", 18 "find_all_before", 19 "find_before", 20 "find_all_between", 21] 22 23 24def ancestor(*nodes: All_Nodes) -> Optional[All_Nodes]: 25 """Get the common ancestor between two nodes. 26 27 Args: 28 *nodes (All_Nodes): A list of any number of nodes 29 to find the common ancestor form. Worst case it will 30 return the root. 31 32 Returns: 33 Optional[All_Nodes]: The node that is the common 34 ancestor or None if not found. 35 """ 36 total_path: list = None 37 38 def filter_func(node, total_path) -> bool: 39 return node in total_path 40 41 for node in nodes: 42 if total_path is not None: 43 total_path = list(filter(lambda n: filter_func(n, total_path), path(node))) 44 else: 45 total_path = path(node) 46 47 return total_path[-1] if len(total_path) > 0 else None 48 49 50def find(start: Root | Element | AST, condition: Test) -> Optional[All_Nodes]: 51 """Walk the nodes children and return the desired node. 52 53 Returns the first node that matches the condition. 54 55 Args: 56 start (Root | Element): Starting node. 57 condition (Test): Condition to check against each node. 58 59 Returns: 60 Optional[All_Nodes]: Returns the found node or None if not found. 61 """ 62 if isinstance(start, AST): 63 start = start.tree 64 65 for node in walk(start): 66 if test(node, condition): 67 return node 68 69 return None 70 71 72def find_all(start: Root | Element | AST, condition: Test) -> list[All_Nodes]: 73 """Find all nodes that match the condition. 74 75 Args: 76 start (Root | Element): Starting node. 77 condition (Test): Condition to apply to each node. 78 79 Returns: 80 list[All_Nodes]: List of found nodes. Empty if no nodes are found. 81 """ 82 if isinstance(start, AST): 83 start = start.tree 84 85 results = [] 86 for node in walk(start): 87 if test(node, condition): 88 results.append(node) 89 return results 90 91 92def find_after( 93 start: Root | Element | AST, 94 condition: Optional[Test] = None, 95) -> Optional[All_Nodes]: 96 """Get the first sibling node following the provided node that matches 97 the condition. 98 99 Args: 100 start (All_Nodes): Node to get sibling from. 101 condition (Test): Condition to check against each node. 102 103 Returns: 104 Optional[All_Nodes]: Returns the first sibling or None if there 105 are no siblings. 106 """ 107 if isinstance(start, AST): 108 start = start.tree 109 110 idx = start.parent.children.index(start) 111 if len(start.parent.children) - 1 > idx: 112 for node in start.parent.children[idx + 1 :]: 113 if condition is not None: 114 if test(node, condition): 115 return node 116 else: 117 return node 118 return None 119 120 121def find_all_after( 122 start: Element, 123 condition: Optional[Test] = None, 124) -> list[All_Nodes]: 125 """Get all sibling nodes that match the condition. 126 127 Args: 128 start (All_Nodes): Node to get siblings from. 129 condition (Test): Condition to check against each node. 130 131 Returns: 132 list[All_Nodes]: Returns the all siblings that match the 133 condition or an empty list if none were found. 134 """ 135 idx = start.parent.children.index(start) 136 matches = [] 137 138 if len(start.parent.children) - 1 > idx: 139 for node in start.parent.children[idx + 1 :]: 140 if condition is not None: 141 if test(node, condition): 142 matches.append(node) 143 else: 144 matches.append(node) 145 146 return matches 147 148 149def find_before( 150 start: Element, 151 condition: Optional[Test] = None, 152) -> Optional[All_Nodes]: 153 """Find the first sibling node before the given node. If a condition is applied 154 then it will be the first sibling node that passes that condition. 155 156 Args: 157 start (All_Nodes): The node to find the previous sibling from. 158 condition (Optional[Test]): The test that is applied to each node. 159 160 Returns: 161 Optional[All_Nodes]: The first node before the given node 162 or None if no prior siblings. 163 """ 164 if isinstance(start, AST): 165 start = start.tree 166 167 idx = start.parent.children.index(start) 168 if idx > 0: 169 for node in start.parent.children[idx - 1 :: -1]: 170 if condition is not None: 171 if test(node, condition): 172 return node 173 else: 174 return node 175 return None 176 177 178def find_all_before( 179 start: Element, 180 condition: Optional[Test] = None, 181) -> list[All_Nodes]: 182 """Find all nodes that come before the given node. 183 184 Args: 185 start (All_Nodes): The node to find all previous siblings from. 186 condition (Optional[Test]): The condition to apply to each node. 187 188 Returns: 189 list[All_Nodes]: A list of nodes that come before the given node. 190 Empty list if no nodes were found. 191 """ 192 idx = start.parent.children.index(start) 193 matches = [] 194 195 if idx > 0: 196 for node in start.parent.children[:idx]: 197 if condition is not None: 198 if test(node, condition): 199 matches.append(node) 200 else: 201 matches.append(node) 202 return matches 203 204 205def find_all_between( 206 parent: Root | Element | AST, 207 start: Optional[int] = 0, 208 end: Optional[int] = 0, 209 condition: Optional[Test] = None, 210 _range: Optional[slice] = None, 211) -> list[All_Nodes]: 212 """Find all sibling nodes in parent that meet the provided condition from start index 213 to end index. 214 215 Args: 216 parent (Root | Element): The parent element to get nodes from. 217 start (int, optional): The starting index, inclusive. Defaults to 0. 218 end (int, optional): The ending index, exclusive. Defaults to 0. 219 condition (Test, optional): Condition to apply to each node. Defaults to None. 220 _range (slice, optional): Slice to apply to the parent nodes children instead of start and 221 end indecies. Defaults to None. 222 223 Returns: 224 list[All_Nodes]: List of all matching nodes or an empty list if none were found. 225 """ 226 if isinstance(parent, AST): 227 parent = parent.tree 228 229 if _range is not None: 230 start = _range.start 231 end = _range.stop 232 233 results = [] 234 if start in range(0, end) and end in range(start, len(parent.children)): 235 for node in parent.children[start:end]: 236 if condition is not None: 237 if test(node, condition): 238 results.append(node) 239 else: 240 results.append(node) 241 return results
25def ancestor(*nodes: All_Nodes) -> Optional[All_Nodes]: 26 """Get the common ancestor between two nodes. 27 28 Args: 29 *nodes (All_Nodes): A list of any number of nodes 30 to find the common ancestor form. Worst case it will 31 return the root. 32 33 Returns: 34 Optional[All_Nodes]: The node that is the common 35 ancestor or None if not found. 36 """ 37 total_path: list = None 38 39 def filter_func(node, total_path) -> bool: 40 return node in total_path 41 42 for node in nodes: 43 if total_path is not None: 44 total_path = list(filter(lambda n: filter_func(n, total_path), path(node))) 45 else: 46 total_path = path(node) 47 48 return total_path[-1] if len(total_path) > 0 else None
Get the common ancestor between two nodes.
Args
- *nodes (All_Nodes): A list of any number of nodes
- to find the common ancestor form. Worst case it will
- return the root.
Returns
Optional[All_Nodes]: The node that is the common ancestor or None if not found.
51def find(start: Root | Element | AST, condition: Test) -> Optional[All_Nodes]: 52 """Walk the nodes children and return the desired node. 53 54 Returns the first node that matches the condition. 55 56 Args: 57 start (Root | Element): Starting node. 58 condition (Test): Condition to check against each node. 59 60 Returns: 61 Optional[All_Nodes]: Returns the found node or None if not found. 62 """ 63 if isinstance(start, AST): 64 start = start.tree 65 66 for node in walk(start): 67 if test(node, condition): 68 return node 69 70 return None
Walk the nodes children and return the desired node.
Returns the first node that matches the condition.
Args
- start (Root | Element): Starting node.
- condition (Test): Condition to check against each node.
Returns
Optional[All_Nodes]: Returns the found node or None if not found.
73def find_all(start: Root | Element | AST, condition: Test) -> list[All_Nodes]: 74 """Find all nodes that match the condition. 75 76 Args: 77 start (Root | Element): Starting node. 78 condition (Test): Condition to apply to each node. 79 80 Returns: 81 list[All_Nodes]: List of found nodes. Empty if no nodes are found. 82 """ 83 if isinstance(start, AST): 84 start = start.tree 85 86 results = [] 87 for node in walk(start): 88 if test(node, condition): 89 results.append(node) 90 return results
Find all nodes that match the condition.
Args
- start (Root | Element): Starting node.
- condition (Test): Condition to apply to each node.
Returns
list[All_Nodes]: List of found nodes. Empty if no nodes are found.
93def find_after( 94 start: Root | Element | AST, 95 condition: Optional[Test] = None, 96) -> Optional[All_Nodes]: 97 """Get the first sibling node following the provided node that matches 98 the condition. 99 100 Args: 101 start (All_Nodes): Node to get sibling from. 102 condition (Test): Condition to check against each node. 103 104 Returns: 105 Optional[All_Nodes]: Returns the first sibling or None if there 106 are no siblings. 107 """ 108 if isinstance(start, AST): 109 start = start.tree 110 111 idx = start.parent.children.index(start) 112 if len(start.parent.children) - 1 > idx: 113 for node in start.parent.children[idx + 1 :]: 114 if condition is not None: 115 if test(node, condition): 116 return node 117 else: 118 return node 119 return None
Get the first sibling node following the provided node that matches the condition.
Args
- start (All_Nodes): Node to get sibling from.
- condition (Test): Condition to check against each node.
Returns
Optional[All_Nodes]: Returns the first sibling or None if there are no siblings.
122def find_all_after( 123 start: Element, 124 condition: Optional[Test] = None, 125) -> list[All_Nodes]: 126 """Get all sibling nodes that match the condition. 127 128 Args: 129 start (All_Nodes): Node to get siblings from. 130 condition (Test): Condition to check against each node. 131 132 Returns: 133 list[All_Nodes]: Returns the all siblings that match the 134 condition or an empty list if none were found. 135 """ 136 idx = start.parent.children.index(start) 137 matches = [] 138 139 if len(start.parent.children) - 1 > idx: 140 for node in start.parent.children[idx + 1 :]: 141 if condition is not None: 142 if test(node, condition): 143 matches.append(node) 144 else: 145 matches.append(node) 146 147 return matches
Get all sibling nodes that match the condition.
Args
- start (All_Nodes): Node to get siblings from.
- condition (Test): Condition to check against each node.
Returns
list[All_Nodes]: Returns the all siblings that match the condition or an empty list if none were found.
179def find_all_before( 180 start: Element, 181 condition: Optional[Test] = None, 182) -> list[All_Nodes]: 183 """Find all nodes that come before the given node. 184 185 Args: 186 start (All_Nodes): The node to find all previous siblings from. 187 condition (Optional[Test]): The condition to apply to each node. 188 189 Returns: 190 list[All_Nodes]: A list of nodes that come before the given node. 191 Empty list if no nodes were found. 192 """ 193 idx = start.parent.children.index(start) 194 matches = [] 195 196 if idx > 0: 197 for node in start.parent.children[:idx]: 198 if condition is not None: 199 if test(node, condition): 200 matches.append(node) 201 else: 202 matches.append(node) 203 return matches
Find all nodes that come before the given node.
Args
- start (All_Nodes): The node to find all previous siblings from.
- condition (Optional[Test]): The condition to apply to each node.
Returns
list[All_Nodes]: A list of nodes that come before the given node. Empty list if no nodes were found.
150def find_before( 151 start: Element, 152 condition: Optional[Test] = None, 153) -> Optional[All_Nodes]: 154 """Find the first sibling node before the given node. If a condition is applied 155 then it will be the first sibling node that passes that condition. 156 157 Args: 158 start (All_Nodes): The node to find the previous sibling from. 159 condition (Optional[Test]): The test that is applied to each node. 160 161 Returns: 162 Optional[All_Nodes]: The first node before the given node 163 or None if no prior siblings. 164 """ 165 if isinstance(start, AST): 166 start = start.tree 167 168 idx = start.parent.children.index(start) 169 if idx > 0: 170 for node in start.parent.children[idx - 1 :: -1]: 171 if condition is not None: 172 if test(node, condition): 173 return node 174 else: 175 return node 176 return None
Find the first sibling node before the given node. If a condition is applied then it will be the first sibling node that passes that condition.
Args
- start (All_Nodes): The node to find the previous sibling from.
- condition (Optional[Test]): The test that is applied to each node.
Returns
Optional[All_Nodes]: The first node before the given node or None if no prior siblings.
206def find_all_between( 207 parent: Root | Element | AST, 208 start: Optional[int] = 0, 209 end: Optional[int] = 0, 210 condition: Optional[Test] = None, 211 _range: Optional[slice] = None, 212) -> list[All_Nodes]: 213 """Find all sibling nodes in parent that meet the provided condition from start index 214 to end index. 215 216 Args: 217 parent (Root | Element): The parent element to get nodes from. 218 start (int, optional): The starting index, inclusive. Defaults to 0. 219 end (int, optional): The ending index, exclusive. Defaults to 0. 220 condition (Test, optional): Condition to apply to each node. Defaults to None. 221 _range (slice, optional): Slice to apply to the parent nodes children instead of start and 222 end indecies. Defaults to None. 223 224 Returns: 225 list[All_Nodes]: List of all matching nodes or an empty list if none were found. 226 """ 227 if isinstance(parent, AST): 228 parent = parent.tree 229 230 if _range is not None: 231 start = _range.start 232 end = _range.stop 233 234 results = [] 235 if start in range(0, end) and end in range(start, len(parent.children)): 236 for node in parent.children[start:end]: 237 if condition is not None: 238 if test(node, condition): 239 results.append(node) 240 else: 241 results.append(node) 242 return results
Find all sibling nodes in parent that meet the provided condition from start index to end index.
Args
- parent (Root | Element): The parent element to get nodes from.
- start (int, optional): The starting index, inclusive. Defaults to 0.
- end (int, optional): The ending index, exclusive. Defaults to 0.
- condition (Test, optional): Condition to apply to each node. Defaults to None.
- _range (slice, optional): Slice to apply to the parent nodes children instead of start and
- end indecies. Defaults to None.
Returns
list[All_Nodes]: List of all matching nodes or an empty list if none were found.