From 55b0172defaef3303bfcd73475508c691dbaedc8 Mon Sep 17 00:00:00 2001
From: Thor Whalen <1906276+thorwhalen@users.noreply.github.com>
Date: Tue, 20 Oct 2026 13:59:29 +0000
Subject: [PATCH] Scope the nested input keys of a kwargs input to that input
KwargsInputBase.pre_render named its get_kwargs function "get_kwargs", and
mk_input_element_specs derives input keys from the function name, so every
parameter inside any KwargsInput got the key get_kwargs_ and collided
across functions. Name the function after the kwargs input's input_key.
Fixes #40 (the MultiSourceInput shape it mentions is split out to #45)
Co-Authored-By: Claude Opus 5.5
---
front/elements/elements.py | 10 ++++++++-
tests/test_kwargs_input_keys.py | 40 +++++++++++++++++++++++++++++++++
2 files changed, 49 insertions(+), 1 deletion(-)
create mode 100644 tests/test_kwargs_input_keys.py
diff --git a/front/elements/elements.py b/front/elements/elements.py
index 287c87f..b44198d 100644
--- a/front/elements/elements.py
+++ b/front/elements/elements.py
@@ -630,7 +630,11 @@ class KwargsInputBase(InputBase):
func_sig: Sig | Callable = None
def pre_render(self):
- """Make ``self.get_kwargs``, a function with signature ``func_sig`` returning its kwargs."""
+ """Make ``self.get_kwargs``, a function with signature ``func_sig`` returning its kwargs.
+
+ It is named after ``input_key``, so the nested inputs get keys like
+ ``_``.
+ """
super().pre_render()
func_sig = self.func_sig if isinstance(self.func_sig, Sig) else self.func_sig()
func_sig = func_sig or Sig()
@@ -639,6 +643,10 @@ def pre_render(self):
def get_kwargs(**kwargs):
return kwargs
+ # The nested inputs' keys derive from this function's name (see
+ # ``mk_input_element_specs``): name it after this input so that they are
+ # scoped to it and do not collide across kwargs inputs (i2mint/front#40).
+ get_kwargs.__name__ = self.input_key or get_kwargs.__name__
self.get_kwargs = get_kwargs
def _return_kwargs(self, output):
diff --git a/tests/test_kwargs_input_keys.py b/tests/test_kwargs_input_keys.py
new file mode 100644
index 0000000..6f51753
--- /dev/null
+++ b/tests/test_kwargs_input_keys.py
@@ -0,0 +1,40 @@
+"""The nested inputs of a kwargs input are keyed by that input, not by ``get_kwargs``.
+
+Regression test for i2mint/front#40: every parameter inside a ``KwargsInput`` used
+to get the key ``get_kwargs_``, so two kwargs inputs sharing a parameter name
+shared their value, widget key and state.
+"""
+
+from dataclasses import dataclass
+from inspect import Parameter
+
+from i2 import Sig
+
+from front.data_binding import BoundData
+from front.elements.elements import KwargsInputBase, mk_input_element_specs
+
+
+@dataclass
+class _Kwargs(KwargsInputBase):
+ def render(self):
+ return None
+
+
+def _nested_input_keys(input_key, state):
+ kwargs_input = _Kwargs(
+ obj=Parameter("kwargs", Parameter.VAR_KEYWORD),
+ input_key=input_key,
+ func_sig=Sig("data"),
+ bound_data_factory=lambda k: BoundData(k, state),
+ )
+ kwargs_input.pre_render()
+ specs = mk_input_element_specs(kwargs_input.get_kwargs, {})
+ return {name: spec["input_key"] for name, spec in specs.items()}
+
+
+def test_nested_input_keys_are_scoped_to_the_kwargs_input():
+ state = {}
+ first = _nested_input_keys("foo_kwargs", state)
+ second = _nested_input_keys("bar_kwargs", state)
+ assert first == {"data": "foo_kwargs_data"}
+ assert second == {"data": "bar_kwargs_data"}