소스 검색

bugfix/723: pc.data_table not working well with pandas as computed vars (#745)

* bugfix/723: pc.data_table not working well with pandas as computed vars

Turns out this isnt really a bug. You need to specify an annotation for the computed var to work. This Pr adds a check for annotations on computed vars for data tables

* added more validation for column field
Elijah Ahianyo 2 년 전
부모
커밋
50cdfac24c
3개의 변경된 파일122개의 추가작업 그리고 2개의 파일을 삭제
  1. 15 2
      pynecone/components/datadisplay/datatable.py
  2. 66 0
      tests/components/datadisplay/conftest.py
  3. 41 0
      tests/components/datadisplay/test_datatable.py

+ 15 - 2
pynecone/components/datadisplay/datatable.py

@@ -5,7 +5,7 @@ from typing import Any, List, Optional
 from pynecone.components.component import Component
 from pynecone.components.component import Component
 from pynecone.components.tags import Tag
 from pynecone.components.tags import Tag
 from pynecone.utils import format, imports, types
 from pynecone.utils import format, imports, types
-from pynecone.var import BaseVar, Var
+from pynecone.var import BaseVar, ComputedVar, Var
 
 
 
 
 class Gridjs(Component):
 class Gridjs(Component):
@@ -61,6 +61,19 @@ class DataTable(Gridjs):
             ValueError: If a pandas dataframe is passed in and columns are also provided.
             ValueError: If a pandas dataframe is passed in and columns are also provided.
         """
         """
         data = props.get("data")
         data = props.get("data")
+        columns = props.get("columns")
+
+        # The annotation should be provided if data is a computed var. We need this to know how to
+        # render pandas dataframes.
+        if isinstance(data, ComputedVar) and data.type_ == Any:
+            raise ValueError(
+                "Annotation of the computed var assigned to the data field should be provided."
+            )
+
+        if columns and isinstance(columns, ComputedVar) and columns.type_ == Any:
+            raise ValueError(
+                "Annotation of the computed var assigned to the column field should be provided."
+            )
 
 
         # If data is a pandas dataframe and columns are provided throw an error.
         # If data is a pandas dataframe and columns are provided throw an error.
         if (
         if (
@@ -73,7 +86,7 @@ class DataTable(Gridjs):
 
 
         # If data is a list and columns are not provided, throw an error
         # If data is a list and columns are not provided, throw an error
         if (
         if (
-            (isinstance(data, Var) and issubclass(data.type_, List))
+            (isinstance(data, Var) and types._issubclass(data.type_, List))
             or issubclass(type(data), List)
             or issubclass(type(data), List)
         ) and not props.get("columns"):
         ) and not props.get("columns"):
             raise ValueError(
             raise ValueError(

+ 66 - 0
tests/components/datadisplay/conftest.py

@@ -1,5 +1,7 @@
 """Data display component tests fixtures."""
 """Data display component tests fixtures."""
+from typing import List
 
 
+import pandas as pd
 import pytest
 import pytest
 
 
 import pynecone as pc
 import pynecone as pc
@@ -21,3 +23,67 @@ def data_table_state(request):
         columns = ["column1", "column2"]
         columns = ["column1", "column2"]
 
 
     return DataTableState
     return DataTableState
+
+
+@pytest.fixture
+def data_table_state2():
+    """Get a data table state.
+
+    Returns:
+        The data table state class.
+    """
+
+    class DataTableState(pc.State):
+        _data = pd.DataFrame()
+
+        @pc.var  # type: ignore
+        def data(self):
+            return self._data
+
+    return DataTableState
+
+
+@pytest.fixture
+def data_table_state3():
+    """Get a data table state.
+
+    Returns:
+        The data table state class.
+    """
+
+    class DataTableState(pc.State):
+        _data: List = []
+        _columns: List = ["col1", "col2"]
+
+        @pc.var  # type: ignore
+        def data(self) -> List:
+            return self._data
+
+        @pc.var  # type: ignore
+        def columns(self):
+            return self._columns
+
+    return DataTableState
+
+
+@pytest.fixture
+def data_table_state4():
+    """Get a data table state.
+
+    Returns:
+        The data table state class.
+    """
+
+    class DataTableState(pc.State):
+        _data: List = []
+        _columns: List = ["col1", "col2"]
+
+        @pc.var  # type: ignore
+        def data(self):
+            return self._data
+
+        @pc.var  # type: ignore
+        def columns(self) -> List:
+            return self._columns
+
+    return DataTableState

+ 41 - 0
tests/components/datadisplay/test_datatable.py

@@ -62,3 +62,44 @@ def test_invalid_props(props):
     """
     """
     with pytest.raises(ValueError):
     with pytest.raises(ValueError):
         data_table(**props)
         data_table(**props)
+
+
+@pytest.mark.parametrize(
+    "fixture, err_msg, is_data_frame",
+    [
+        (
+            "data_table_state2",
+            "Annotation of the computed var assigned to the data field should be provided.",
+            True,
+        ),
+        (
+            "data_table_state3",
+            "Annotation of the computed var assigned to the column field should be provided.",
+            False,
+        ),
+        (
+            "data_table_state4",
+            "Annotation of the computed var assigned to the data field should be provided.",
+            False,
+        ),
+    ],
+)
+def test_computed_var_without_annotation(fixture, request, err_msg, is_data_frame):
+    """Test if value error is thrown when the computed var assigned to the data/column prop is not annotated.
+
+    Args:
+        fixture: the state.
+        request: fixture request.
+        err_msg: expected error message.
+        is_data_frame: whether data field is a pandas dataframe.
+    """
+    with pytest.raises(ValueError) as err:
+
+        if is_data_frame:
+            data_table(data=request.getfixturevalue(fixture).data)
+        else:
+            data_table(
+                data=request.getfixturevalue(fixture).data,
+                columns=request.getfixturevalue(fixture).columns,
+            )
+    assert err.value.args[0] == err_msg