phml.utils.locate.find

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, check
 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, strict: bool = True) -> 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 check(node, condition, strict=strict):
 67            return node
 68
 69    return None
 70
 71
 72def find_all(start: Root | Element | AST, condition: Test, strict: bool = True) -> 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 check(node, condition, strict=strict):
 88            results.append(node)
 89    return results
 90
 91
 92def find_after(
 93    start: Root | Element | AST,
 94    condition: Optional[Test] = None,
 95    strict: bool = True,
 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 check(node, condition, strict=strict):
116                    return node
117            else:
118                return node
119    return None
120
121
122def find_all_after(
123    start: Element,
124    condition: Optional[Test] = None,
125    strict: bool = True,
126) -> list[All_Nodes]:
127    """Get all sibling nodes that match the condition.
128
129    Args:
130        start (All_Nodes): Node to get siblings from.
131        condition (Test): Condition to check against each node.
132
133    Returns:
134        list[All_Nodes]: Returns the all siblings that match the
135        condition or an empty list if none were found.
136    """
137    idx = start.parent.children.index(start)
138    matches = []
139
140    if len(start.parent.children) - 1 > idx:
141        for node in start.parent.children[idx + 1 :]:
142            if condition is not None:
143                if check(node, condition, strict=strict):
144                    matches.append(node)
145            else:
146                matches.append(node)
147
148    return matches
149
150
151def find_before(
152    start: Element,
153    condition: Optional[Test] = None,
154    strict: bool = True,
155) -> Optional[All_Nodes]:
156    """Find the first sibling node before the given node. If a condition is applied
157    then it will be the first sibling node that passes that condition.
158
159    Args:
160        start (All_Nodes): The node to find the previous sibling from.
161        condition (Optional[Test]): The test that is applied to each node.
162
163    Returns:
164        Optional[All_Nodes]: The first node before the given node
165        or None if no prior siblings.
166    """
167    if isinstance(start, AST):
168        start = start.tree
169
170    idx = start.parent.children.index(start)
171    if idx > 0:
172        for node in start.parent.children[idx - 1 :: -1]:
173            if condition is not None:
174                if check(node, condition, strict=strict):
175                    return node
176            else:
177                return node
178    return None
179
180
181def find_all_before(
182    start: Element,
183    condition: Optional[Test] = None,
184    strict: bool = True,
185) -> list[All_Nodes]:
186    """Find all nodes that come before the given node.
187
188    Args:
189        start (All_Nodes): The node to find all previous siblings from.
190        condition (Optional[Test]): The condition to apply to each node.
191
192    Returns:
193        list[All_Nodes]: A list of nodes that come before the given node.
194        Empty list if no nodes were found.
195    """
196    idx = start.parent.children.index(start)
197    matches = []
198
199    if idx > 0:
200        for node in start.parent.children[:idx]:
201            if condition is not None:
202                if check(node, condition, strict=strict):
203                    matches.append(node)
204            else:
205                matches.append(node)
206    return matches
207
208
209def find_all_between(
210    parent: Root | Element | AST,
211    start: Optional[int] = 0,
212    end: Optional[int] = 0,
213    condition: Optional[Test] = None,
214    _range: Optional[slice] = None,
215    strict: bool = True,
216) -> list[All_Nodes]:
217    """Find all sibling nodes in parent that meet the provided condition from start index
218    to end index.
219
220    Args:
221        parent (Root | Element): The parent element to get nodes from.
222        start (int, optional): The starting index, inclusive. Defaults to 0.
223        end (int, optional): The ending index, exclusive. Defaults to 0.
224        condition (Test, optional): Condition to apply to each node. Defaults to None.
225        _range (slice, optional): Slice to apply to the parent nodes children instead of start and
226        end indecies. Defaults to None.
227
228    Returns:
229        list[All_Nodes]: List of all matching nodes or an empty list if none were found.
230    """
231    if isinstance(parent, AST):
232        parent = parent.tree
233
234    if _range is not None:
235        start = _range.start
236        end = _range.stop
237
238    results = []
239    if start in range(0, end) and end in range(start, len(parent.children) + 1):
240        for node in parent.children[start:end]:
241            if condition is not None:
242                if check(node, condition, strict=strict):
243                    results.append(node)
244            else:
245                results.append(node)
246    return results
def ancestor( *nodes: 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) -> Union[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, NoneType]:
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.

def find( start: phml.nodes.root.Root | phml.nodes.element.Element | phml.nodes.AST.AST, condition: Union[NoneType, str, list, dict, Callable], strict: bool = True) -> Union[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, NoneType]:
51def find(start: Root | Element | AST, condition: Test, strict: bool = True) -> 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 check(node, condition, strict=strict):
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.

def find_all( start: phml.nodes.root.Root | phml.nodes.element.Element | phml.nodes.AST.AST, condition: Union[NoneType, str, list, dict, Callable], strict: bool = True) -> list[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]:
73def find_all(start: Root | Element | AST, condition: Test, strict: bool = True) -> 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 check(node, condition, strict=strict):
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.

def find_after( start: phml.nodes.root.Root | phml.nodes.element.Element | phml.nodes.AST.AST, condition: Union[NoneType, str, list, dict, Callable] = None, strict: bool = True) -> Union[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, NoneType]:
 93def find_after(
 94    start: Root | Element | AST,
 95    condition: Optional[Test] = None,
 96    strict: bool = True,
 97) -> Optional[All_Nodes]:
 98    """Get the first sibling node following the provided node that matches
 99    the condition.
100
101    Args:
102        start (All_Nodes): Node to get sibling from.
103        condition (Test): Condition to check against each node.
104
105    Returns:
106        Optional[All_Nodes]: Returns the first sibling or None if there
107        are no siblings.
108    """
109    if isinstance(start, AST):
110        start = start.tree
111
112    idx = start.parent.children.index(start)
113    if len(start.parent.children) - 1 > idx:
114        for node in start.parent.children[idx + 1 :]:
115            if condition is not None:
116                if check(node, condition, strict=strict):
117                    return node
118            else:
119                return node
120    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.

def find_all_after( start: phml.nodes.element.Element, condition: Union[NoneType, str, list, dict, Callable] = None, strict: bool = True) -> list[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]:
123def find_all_after(
124    start: Element,
125    condition: Optional[Test] = None,
126    strict: bool = True,
127) -> list[All_Nodes]:
128    """Get all sibling nodes that match the condition.
129
130    Args:
131        start (All_Nodes): Node to get siblings from.
132        condition (Test): Condition to check against each node.
133
134    Returns:
135        list[All_Nodes]: Returns the all siblings that match the
136        condition or an empty list if none were found.
137    """
138    idx = start.parent.children.index(start)
139    matches = []
140
141    if len(start.parent.children) - 1 > idx:
142        for node in start.parent.children[idx + 1 :]:
143            if condition is not None:
144                if check(node, condition, strict=strict):
145                    matches.append(node)
146            else:
147                matches.append(node)
148
149    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.

def find_all_before( start: phml.nodes.element.Element, condition: Union[NoneType, str, list, dict, Callable] = None, strict: bool = True) -> list[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]:
182def find_all_before(
183    start: Element,
184    condition: Optional[Test] = None,
185    strict: bool = True,
186) -> list[All_Nodes]:
187    """Find all nodes that come before the given node.
188
189    Args:
190        start (All_Nodes): The node to find all previous siblings from.
191        condition (Optional[Test]): The condition to apply to each node.
192
193    Returns:
194        list[All_Nodes]: A list of nodes that come before the given node.
195        Empty list if no nodes were found.
196    """
197    idx = start.parent.children.index(start)
198    matches = []
199
200    if idx > 0:
201        for node in start.parent.children[:idx]:
202            if condition is not None:
203                if check(node, condition, strict=strict):
204                    matches.append(node)
205            else:
206                matches.append(node)
207    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.

def find_before( start: phml.nodes.element.Element, condition: Union[NoneType, str, list, dict, Callable] = None, strict: bool = True) -> Union[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, NoneType]:
152def find_before(
153    start: Element,
154    condition: Optional[Test] = None,
155    strict: bool = True,
156) -> Optional[All_Nodes]:
157    """Find the first sibling node before the given node. If a condition is applied
158    then it will be the first sibling node that passes that condition.
159
160    Args:
161        start (All_Nodes): The node to find the previous sibling from.
162        condition (Optional[Test]): The test that is applied to each node.
163
164    Returns:
165        Optional[All_Nodes]: The first node before the given node
166        or None if no prior siblings.
167    """
168    if isinstance(start, AST):
169        start = start.tree
170
171    idx = start.parent.children.index(start)
172    if idx > 0:
173        for node in start.parent.children[idx - 1 :: -1]:
174            if condition is not None:
175                if check(node, condition, strict=strict):
176                    return node
177            else:
178                return node
179    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.

def find_all_between( parent: phml.nodes.root.Root | phml.nodes.element.Element | phml.nodes.AST.AST, start: Optional[int] = 0, end: Optional[int] = 0, condition: Union[NoneType, str, list, dict, Callable] = None, _range: Optional[slice] = None, strict: bool = True) -> list[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]:
210def find_all_between(
211    parent: Root | Element | AST,
212    start: Optional[int] = 0,
213    end: Optional[int] = 0,
214    condition: Optional[Test] = None,
215    _range: Optional[slice] = None,
216    strict: bool = True,
217) -> list[All_Nodes]:
218    """Find all sibling nodes in parent that meet the provided condition from start index
219    to end index.
220
221    Args:
222        parent (Root | Element): The parent element to get nodes from.
223        start (int, optional): The starting index, inclusive. Defaults to 0.
224        end (int, optional): The ending index, exclusive. Defaults to 0.
225        condition (Test, optional): Condition to apply to each node. Defaults to None.
226        _range (slice, optional): Slice to apply to the parent nodes children instead of start and
227        end indecies. Defaults to None.
228
229    Returns:
230        list[All_Nodes]: List of all matching nodes or an empty list if none were found.
231    """
232    if isinstance(parent, AST):
233        parent = parent.tree
234
235    if _range is not None:
236        start = _range.start
237        end = _range.stop
238
239    results = []
240    if start in range(0, end) and end in range(start, len(parent.children) + 1):
241        for node in parent.children[start:end]:
242            if condition is not None:
243                if check(node, condition, strict=strict):
244                    results.append(node)
245            else:
246                results.append(node)
247    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.