@@ -210,27 +210,42 @@ def _clean_cpe(cpe: str) -> str:
210
210
# Remove trailing ':' characters
211
211
return cleaned_cpe .rstrip (":" )
212
212
213
- def extend_with_product_mappings (self , ancestor_trees : list [Node ]) -> list [Node ]:
214
- """Create a new list of results with any matching streams or module as ancestors"""
213
+ def extend_with_product_mappings (
214
+ self , ancestor_trees : list [Node ], keep_cpes : bool = False
215
+ ) -> None :
216
+ """Update the ancestor_trees with any matching streams or module as descendants
217
+
218
+ Args:
219
+ ancestor_trees: List of Node trees to extend with product mappings
220
+ keep_cpes: If False, replace CPE leaf nodes with product streams. If True, keep CPE nodes as parents of streams.
221
+ """
215
222
if not self .product_trees :
216
223
# ProdDefs service is unavailable, don't attempt any product mapping
217
- return ancestor_trees
224
+ return None
225
+
218
226
for tree in ancestor_trees :
219
227
for leaf in tree .leaves :
220
228
cleaned_leaf_name = self ._clean_cpe (leaf .name )
221
- leaf_with_products = self ._check_streams (leaf , cleaned_leaf_name )
229
+ leaf_with_products = self ._check_streams (
230
+ leaf , cleaned_leaf_name , keep_cpes
231
+ )
222
232
if not leaf_with_products :
223
- leaf_with_products = self ._check_modules (leaf , cleaned_leaf_name )
233
+ leaf_with_products = self ._check_modules (
234
+ leaf , cleaned_leaf_name , keep_cpes
235
+ )
224
236
if not leaf_with_products :
225
237
console .print (
226
238
f"Warning, didn't find any products matching { cleaned_leaf_name } " ,
227
239
style = "warning" ,
228
240
)
229
- # the tree is modified as a side effect of the checking streams|modules
230
- # therefore we do not need to explicitly store and return it elsewhere
231
- return ancestor_trees
232
-
233
- def _check_streams (self , leaf : Node , cpe : str ) -> list [Node ]:
241
+ else :
242
+ # When keep_cpes=False, we need to remove the CPE leaf from the tree
243
+ # since it's been replaced by the product streams
244
+ if not keep_cpes :
245
+ # Remove the CPE leaf node from its parent
246
+ leaf .parent = None
247
+
248
+ def _check_streams (self , leaf : Node , cpe : str , keep_cpes : bool ) -> list [Node ]:
234
249
"""Check if cpe matches exactly to any ProductStreams, if it does add the CPE as a parent
235
250
of the stream. If more than one stream matches, create copies of the stream and leaf"""
236
251
if cpe not in self .stream_nodes_by_cpe :
@@ -240,18 +255,30 @@ def _check_streams(self, leaf: Node, cpe: str) -> list[Node]:
240
255
# the original stream_nodes_by_cpe map which should be preserved incase we encounter the
241
256
# same CPE twice
242
257
copy_of_stream_nodes = copy .deepcopy (stream_nodes )
243
- return self ._duplicate_leaves_and_set_parents (leaf , copy_of_stream_nodes )
258
+ return self ._duplicate_leaves_and_set_parents (
259
+ leaf , copy_of_stream_nodes , keep_cpes
260
+ )
244
261
245
- def _check_modules (self , leaf : Node , cpe : str ) -> list [Node ]:
262
+ def _check_modules (self , leaf : Node , cpe : str , keep_cpes : bool ) -> list [Node ]:
246
263
"""Check if the cpe matches any ProductModule"""
247
264
module_nodes = self .match_module_pattern (cpe )
248
- return self ._duplicate_leaves_and_set_parents (leaf , module_nodes )
265
+ return self ._duplicate_leaves_and_set_parents (leaf , module_nodes , keep_cpes )
249
266
250
267
def _duplicate_leaves_and_set_parents (
251
- self , leaf : Node , product_nodes : list [Any ]
268
+ self , leaf : Node , product_nodes : list [Any ], keep_cpes : bool
252
269
) -> list [Node ]:
253
270
"""Convert product modules to their parent streams and attach all streams as children of the leaf.
254
- Deduplicates streams to avoid multiple identical children. Returns the leaf in a list."""
271
+ Deduplicates streams to avoid multiple identical children.
272
+
273
+ Args:
274
+ leaf: The leaf node to process
275
+ product_nodes: List of product nodes (modules or streams)
276
+ keep_cpes: If False, replace the leaf with streams in the tree. If True, set streams as children of the leaf.
277
+
278
+ Returns:
279
+ If keep_cpes=True: Returns the leaf in a list.
280
+ If keep_cpes=False: Returns the list of unique streams that replaced the leaf.
281
+ """
255
282
if not product_nodes :
256
283
return []
257
284
@@ -274,10 +301,17 @@ def _duplicate_leaves_and_set_parents(
274
301
unique_streams .append (stream )
275
302
seen .add (stream )
276
303
277
- for stream in unique_streams :
278
- stream .parent = leaf
279
-
280
- return [leaf ]
304
+ if keep_cpes :
305
+ # Original behavior: set streams as children of the leaf
306
+ for stream in unique_streams :
307
+ stream .parent = leaf
308
+ return [leaf ]
309
+ else :
310
+ # New behavior: replace the leaf with the streams in the tree
311
+ # Each stream takes the place of the leaf in the tree hierarchy
312
+ for stream in unique_streams :
313
+ stream .parent = leaf .parent
314
+ return unique_streams
281
315
282
316
def _add_ancestor (self , leaf : Node , product : Any ) -> None :
283
317
if product .parent :
0 commit comments