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"}