phml.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.travel.travel import path, walk
 10from phml.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: All_Nodes,
 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
109    if not isinstance(start, (AST, Root)):
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 check(node, condition, strict=strict):
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    strict: bool = True,
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 check(node, condition, strict=strict):
143                    matches.append(node)
144            else:
145                matches.append(node)
146
147    return matches
148
149
150def find_before(
151    start: All_Nodes,
152    condition: Optional[Test] = None,
153    strict: bool = True,
154) -> Optional[All_Nodes]:
155    """Find the first sibling node before the given node. If a condition is applied
156    then it will be the first sibling node that passes that condition.
157
158    Args:
159        start (All_Nodes): The node to find the previous sibling from.
160        condition (Optional[Test]): The test that is applied to each node.
161
162    Returns:
163        Optional[All_Nodes]: The first node before the given node
164        or None if no prior siblings.
165    """
166
167    if not isinstance(start, (AST, Root)):
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 check(node, condition, strict=strict):
173                        return node
174                else:
175                    return node
176    return None
177
178
179def find_all_before(
180    start: Element,
181    condition: Optional[Test] = None,
182    strict: bool = True,
183) -> list[All_Nodes]:
184    """Find all nodes that come before the given node.
185
186    Args:
187        start (All_Nodes): The node to find all previous siblings from.
188        condition (Optional[Test]): The condition to apply to each node.
189
190    Returns:
191        list[All_Nodes]: A list of nodes that come before the given node.
192        Empty list if no nodes were found.
193    """
194    idx = start.parent.children.index(start)
195    matches = []
196
197    if idx > 0:
198        for node in start.parent.children[:idx]:
199            if condition is not None:
200                if check(node, condition, strict=strict):
201                    matches.append(node)
202            else:
203                matches.append(node)
204    return matches
205
206
207def find_all_between(
208    parent: Root | Element | AST,
209    start: Optional[int] = 0,
210    end: Optional[int] = 0,
211    condition: Optional[Test] = None,
212    _range: Optional[slice] = None,
213    strict: bool = True,
214) -> list[All_Nodes]:
215    """Find all sibling nodes in parent that meet the provided condition from start index
216    to end index.
217
218    Args:
219        parent (Root | Element): The parent element to get nodes from.
220        start (int, optional): The starting index, inclusive. Defaults to 0.
221        end (int, optional): The ending index, exclusive. Defaults to 0.
222        condition (Test, optional): Condition to apply to each node. Defaults to None.
223        _range (slice, optional): Slice to apply to the parent nodes children instead of start and
224        end indecies. Defaults to None.
225
226    Returns:
227        list[All_Nodes]: List of all matching nodes or an empty list if none were found.
228    """
229    if isinstance(parent, AST):
230        parent = parent.tree
231
232    if _range is not None:
233        start = _range.start
234        end = _range.stop
235
236    results = []
237    if start in range(0, end) and end in range(start, len(parent.children) + 1):
238        for node in parent.children[start:end]:
239            if condition is not None:
240                if check(node, condition, strict=strict):
241                    results.append(node)
242            else:
243                results.append(node)
244    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.

Arguments:
  • *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.

Arguments:
  • 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.

Arguments:
  • 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.text.Text | phml.nodes.comment.Comment | phml.nodes.doctype.DocType | phml.nodes.parent.Parent | phml.nodes.node.Node | phml.nodes.literal.Literal, 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: All_Nodes,
 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
110    if not isinstance(start, (AST, Root)):
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

Get the first sibling node following the provided node that matches the condition.

Arguments:
  • 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]:
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

Get all sibling nodes that match the condition.

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

Find all nodes that come before the given node.

Arguments:
  • 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.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, 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]:
151def find_before(
152    start: All_Nodes,
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
168    if not isinstance(start, (AST, Root)):
169        idx = start.parent.children.index(start)
170        if idx > 0:
171            for node in start.parent.children[idx - 1 :: -1]:
172                if condition is not None:
173                    if check(node, condition, strict=strict):
174                        return node
175                else:
176                    return node
177    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.

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

Find all sibling nodes in parent that meet the provided condition from start index to end index.

Arguments:
  • 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.