maxfordham / ipyautoui

create ipywidgets user input form pydantic model or jsonschema
https://maxfordham.github.io/ipyautoui/
42 stars 4 forks source link

🐛 MultiIndexEditableGrid Demo not working #300

Open ollyhensby opened 6 months ago

ollyhensby commented 6 months ago

Describe the bug MultiIndexEditableGrid Demo failing to open:

image

See traceback below:

Command Line Output
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
File ~/miniforge3/envs/ipyautoui-dev/lib/python3.12/site-packages/ipywidgets/widgets/widget.py:773, in Widget._handle_msg(self, msg)
    771         if 'buffer_paths' in data:
    772             _put_buffers(state, data['buffer_paths'], msg['buffers'])
--> 773         self.set_state(state)
    775 # Handle a state request.
    776 elif method == 'request_state':

File ~/miniforge3/envs/ipyautoui-dev/lib/python3.12/site-packages/ipywidgets/widgets/widget.py:650, in Widget.set_state(self, sync_data)
    645         self._send(msg, buffers=echo_buffers)
    647 # The order of these context managers is important. Properties must
    648 # be locked when the hold_trait_notification context manager is
    649 # released and notifications are fired.
--> 650 with self._lock_property(**sync_data), self.hold_trait_notifications():
    651     for name in sync_data:
    652         if name in self.keys:

File ~/miniforge3/envs/ipyautoui-dev/lib/python3.12/contextlib.py:144, in _GeneratorContextManager.__exit__(self, typ, value, traceback)
    142 if typ is None:
    143     try:
--> 144         next(self.gen)
    145     except StopIteration:
    146         return False

File ~/miniforge3/envs/ipyautoui-dev/lib/python3.12/site-packages/traitlets/traitlets.py:1498, in HasTraits.hold_trait_notifications(self)
   1496 for changes in cache.values():
   1497     for change in changes:
-> 1498         self.notify_change(change)

File ~/miniforge3/envs/ipyautoui-dev/lib/python3.12/site-packages/ipywidgets/widgets/widget.py:701, in Widget.notify_change(self, change)
    698     if name in self.keys and self._should_send_property(name, getattr(self, name)):
    699         # Send new state to front-end
    700         self.send_state(key=name)
--> 701 super().notify_change(change)

File ~/miniforge3/envs/ipyautoui-dev/lib/python3.12/site-packages/traitlets/traitlets.py:1513, in HasTraits.notify_change(self, change)
   1511 def notify_change(self, change):
   1512     """Notify observers of a change event"""
-> 1513     return self._notify_observers(change)

File ~/miniforge3/envs/ipyautoui-dev/lib/python3.12/site-packages/traitlets/traitlets.py:1560, in HasTraits._notify_observers(self, event)
   1557 elif isinstance(c, EventHandler) and c.name is not None:
   1558     c = getattr(self, c.name)
-> 1560 c(event)

File ~/miniforge3/envs/ipyautoui-dev/lib/python3.12/site-packages/ipywidgets/widgets/widget_selection.py:236, in _Selection._propagate_index(self, change)
    234     self.label = label
    235 if self.value is not value:
--> 236     self.value = value

File ~/miniforge3/envs/ipyautoui-dev/lib/python3.12/site-packages/traitlets/traitlets.py:729, in TraitType.__set__(self, obj, value)
    727     raise TraitError('The "%s" trait is read-only.' % self.name)
    728 else:
--> 729     self.set(obj, value)

File ~/miniforge3/envs/ipyautoui-dev/lib/python3.12/site-packages/traitlets/traitlets.py:718, in TraitType.set(self, obj, value)
    714     silent = False
    715 if silent is not True:
    716     # we explicitly compare silent to True just in case the equality
    717     # comparison above returns something other than True/False
--> 718     obj._notify_trait(self.name, old_value, new_value)

File ~/miniforge3/envs/ipyautoui-dev/lib/python3.12/site-packages/traitlets/traitlets.py:1501, in HasTraits._notify_trait(self, name, old_value, new_value)
   1500 def _notify_trait(self, name, old_value, new_value):
-> 1501     self.notify_change(
   1502         Bunch(
   1503             name=name,
   1504             old=old_value,
   1505             new=new_value,
   1506             owner=self,
   1507             type="change",
   1508         )
   1509     )

