3535DataFrameResult = Union [None , "pd.DataFrame" , "DataGrid" , "DataTable" ]
3636
3737
38- class OnCellUpdateFn (Protocol ):
38+ class CellUpdateFn (Protocol ):
3939 async def __call__ (
4040 self ,
4141 * ,
42- row_index : int ,
43- column_index : int ,
44- value : str ,
45- prev : str ,
42+ info : CellUpdateInfo ,
43+ # TODO-barret; Remove `**kwargs`
4644 ** kwargs : Any , # future proofing
4745 ) -> Any : ...
48- class OnCellUpdateParams (TypedDict ):
46+
47+
48+ class CellUpdateInfo (TypedDict ):
4949 row_index : int
5050 column_index : int
5151 value : str
5252 prev : str
5353
5454
55- class OnCellsUpdateFn (Protocol ):
55+ class CellsUpdateFn (Protocol ):
5656 async def __call__ (
5757 self ,
5858 * ,
59- update_infos : list [OnCellUpdateParams ],
59+ infos : list [CellUpdateInfo ],
60+ # TODO-barret; Remove `**kwargs`
6061 ** kwargs : Any , # future proofing
6162 ) -> Any : ...
6263@dataclass
@@ -403,8 +404,8 @@ class data_frame(Renderer[DataFrameResult]):
403404 _value : reactive .Value [DataFrameResult | None ]
404405 # _data: reactive.Value[pd.DataFrame | None]
405406
406- handle_cell_update : OnCellUpdateFn
407- handle_cells_update : OnCellsUpdateFn
407+ handle_cell_update : CellUpdateFn
408+ handle_cells_update : CellsUpdateFn
408409
409410 cell_patches : reactive .Value [list [CellPatch ]]
410411
@@ -455,6 +456,7 @@ def _init_reactives(self) -> None:
455456
456457 # Init
457458 self ._value : reactive .Value [Union [DataFrameResult , None ]] = reactive .Value (None )
459+ self .cell_patches : reactive .Value [list [CellPatch ]] = reactive .Value ([])
458460
459461 @reactive .calc
460462 def self_data () -> pd .DataFrame :
@@ -514,8 +516,6 @@ def self_data_selected() -> pd.DataFrame:
514516
515517 self .data_selected = self_data_selected
516518
517- self .cell_patches : reactive .Value [list [CellPatch ]] = reactive .Value ([])
518-
519519 @reactive .calc
520520 def self_data_patched () -> pd .DataFrame :
521521 data = self .data ()
@@ -537,44 +537,46 @@ def _get_session(self) -> Session:
537537 )
538538 return self ._session
539539
540- def on_cell_update (self , fn : OnCellUpdateFn ) -> Self :
540+ # TODO-barret; `on` sounds like a registration. Maybe use `set` instead? `set_cell_update_fn`?
541+ def on_cell_update (self , fn : CellUpdateFn ) -> Self :
541542 self .handle_cell_update = fn
542543 return self
543544
544- def on_cells_update (self , fn : OnCellsUpdateFn ) -> Self :
545+ def on_cells_update (self , fn : CellsUpdateFn ) -> Self :
545546 self .handle_cells_update = fn
546547 return self
547548
548549 def _init_handlers (self ) -> None :
549550 async def _on_cell_update_default (
550551 * ,
551- row_index : int ,
552- column_index : int ,
553- value : str ,
554- prev : str ,
552+ info : CellUpdateInfo ,
553+ # row_index: int,
554+ # column_index: int,
555+ # value: str,
556+ # prev: str,
555557 ** kwargs : Any ,
556558 ) -> str :
557- return value
559+ return info [ " value" ]
558560
559561 async def _on_cells_update_default (
560562 * ,
561- update_infos : list [OnCellUpdateParams ],
563+ infos : list [CellUpdateInfo ],
562564 ** kwargs : Any ,
563565 ):
564566 with reactive .isolate ():
565567 formatted_values : list [Any ] = []
566- for update_info in update_infos :
567- row_index = update_info ["row_index" ]
568- column_index = update_info ["column_index" ]
569- value = update_info ["value" ]
570- prev = update_info ["prev" ]
571-
572- formatted_value = await self .handle_cell_update (
573- row_index = row_index ,
574- column_index = column_index ,
575- value = value ,
576- prev = prev ,
577- )
568+ for update_info in infos :
569+ # row_index = update_info["row_index"]
570+ # column_index = update_info["column_index"]
571+ # value = update_info["value"]
572+ # prev = update_info["prev"]
573+
574+ formatted_value = await self .handle_cell_update (info = update_info )
575+ # row_index=row_index,
576+ # column_index=column_index,
577+ # value=value,
578+ # prev=prev,
579+ # )
578580 # TODO-barret; check type here?
579581 # TODO-barret; The return value should be coerced by pandas to the correct type
580582 formatted_values .append (formatted_value )
@@ -587,43 +589,45 @@ async def _on_cells_update_default(
587589
588590 # To be called by session's outputRPC message handler on this data_frame
589591 # Do not change this method unless you update corresponding code in `/js/dataframe/`!!
590- async def _handle_cells_update (self , update_infos : list [OnCellUpdateParams ]):
591- with session_context (self ._get_session ()):
592- with reactive .isolate ():
593- # Make new array to trigger reactive update
594- patches = [p for p in self .cell_patches ()]
592+ # @outputRPC_handler
593+ async def _handle_cells_update (self , update_infos : list [CellUpdateInfo ]):
594+
595+ # if len(self.cell_patches()) > 1:
596+ # raise RuntimeError("Barret testing!")
597+
598+ # Make new array to trigger reactive update
599+ patches = [p for p in self .cell_patches ()]
595600
596- # Call on_cells_update
597- formatted_values = await self .handle_cells_update (
598- update_infos = update_infos
601+ # Call user's on_cells_update method to retrieve formatted values
602+ # TODO-barret; REname `formatted` to `raw_values`
603+ formatted_values = await self .handle_cells_update (infos = update_infos )
604+
605+ if len (formatted_values ) != len (update_infos ):
606+ raise ValueError (
607+ f"The return value of { self .output_id } 's `handle_cells_update()` (typically set by `@{ self .output_id } .on_cells_update`) must be a list of the same length as the input list of cell updates. Received { len (formatted_values )} items and expected { len (update_infos )} ."
608+ )
609+
610+ # Add new patches
611+ for formatted_value , update_info in zip (formatted_values , update_infos ):
612+ patches .append (
613+ CellPatch (
614+ row_index = update_info ["row_index" ],
615+ column_index = update_info ["column_index" ],
616+ value = formatted_value ,
617+ prev = update_info ["prev" ],
599618 )
619+ )
600620
601- if len (formatted_values ) != len (update_infos ):
602- raise ValueError (
603- f"The return value of { self .output_id } 's `handle_cells_update()` (typically set by `@{ self .output_id } .on_cells_update`) must be a list of the same length as the input list of cell updates. Received { len (formatted_values )} items and expected { len (update_infos )} ."
604- )
605-
606- # Add new patches
607- for formatted_value , update_info in zip (formatted_values , update_infos ):
608- patches .append (
609- CellPatch (
610- row_index = update_info ["row_index" ],
611- column_id = update_info ["column_id" ],
612- value = formatted_value ,
613- prev = update_info ["prev" ],
614- )
615- )
616-
617- # Remove duplicate patches
618- patch_map : dict [tuple [int , str ], CellPatch ] = {}
619- for patch in patches :
620- patch_map [(patch .row_index , patch .column_id )] = patch
621- patches = list (patch_map .values ())
622-
623- # Set new patches
624- self .cell_patches .set (patches )
621+ # Remove duplicate patches
622+ patch_map : dict [tuple [int , int ], CellPatch ] = {}
623+ for patch in patches :
624+ patch_map [(patch .row_index , patch .column_index )] = patch
625+ patches = list (patch_map .values ())
625626
626- return formatted_values
627+ # Set new patches
628+ self .cell_patches .set (patches )
629+
630+ return formatted_values
627631
628632 def auto_output_ui (self ) -> Tag :
629633 return ui .output_data_frame (id = self .output_id )
@@ -661,11 +665,10 @@ def _set_output_metadata(self, *, output_id: str) -> None:
661665 async def render (self ) -> Jsonifiable :
662666 # Reset value
663667 self ._value .set (None )
668+ self .cell_patches .set ([])
664669
665670 value = await self .fn ()
666671 if value is None :
667- # Quit early
668- self ._value .set (None )
669672 return None
670673
671674 if not isinstance (value , AbstractTabularData ):
0 commit comments