@@ -69,6 +69,55 @@ class ValidationWarning(TypedDict):
6969 reason : str
7070
7171
72+ def _extract_service_ref (
73+ sub_path : str ,
74+ value : str ,
75+ refs : list [ExtractedRef ],
76+ unvalidated_templates : list [int ],
77+ ) -> None :
78+ """Record a service ref at *sub_path*, counting templates instead."""
79+ if _is_template (value ):
80+ unvalidated_templates [0 ] += 1
81+ else :
82+ refs .append ({"path" : sub_path , "value" : value , "kind" : "service" })
83+
84+
85+ def _extract_entity_refs (
86+ sub_path : str ,
87+ value : Any ,
88+ refs : list [ExtractedRef ],
89+ unvalidated_templates : list [int ],
90+ ) -> bool :
91+ """Record entity ref(s) from a str or list *value*; return True if handled.
92+
93+ Returns False when *value* is neither a str nor a list so the caller can
94+ keep recursing into it (an ``entity_id`` mapped to a nested structure).
95+ """
96+ if isinstance (value , str ):
97+ if _is_template (value ):
98+ unvalidated_templates [0 ] += 1
99+ else :
100+ refs .append ({"path" : sub_path , "value" : value , "kind" : "entity" })
101+ return True
102+ if isinstance (value , list ):
103+ for i , item in enumerate (value ):
104+ if not isinstance (item , str ):
105+ continue
106+ item_path = f"{ sub_path } [{ i } ]"
107+ if _is_template (item ):
108+ unvalidated_templates [0 ] += 1
109+ else :
110+ refs .append (
111+ {
112+ "path" : item_path ,
113+ "value" : item ,
114+ "kind" : "entity" ,
115+ }
116+ )
117+ return True
118+ return False
119+
120+
72121def extract_refs (config : Any ) -> WalkerResult :
73122 """Pull every literal service/entity reference out of *config*.
74123
@@ -97,43 +146,17 @@ def _walk(node: Any, path: str) -> None:
97146 sub_path = f"{ path } .{ key } " if path else key
98147
99148 if key in _SERVICE_KEYS and isinstance (value , str ):
100- if _is_template ( value ):
101- unvalidated_templates [ 0 ] += 1
102- else :
103- refs . append (
104- { "path" : sub_path , " value" : value , "kind" : "service" }
105- )
149+ _extract_service_ref ( sub_path , value , refs , unvalidated_templates )
150+ continue
151+
152+ if key in _ENTITY_KEYS and _extract_entity_refs (
153+ sub_path , value , refs , unvalidated_templates
154+ ):
106155 continue
107156
108- if key in _ENTITY_KEYS :
109- if isinstance (value , str ):
110- if _is_template (value ):
111- unvalidated_templates [0 ] += 1
112- else :
113- refs .append (
114- {"path" : sub_path , "value" : value , "kind" : "entity" }
115- )
116- continue
117- if isinstance (value , list ):
118- for i , item in enumerate (value ):
119- if not isinstance (item , str ):
120- continue
121- item_path = f"{ sub_path } [{ i } ]"
122- if _is_template (item ):
123- unvalidated_templates [0 ] += 1
124- else :
125- refs .append (
126- {
127- "path" : item_path ,
128- "value" : item ,
129- "kind" : "entity" ,
130- }
131- )
132- continue
133-
134- # Neither a service nor an entity key: recurse so deeply
135- # nested action blocks (choose/if/parallel/repeat) still
136- # get walked.
157+ # Neither a service nor an entity key (or an entity key whose
158+ # value is neither str nor list): recurse so deeply nested
159+ # action blocks (choose/if/parallel/repeat) still get walked.
137160 _walk (value , sub_path )
138161
139162 elif isinstance (node , list ):
0 commit comments