File ~/miniforge3/envs/ipyautoui-dev/lib/python3.12/site-packages/ipywidgets/widgets/widget.py:701, in Widget.notify_change(self, change)
    698     if name in self.keys and self._should_send_property(name, getattr(self, name)):
    699         # Send new state to front-end
    700         self.send_state(key=name)
--> 701 super().notify_change(change)

File ~/miniforge3/envs/ipyautoui-dev/lib/python3.12/site-packages/traitlets/traitlets.py:1513, in HasTraits.notify_change(self, change)
   1511 def notify_change(self, change):
   1512     """Notify observers of a change event"""
-> 1513     return self._notify_observers(change)

File ~/miniforge3/envs/ipyautoui-dev/lib/python3.12/site-packages/traitlets/traitlets.py:1560, in HasTraits._notify_observers(self, event)
   1557 elif isinstance(c, EventHandler) and c.name is not None:
   1558     c = getattr(self, c.name)
-> 1560 c(event)

File ~/git/MXF/ipyautoui/src/ipyautoui/demo.py:212, in DemoReel._update_demo(self, on_change)
    210 model = self.map_name_pydantic_model[self.select.value]
    211 if self.demo.pydantic_model != model:
--> 212     self.demo.pydantic_model = model

File ~/miniforge3/envs/ipyautoui-dev/lib/python3.12/site-packages/traitlets/traitlets.py:729, in TraitType.__set__(self, obj, value)
    727     raise TraitError('The "%s" trait is read-only.' % self.name)
    728 else:
--> 729     self.set(obj, value)

File ~/miniforge3/envs/ipyautoui-dev/lib/python3.12/site-packages/traitlets/traitlets.py:718, in TraitType.set(self, obj, value)
    714     silent = False
    715 if silent is not True:
    716     # we explicitly compare silent to True just in case the equality
    717     # comparison above returns something other than True/False
--> 718     obj._notify_trait(self.name, old_value, new_value)

File ~/miniforge3/envs/ipyautoui-dev/lib/python3.12/site-packages/traitlets/traitlets.py:1501, in HasTraits._notify_trait(self, name, old_value, new_value)
   1500 def _notify_trait(self, name, old_value, new_value):
-> 1501     self.notify_change(
   1502         Bunch(
   1503             name=name,
   1504             old=old_value,
   1505             new=new_value,
   1506             owner=self,
   1507             type="change",
   1508         )
   1509     )

File ~/miniforge3/envs/ipyautoui-dev/lib/python3.12/site-packages/ipywidgets/widgets/widget.py:701, in Widget.notify_change(self, change)
    698     if name in self.keys and self._should_send_property(name, getattr(self, name)):
    699         # Send new state to front-end
    700         self.send_state(key=name)
--> 701 super().notify_change(change)

File ~/miniforge3/envs/ipyautoui-dev/lib/python3.12/site-packages/traitlets/traitlets.py:1513, in HasTraits.notify_change(self, change)
   1511 def notify_change(self, change):
   1512     """Notify observers of a change event"""
-> 1513     return self._notify_observers(change)

File ~/miniforge3/envs/ipyautoui-dev/lib/python3.12/site-packages/traitlets/traitlets.py:1560, in HasTraits._notify_observers(self, event)
   1557 elif isinstance(c, EventHandler) and c.name is not None:
   1558     c = getattr(self, c.name)
-> 1560 c(event)

File ~/git/MXF/ipyautoui/src/ipyautoui/demo.py:52, in Demo._observe_pydantic_model(self, change)
     47 except:
     48     raise ValueError(
     49         "for the Demo to work, the `pydantic_model` must"
     50         " be defined in a separate python file."
     51     )
---> 52 self._update_autoui()
     53 self._update_pydantic()
     54 self._update_jsonschema()

File ~/git/MXF/ipyautoui/src/ipyautoui/demo.py:106, in Demo._update_autoui(self)
    105 def _update_autoui(self):
--> 106     self.autoui = AutoUi(self.pydantic_model)
    107     self._update_pycall()
    108     self.vbx_autoui.children = [self.vbx_pycall, self.autoui ]

File ~/git/MXF/ipyautoui/src/ipyautoui/autoui.py:285, in autoui(schema, value, path, **kwargs)
    283 ui = get_autoui(schema, **kwargs)  # TODO: resolve how path is handled
    284 if value is None:
--> 285     return ui(**kwargs)
    286 else:
    287     return ui(value=value, **kwargs)

