Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion front/elements/elements.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
``<input_key>_<param>``.
"""
super().pre_render()
func_sig = self.func_sig if isinstance(self.func_sig, Sig) else self.func_sig()
func_sig = func_sig or Sig()
Expand All @@ -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):
Expand Down
40 changes: 40 additions & 0 deletions tests/test_kwargs_input_keys.py
Original file line number Diff line number Diff line change
@@ -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_<param>``, 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"}
Loading