@@ -15,6 +15,7 @@ class SourceConnection:
1515 source : str
1616 detail : str = ""
1717 interaction : str = "request"
18+ target_reference : str = ""
1819
1920
2021_PYTHON_REQUEST_METHODS = {
@@ -93,7 +94,7 @@ def infer_source_connections(
9394
9495def _infer_python_connections (
9596 class_name : str ,
96- _host_settings : dict [str , Any ],
97+ host_settings : dict [str , Any ],
9798 roots : tuple [Path , ...],
9899) -> list [SourceConnection ]:
99100 candidates = []
@@ -114,7 +115,12 @@ def _infer_python_connections(
114115
115116 connections : list [SourceConnection ] = []
116117 for _root_index , candidate in candidates [:1 ]:
117- connections .extend (candidate .connections )
118+ connections .extend (
119+ _python_connections_for_host_settings (
120+ candidate .connections ,
121+ host_settings ,
122+ )
123+ )
118124 return _unique_connections (connections )
119125
120126
@@ -281,7 +287,7 @@ def _python_class_connections(class_node: ast.ClassDef) -> list[SourceConnection
281287 continue
282288 call_name = _python_call_name (node .func )
283289 if call_name in _PYTHON_REQUEST_METHODS :
284- for target , detail , interaction in _python_request_targets (
290+ for target , detail , interaction , target_reference in _python_request_targets (
285291 node ,
286292 string_values ,
287293 call_name ,
@@ -292,10 +298,11 @@ def _python_class_connections(class_node: ast.ClassDef) -> list[SourceConnection
292298 source = "Python source" ,
293299 detail = detail ,
294300 interaction = interaction ,
301+ target_reference = target_reference ,
295302 )
296303 )
297304 elif call_name in _PYTHON_MULTI_REQUEST_METHODS :
298- for target , detail , interaction in _python_multi_request_targets (
305+ for target , detail , interaction , target_reference in _python_multi_request_targets (
299306 node ,
300307 string_values ,
301308 call_name ,
@@ -306,6 +313,7 @@ def _python_class_connections(class_node: ast.ClassDef) -> list[SourceConnection
306313 source = "Python source" ,
307314 detail = detail ,
308315 interaction = interaction ,
316+ target_reference = target_reference ,
309317 )
310318 )
311319 return _unique_connections (connections )
@@ -367,7 +375,7 @@ def _python_request_targets(
367375 call : ast .Call ,
368376 string_values : dict [str , list [str ]],
369377 call_name : str ,
370- ) -> list [tuple [str , str , str ]]:
378+ ) -> list [tuple [str , str , str , str ]]:
371379 target_node = _python_call_target_node (call )
372380 if target_node is None :
373381 return []
@@ -383,14 +391,14 @@ def _python_multi_request_targets(
383391 call : ast .Call ,
384392 string_values : dict [str , list [str ]],
385393 call_name : str ,
386- ) -> list [tuple [str , str , str ]]:
394+ ) -> list [tuple [str , str , str , str ]]:
387395 if not call .args :
388396 return []
389397 collection = call .args [0 ]
390398 if not isinstance (collection , (ast .List , ast .Tuple )):
391399 return []
392400
393- targets : list [tuple [str , str , str ]] = []
401+ targets : list [tuple [str , str , str , str ]] = []
394402 for item in collection .elts :
395403 if not isinstance (item , ast .Tuple ) or not item .elts :
396404 continue
@@ -410,20 +418,59 @@ def _resolve_python_targets(
410418 string_values : dict [str , list [str ]],
411419 call_name : str ,
412420 interaction : str ,
413- ) -> list [tuple [str , str , str ]]:
421+ ) -> list [tuple [str , str , str , str ]]:
414422 literal = _python_literal_string (node )
415423 if literal is not None :
416- return [(literal , f"{ call_name } literal" , interaction )]
424+ return [(literal , f"{ call_name } literal" , interaction , "" )]
417425
418426 name = _python_reference_name (node )
419427 if name is None :
420428 return []
429+ detail = f"{ call_name } { name } "
430+ values = string_values .get (name , [])
431+ if not values :
432+ return [("" , detail , interaction , name )]
421433 return [
422- (value , f" { call_name } { name } " , interaction )
423- for value in string_values . get ( name , [])
434+ (value , detail , interaction , name )
435+ for value in values
424436 ]
425437
426438
439+ def _python_connections_for_host_settings (
440+ connections : tuple [SourceConnection , ...],
441+ host_settings : dict [str , Any ],
442+ ) -> list [SourceConnection ]:
443+ resolved : list [SourceConnection ] = []
444+ for connection in connections :
445+ setting_name = _python_reference_setting_name (connection .target_reference )
446+ setting_targets = (
447+ _split_targets (host_settings .get (setting_name , "" ))
448+ if setting_name
449+ else []
450+ )
451+ if setting_targets :
452+ for target in setting_targets :
453+ resolved .append (
454+ SourceConnection (
455+ target = target ,
456+ source = connection .source ,
457+ detail = connection .detail ,
458+ interaction = connection .interaction ,
459+ target_reference = connection .target_reference ,
460+ )
461+ )
462+ continue
463+ if connection .target :
464+ resolved .append (connection )
465+ return resolved
466+
467+
468+ def _python_reference_setting_name (reference : str ) -> str :
469+ if not reference .startswith ("self." ):
470+ return ""
471+ return reference .removeprefix ("self." )
472+
473+
427474def _python_call_target_node (call : ast .Call ) -> ast .AST | None :
428475 for keyword in call .keywords :
429476 if keyword .arg == "target" :
@@ -664,15 +711,16 @@ def _root_key(root: Path) -> str:
664711
665712def _unique_connections (connections : list [SourceConnection ]) -> list [SourceConnection ]:
666713 unique : list [SourceConnection ] = []
667- seen : set [tuple [str , str , str , str ]] = set ()
714+ seen : set [tuple [str , str , str , str , str ]] = set ()
668715 for connection in connections :
669- if not connection .target :
716+ if not connection .target and not connection . target_reference :
670717 continue
671718 key = (
672719 connection .target ,
673720 connection .source ,
674721 connection .detail ,
675722 connection .interaction ,
723+ connection .target_reference ,
676724 )
677725 if key in seen :
678726 continue
0 commit comments