File ~/git/MXF/ipyautoui/src/ipyautoui/custom/editgrid.py:291, in EditGrid.from_pydantic_model(cls, model, **kwargs)
    289 @classmethod
    290 def from_pydantic_model(cls, model: ty.Type[BaseModel], **kwargs):
--> 291     return cls(model, **kwargs)

File ~/git/MXF/ipyautoui/src/ipyautoui/custom/editgrid.py:326, in EditGrid.__init__(self, schema, value, by_alias, by_title, datahandler, ui_add, ui_edit, ui_delete, ui_copy, warn_on_delete, show_copy_dialogue, close_crud_dialogue_on_action, title, description, show_title, **kwargs)
    322 self.datahandler = datahandler
    323 getvalue = lambda value: (
    324     None if value is None or value == [{}] else pd.DataFrame(value)
    325 )
--> 326 self.grid = AutoGrid(
    327     schema, data=getvalue(value), by_alias=self.by_alias, **kwargs
    328 )
    330 self._init_form()
    331 if ui_add is None:

File ~/git/MXF/ipyautoui/src/ipyautoui/custom/autogrid.py:713, in AutoGrid.__init__(self, schema, data, by_alias, by_title, order, **kwargs)
    711 self.by_title = by_title
    712 self.selection_mode = MAP_TRANSPOSED_SELECTION_MODE[self.transposed]
--> 713 self.model, self.schema = asch._init_model_schema(schema, by_alias=by_alias)
    714 # ^ generates gridschema
    715 self.gridschema.get_traits = self.datagrid_trait_names

File ~/miniforge3/envs/ipyautoui-dev/lib/python3.12/site-packages/traitlets/traitlets.py:729, in TraitType.__set__(self, obj, value)
    727     raise TraitError('The "%s" trait is read-only.' % self.name)
    728 else:
--> 729     self.set(obj, value)

File ~/miniforge3/envs/ipyautoui-dev/lib/python3.12/site-packages/traitlets/traitlets.py:718, in TraitType.set(self, obj, value)
    714     silent = False
    715 if silent is not True:
    716     # we explicitly compare silent to True just in case the equality
    717     # comparison above returns something other than True/False
--> 718     obj._notify_trait(self.name, old_value, new_value)

File ~/miniforge3/envs/ipyautoui-dev/lib/python3.12/site-packages/traitlets/traitlets.py:1501, in HasTraits._notify_trait(self, name, old_value, new_value)
   1500 def _notify_trait(self, name, old_value, new_value):
-> 1501     self.notify_change(
   1502         Bunch(
   1503             name=name,
   1504             old=old_value,
   1505             new=new_value,
   1506             owner=self,
   1507             type="change",
   1508         )
   1509     )

File ~/miniforge3/envs/ipyautoui-dev/lib/python3.12/site-packages/ipywidgets/widgets/widget.py:701, in Widget.notify_change(self, change)
    698     if name in self.keys and self._should_send_property(name, getattr(self, name)):
    699         # Send new state to front-end
    700         self.send_state(key=name)
--> 701 super().notify_change(change)

File ~/miniforge3/envs/ipyautoui-dev/lib/python3.12/site-packages/traitlets/traitlets.py:1513, in HasTraits.notify_change(self, change)
   1511 def notify_change(self, change):
   1512     """Notify observers of a change event"""
-> 1513     return self._notify_observers(change)

File ~/miniforge3/envs/ipyautoui-dev/lib/python3.12/site-packages/traitlets/traitlets.py:1560, in HasTraits._notify_observers(self, event)
   1557 elif isinstance(c, EventHandler) and c.name is not None:
   1558     c = getattr(self, c.name)
-> 1560 c(event)

File ~/git/MXF/ipyautoui/src/ipyautoui/custom/autogrid.py:624, in AutoGrid._update_from_schema(self, change)
    622 @tr.observe("schema")
    623 def _update_from_schema(self, change):
--> 624     self.gridschema = GridSchema(self.schema, **self.kwargs)

File ~/git/MXF/ipyautoui/src/ipyautoui/custom/autogrid.py:206, in GridSchema.__init__(self, schema, get_traits, **kwargs)
    203 self.get_traits = get_traits
    205 self.map_name_index = self.get_map_name_index()
--> 206 self.map_index_name = {v: k for k, v in self.map_name_index.items()}
    208 self.set_renderers(**kwargs)
    210 self.column_widths = get_column_widths_from_schema(
    211     schema, self.properties, self.map_name_index, **kwargs
    212 )

TypeError: unhashable type: 'list'