12
12
"find_name" ,
13
13
"find_names" ,
14
14
"find_relative_path" ,
15
+ "find_relative_paths" ,
15
16
"find_full_path" ,
16
17
"find_path" ,
17
18
"find_paths" ,
28
29
DAGNodeT = TypeVar ("DAGNodeT" , bound = DAGNode )
29
30
30
31
32
+ def __check_result_count (
33
+ result : Tuple [Any , ...], min_count : int , max_count : int
34
+ ) -> None :
35
+ """Check result fulfil min_count and max_count requirements
36
+
37
+ Args:
38
+ result (Tuple[Any]): result of search
39
+ min_count (int): checks for minimum number of occurrences,
40
+ raise SearchError if the number of results do not meet min_count, defaults to None
41
+ max_count (int): checks for maximum number of occurrences,
42
+ raise SearchError if the number of results do not meet min_count, defaults to None
43
+ """
44
+ if min_count and len (result ) < min_count :
45
+ raise SearchError (
46
+ f"Expected more than or equal to { min_count } element(s), found { len (result )} elements\n { result } "
47
+ )
48
+ if max_count and len (result ) > max_count :
49
+ raise SearchError (
50
+ f"Expected less than or equal to { max_count } element(s), found { len (result )} elements\n { result } "
51
+ )
52
+
53
+
31
54
def findall (
32
55
tree : T ,
33
56
condition : Callable [[T ], bool ],
@@ -36,7 +59,7 @@ def findall(
36
59
max_count : int = 0 ,
37
60
) -> Tuple [T , ...]:
38
61
"""
39
- Search tree for nodes matching condition (callable function).
62
+ Search tree for one or more nodes matching condition (callable function).
40
63
41
64
Examples:
42
65
>>> from bigtree import Node, findall
@@ -60,20 +83,13 @@ def findall(
60
83
(Tuple[BaseNode, ...])
61
84
"""
62
85
result = tuple (preorder_iter (tree , filter_condition = condition , max_depth = max_depth ))
63
- if min_count and len (result ) < min_count :
64
- raise SearchError (
65
- f"Expected more than { min_count } element(s), found { len (result )} elements\n { result } "
66
- )
67
- if max_count and len (result ) > max_count :
68
- raise SearchError (
69
- f"Expected less than { max_count } element(s), found { len (result )} elements\n { result } "
70
- )
86
+ __check_result_count (result , min_count , max_count )
71
87
return result
72
88
73
89
74
90
def find (tree : T , condition : Callable [[T ], bool ], max_depth : int = 0 ) -> T :
75
91
"""
76
- Search tree for * single node* matching condition (callable function).
92
+ Search tree for a single node matching condition (callable function).
77
93
78
94
Examples:
79
95
>>> from bigtree import Node, find
@@ -86,7 +102,7 @@ def find(tree: T, condition: Callable[[T], bool], max_depth: int = 0) -> T:
86
102
>>> find(root, lambda node: node.age > 5)
87
103
Traceback (most recent call last):
88
104
...
89
- bigtree.utils.exceptions.SearchError: Expected less than 1 element(s), found 4 elements
105
+ bigtree.utils.exceptions.SearchError: Expected less than or equal to 1 element(s), found 4 elements
90
106
(Node(/a, age=90), Node(/a/b, age=65), Node(/a/c, age=60), Node(/a/c/d, age=40))
91
107
92
108
Args:
@@ -104,7 +120,7 @@ def find(tree: T, condition: Callable[[T], bool], max_depth: int = 0) -> T:
104
120
105
121
def find_name (tree : NodeT , name : str , max_depth : int = 0 ) -> NodeT :
106
122
"""
107
- Search tree for single node matching name attribute.
123
+ Search tree for a single node matching name attribute.
108
124
109
125
Examples:
110
126
>>> from bigtree import Node, find_name
@@ -128,7 +144,7 @@ def find_name(tree: NodeT, name: str, max_depth: int = 0) -> NodeT:
128
144
129
145
def find_names (tree : NodeT , name : str , max_depth : int = 0 ) -> Iterable [NodeT ]:
130
146
"""
131
- Search tree for multiple node(s) matching name attribute.
147
+ Search tree for one or more nodes matching name attribute.
132
148
133
149
Examples:
134
150
>>> from bigtree import Node, find_names
@@ -152,9 +168,9 @@ def find_names(tree: NodeT, name: str, max_depth: int = 0) -> Iterable[NodeT]:
152
168
return findall (tree , lambda node : node .node_name == name , max_depth )
153
169
154
170
155
- def find_relative_path (tree : NodeT , path_name : str ) -> Iterable [ NodeT ] :
171
+ def find_relative_path (tree : NodeT , path_name : str ) -> NodeT :
156
172
r"""
157
- Search tree for single node matching relative path attribute.
173
+ Search tree for a single node matching relative path attribute.
158
174
159
175
- Supports unix folder expression for relative path, i.e., '../../node_name'
160
176
- Supports wildcards, i.e., '\*/node_name'
@@ -167,18 +183,64 @@ def find_relative_path(tree: NodeT, path_name: str) -> Iterable[NodeT]:
167
183
>>> c = Node("c", age=60, parent=root)
168
184
>>> d = Node("d", age=40, parent=c)
169
185
>>> find_relative_path(d, "..")
170
- ( Node(/a/c, age=60), )
186
+ Node(/a/c, age=60)
171
187
>>> find_relative_path(d, "../../b")
172
- ( Node(/a/b, age=65), )
188
+ Node(/a/b, age=65)
173
189
>>> find_relative_path(d, "../../*")
190
+ Traceback (most recent call last):
191
+ ...
192
+ bigtree.utils.exceptions.SearchError: Expected less than or equal to 1 element(s), found 2 elements
174
193
(Node(/a/b, age=65), Node(/a/c, age=60))
175
194
176
195
Args:
177
196
tree (Node): tree to search
178
197
path_name (str): value to match (relative path) of path_name attribute
179
198
180
199
Returns:
181
- (Iterable[Node])
200
+ (Node)
201
+ """
202
+ result = find_relative_paths (tree , path_name , max_count = 1 )
203
+
204
+ if result :
205
+ return result [0 ]
206
+
207
+
208
+ def find_relative_paths (
209
+ tree : NodeT ,
210
+ path_name : str ,
211
+ min_count : int = 0 ,
212
+ max_count : int = 0 ,
213
+ ) -> Tuple [NodeT , ...]:
214
+ r"""
215
+ Search tree for one or more nodes matching relative path attribute.
216
+
217
+ - Supports unix folder expression for relative path, i.e., '../../node_name'
218
+ - Supports wildcards, i.e., '\*/node_name'
219
+ - If path name starts with leading separator symbol, it will start at root node.
220
+
221
+ Examples:
222
+ >>> from bigtree import Node, find_relative_paths
223
+ >>> root = Node("a", age=90)
224
+ >>> b = Node("b", age=65, parent=root)
225
+ >>> c = Node("c", age=60, parent=root)
226
+ >>> d = Node("d", age=40, parent=c)
227
+ >>> find_relative_paths(d, "..")
228
+ (Node(/a/c, age=60),)
229
+ >>> find_relative_paths(d, "../../b")
230
+ (Node(/a/b, age=65),)
231
+ >>> find_relative_paths(d, "../../*")
232
+ (Node(/a/b, age=65), Node(/a/c, age=60))
233
+
234
+ Args:
235
+ tree (Node): tree to search
236
+ path_name (str): value to match (relative path) of path_name attribute
237
+ min_count (int): checks for minimum number of occurrences,
238
+ raise SearchError if the number of results do not meet min_count, defaults to None
239
+ max_count (int): checks for maximum number of occurrences,
240
+ raise SearchError if the number of results do not meet min_count, defaults to None
241
+
242
+ Returns:
243
+ (Tuple[Node, ...])
182
244
"""
183
245
sep = tree .sep
184
246
if path_name .startswith (sep ):
@@ -220,13 +282,14 @@ def resolve(node: NodeT, path_idx: int) -> None:
220
282
resolve (node , path_idx + 1 )
221
283
222
284
resolve (tree , 0 )
223
-
224
- return tuple (resolved_nodes )
285
+ result = tuple (resolved_nodes )
286
+ __check_result_count (result , min_count , max_count )
287
+ return result
225
288
226
289
227
290
def find_full_path (tree : NodeT , path_name : str ) -> NodeT :
228
291
"""
229
- Search tree for single node matching path attribute.
292
+ Search tree for a single node matching path attribute.
230
293
231
294
- Path name can be with or without leading tree path separator symbol.
232
295
- Path name must be full path, works similar to `find_path` but faster.
@@ -265,7 +328,7 @@ def find_full_path(tree: NodeT, path_name: str) -> NodeT:
265
328
266
329
def find_path (tree : NodeT , path_name : str ) -> NodeT :
267
330
"""
268
- Search tree for single node matching path attribute.
331
+ Search tree for a single node matching path attribute.
269
332
270
333
- Path name can be with or without leading tree path separator symbol.
271
334
- Path name can be full path or partial path (trailing part of path) or node name.
@@ -292,9 +355,9 @@ def find_path(tree: NodeT, path_name: str) -> NodeT:
292
355
return find (tree , lambda node : node .path_name .endswith (path_name ))
293
356
294
357
295
- def find_paths (tree : NodeT , path_name : str ) -> Tuple [NodeT , ... ]:
358
+ def find_paths (tree : NodeT , path_name : str ) -> Iterable [NodeT ]:
296
359
"""
297
- Search tree for multiple nodes matching path attribute.
360
+ Search tree for one or more nodes matching path attribute.
298
361
299
362
- Path name can be with or without leading tree path separator symbol.
300
363
- Path name can be partial path (trailing part of path) or node name.
@@ -315,7 +378,7 @@ def find_paths(tree: NodeT, path_name: str) -> Tuple[NodeT, ...]:
315
378
path_name (str): value to match (full path) or trailing part (partial path) of path_name attribute
316
379
317
380
Returns:
318
- (Tuple [Node, ... ])
381
+ (Iterable [Node])
319
382
"""
320
383
path_name = path_name .rstrip (tree .sep )
321
384
return findall (tree , lambda node : node .path_name .endswith (path_name ))
@@ -325,7 +388,7 @@ def find_attr(
325
388
tree : BaseNode , attr_name : str , attr_value : Any , max_depth : int = 0
326
389
) -> BaseNode :
327
390
"""
328
- Search tree for single node matching custom attribute.
391
+ Search tree for a single node matching custom attribute.
329
392
330
393
Examples:
331
394
>>> from bigtree import Node, find_attr
@@ -354,9 +417,9 @@ def find_attr(
354
417
355
418
def find_attrs (
356
419
tree : BaseNode , attr_name : str , attr_value : Any , max_depth : int = 0
357
- ) -> Tuple [BaseNode , ... ]:
420
+ ) -> Iterable [BaseNode ]:
358
421
"""
359
- Search tree for node(s) matching custom attribute.
422
+ Search tree for one or more nodes matching custom attribute.
360
423
361
424
Examples:
362
425
>>> from bigtree import Node, find_attrs
@@ -374,7 +437,7 @@ def find_attrs(
374
437
max_depth (int): maximum depth to search for, based on the `depth` attribute, defaults to None
375
438
376
439
Returns:
377
- (Tuple [BaseNode, ... ])
440
+ (Iterable [BaseNode])
378
441
"""
379
442
return findall (
380
443
tree ,
@@ -390,7 +453,7 @@ def find_children(
390
453
max_count : int = 0 ,
391
454
) -> Tuple [Union [T , DAGNodeT ], ...]:
392
455
"""
393
- Search children for nodes matching condition (callable function).
456
+ Search children for one or more nodes matching condition (callable function).
394
457
395
458
Examples:
396
459
>>> from bigtree import Node, find_children
@@ -410,17 +473,10 @@ def find_children(
410
473
raise SearchError if the number of results do not meet min_count, defaults to None
411
474
412
475
Returns:
413
- (BaseNode/ DAGNode)
476
+ (Tuple[Union[ BaseNode, DAGNode], ...] )
414
477
"""
415
478
result = tuple ([node for node in tree .children if node and condition (node )])
416
- if min_count and len (result ) < min_count :
417
- raise SearchError (
418
- f"Expected more than { min_count } element(s), found { len (result )} elements\n { result } "
419
- )
420
- if max_count and len (result ) > max_count :
421
- raise SearchError (
422
- f"Expected less than { max_count } element(s), found { len (result )} elements\n { result } "
423
- )
479
+ __check_result_count (result , min_count , max_count )
424
480
return result
425
481
426
482
@@ -429,7 +485,7 @@ def find_child(
429
485
condition : Callable [[Union [T , DAGNodeT ]], bool ],
430
486
) -> Union [T , DAGNodeT ]:
431
487
"""
432
- Search children for * single node* matching condition (callable function).
488
+ Search children for a single node matching condition (callable function).
433
489
434
490
Examples:
435
491
>>> from bigtree import Node, find_child
@@ -456,7 +512,7 @@ def find_child_by_name(
456
512
tree : Union [NodeT , DAGNodeT ], name : str
457
513
) -> Union [NodeT , DAGNodeT ]:
458
514
"""
459
- Search tree for single node matching name attribute.
515
+ Search tree for a single node matching name attribute.
460
516
461
517
Examples:
462
518
>>> from bigtree import Node, find_child_by_name
0 commit comments