Internal: materialize this template into a concrete Node instance. Graph/BaseGraph should call this during create_node(), so Nodes are always registered inside a Graph.
(
self,
*,
name: str,
instantiate: Callable[..., Node],
creation_path: tuple[str, ...] | None = None,
**override_kwargs: Any,
)
| 309 | return self.clone(**override_kwargs) |
| 310 | |
| 311 | def _materialize( |
| 312 | self, |
| 313 | *, |
| 314 | name: str, |
| 315 | instantiate: Callable[..., Node], |
| 316 | creation_path: tuple[str, ...] | None = None, |
| 317 | **override_kwargs: Any, |
| 318 | ) -> T: |
| 319 | """ |
| 320 | Internal: materialize this template into a concrete Node instance. |
| 321 | |
| 322 | Graph/BaseGraph should call this during create_node(), so Nodes are always registered inside a Graph. |
| 323 | """ |
| 324 | final_config = self.render_config(**override_kwargs) |
| 325 | |
| 326 | accepted_kw_names, accepts_var_kwargs = _accepted_init_kwargs(self.node_cls) |
| 327 | |
| 328 | def can_inject(key: str) -> bool: |
| 329 | if key == "name": |
| 330 | return False |
| 331 | return accepts_var_kwargs or key in accepted_kw_names |
| 332 | |
| 333 | memo: dict[int, Any] = {} |
| 334 | |
| 335 | # Apply selector defaults (fill missing only, higher than global defaults). |
| 336 | for sel, pf, cfg in reversed(_TEMPLATE_DEFAULTS_RULES_CVAR.get()): |
| 337 | if not sel.match_declaration(name=name, cls=self.node_cls): |
| 338 | continue |
| 339 | if pf is not None: |
| 340 | if creation_path is None or not match_path_filter(pf, creation_path): |
| 341 | continue |
| 342 | for key, value in cfg.items(): |
| 343 | if key not in final_config and can_inject(key): |
| 344 | final_config[key] = _safe_clone(value, memo) |
| 345 | |
| 346 | # Apply global defaults (fill missing only). |
| 347 | scoped_defaults = _TEMPLATE_DEFAULTS_CVAR.get() |
| 348 | for key, value in scoped_defaults.items(): |
| 349 | if key not in final_config and can_inject(key): |
| 350 | final_config[key] = _safe_clone(value, memo) |
| 351 | |
| 352 | # Apply global overrides (force overwrite). |
| 353 | scoped_overrides = _TEMPLATE_OVERRIDES_CVAR.get() |
| 354 | for key, value in scoped_overrides.items(): |
| 355 | if can_inject(key): |
| 356 | final_config[key] = _safe_clone(value, memo) |
| 357 | |
| 358 | # Apply selector overrides (force overwrite, higher than global overrides). |
| 359 | for sel, pf, cfg in _TEMPLATE_OVERRIDES_RULES_CVAR.get(): |
| 360 | if not sel.match_declaration(name=name, cls=self.node_cls): |
| 361 | continue |
| 362 | if pf is not None: |
| 363 | if creation_path is None or not match_path_filter(pf, creation_path): |
| 364 | continue |
| 365 | for key, value in cfg.items(): |
| 366 | if can_inject(key): |
| 367 | final_config[key] = _safe_clone(value, memo) |
| 368 | return instantiate(self.node_cls, name=name, **final_config) # type: ignore |
no test coverage detected