adf0d17497
publish / version_or_publish (push) Has been cancelled
storybook-build / changes (push) Has been cancelled
storybook-build / :storybook-build (push) Has been cancelled
Sync Gradio Skills to Hugging Face / sync-skills (push) Has been cancelled
functional / changes (push) Has been cancelled
functional / build-frontend (push) Has been cancelled
functional / functional-test-SSR=false (push) Has been cancelled
functional / functional-reload (push) Has been cancelled
js / changes (push) Has been cancelled
js / js-test (push) Has been cancelled
docs-build / changes (push) Has been cancelled
docs-build / docs-build (push) Has been cancelled
docs-build / website-build (push) Has been cancelled
functional / functional-test-SSR=true (push) Has been cancelled
hygiene / hygiene-test (push) Has been cancelled
python / changes (push) Has been cancelled
python / build (push) Has been cancelled
python / test-ubuntu-latest-flaky (push) Has been cancelled
python / test-ubuntu-latest-not-flaky (push) Has been cancelled
python / test-windows-latest-flaky (push) Has been cancelled
python / test-windows-latest-not-flaky (push) Has been cancelled
535 lines
18 KiB
Python
535 lines
18 KiB
Python
from datetime import datetime
|
|
|
|
import numpy as np
|
|
import pandas as pd
|
|
import polars as pl
|
|
import pytest
|
|
|
|
import gradio as gr
|
|
from gradio.components.dataframe import DataframeData
|
|
|
|
|
|
class TestDataframe:
|
|
def test_component_functions(self):
|
|
"""
|
|
Preprocess, serialize, get_config
|
|
"""
|
|
x_data = {
|
|
"data": [["Tim", 12, False], ["Jan", 24, True]],
|
|
"headers": ["Name", "Age", "Member"],
|
|
"metadata": None,
|
|
}
|
|
x_payload = DataframeData(**x_data) # type: ignore
|
|
dataframe_input = gr.Dataframe(headers=["Name", "Age", "Member"])
|
|
output = dataframe_input.preprocess(x_payload)
|
|
assert output["Age"][1] == 24 # type: ignore
|
|
assert not output["Member"][0] # type: ignore
|
|
assert dataframe_input.postprocess(output) == x_payload
|
|
|
|
dataframe_input = gr.Dataframe(
|
|
headers=["Name", "Age", "Member"], label="Dataframe Input"
|
|
)
|
|
assert dataframe_input.get_config() == {
|
|
"value": {
|
|
"headers": ["Name", "Age", "Member"],
|
|
"data": [],
|
|
"metadata": None,
|
|
},
|
|
"_selectable": False,
|
|
"key": None,
|
|
"preserved_by_key": ["value"],
|
|
"headers": ["Name", "Age", "Member"],
|
|
"row_count": [3, "dynamic"],
|
|
"row_limits": None,
|
|
"col_count": [3, "dynamic"],
|
|
"column_count": [3, "dynamic"],
|
|
"column_limits": None,
|
|
"datatype": "str",
|
|
"type": "pandas",
|
|
"label": "Dataframe Input",
|
|
"show_label": True,
|
|
"scale": None,
|
|
"min_width": 160,
|
|
"interactive": None,
|
|
"visible": True,
|
|
"elem_id": None,
|
|
"elem_classes": [],
|
|
"show_row_numbers": False,
|
|
"show_search": "none",
|
|
"static_columns": [],
|
|
"pinned_columns": None,
|
|
"wrap": False,
|
|
"proxy_url": None,
|
|
"name": "dataframe",
|
|
"max_height": 500,
|
|
"latex_delimiters": [{"display": True, "left": "$$", "right": "$$"}],
|
|
"line_breaks": True,
|
|
"column_widths": [],
|
|
"buttons": None,
|
|
"max_chars": None,
|
|
}
|
|
dataframe_input = gr.Dataframe()
|
|
output = dataframe_input.preprocess(DataframeData(**x_data)) # type: ignore
|
|
assert output["Age"][1] == 24 # type: ignore
|
|
|
|
x_data = {
|
|
"data": [["Tim", 12, False], ["Jan", 24, True]],
|
|
"headers": ["Name", "Age", "Member"],
|
|
"metadata": {"display_value": None, "styling": None},
|
|
}
|
|
dataframe_input.preprocess(DataframeData(**x_data)) # type: ignore
|
|
|
|
with pytest.raises(ValueError):
|
|
gr.Dataframe(type="unknown") # type: ignore
|
|
|
|
dataframe_output = gr.Dataframe()
|
|
assert dataframe_output.get_config() == {
|
|
"value": {
|
|
"headers": ["1", "2", "3"],
|
|
"data": [],
|
|
"metadata": None,
|
|
},
|
|
"_selectable": False,
|
|
"key": None,
|
|
"preserved_by_key": ["value"],
|
|
"headers": ["1", "2", "3"],
|
|
"row_count": [3, "dynamic"],
|
|
"row_limits": None,
|
|
"col_count": [3, "dynamic"],
|
|
"column_count": [3, "dynamic"],
|
|
"column_limits": None,
|
|
"datatype": "str",
|
|
"type": "pandas",
|
|
"label": None,
|
|
"show_label": True,
|
|
"show_row_numbers": False,
|
|
"show_search": "none",
|
|
"static_columns": [],
|
|
"pinned_columns": None,
|
|
"scale": None,
|
|
"min_width": 160,
|
|
"interactive": None,
|
|
"visible": True,
|
|
"elem_id": None,
|
|
"elem_classes": [],
|
|
"wrap": False,
|
|
"proxy_url": None,
|
|
"name": "dataframe",
|
|
"max_height": 500,
|
|
"latex_delimiters": [{"display": True, "left": "$$", "right": "$$"}],
|
|
"line_breaks": True,
|
|
"column_widths": [],
|
|
"buttons": None,
|
|
"max_chars": None,
|
|
}
|
|
|
|
dataframe_input = gr.Dataframe(column_widths=["100px", 200, "50%"])
|
|
assert dataframe_input.get_config()["column_widths"] == [
|
|
"100px",
|
|
"200px",
|
|
"50%",
|
|
]
|
|
|
|
def test_postprocess(self):
|
|
"""
|
|
postprocess
|
|
"""
|
|
dataframe_output = gr.Dataframe()
|
|
output = dataframe_output.postprocess(np.zeros((2, 2))).model_dump()
|
|
assert output == {
|
|
"data": [[0, 0], [0, 0]],
|
|
"headers": ["1", "2"],
|
|
"metadata": None,
|
|
}
|
|
output = dataframe_output.postprocess([[1, 3, 5]]).model_dump()
|
|
assert output == {
|
|
"data": [[1, 3, 5]],
|
|
"headers": ["1", "2", "3"],
|
|
"metadata": None,
|
|
}
|
|
output = dataframe_output.postprocess(
|
|
pd.DataFrame([[2, True], [3, True], [4, False]], columns=["num", "prime"]) # type: ignore
|
|
).model_dump()
|
|
assert output == {
|
|
"headers": ["num", "prime"],
|
|
"data": [[2, True], [3, True], [4, False]],
|
|
"metadata": None,
|
|
}
|
|
with pytest.raises(ValueError):
|
|
gr.Dataframe(type="unknown") # type: ignore
|
|
|
|
# When the headers don't match the data
|
|
dataframe_output = gr.Dataframe(headers=["one", "two", "three"])
|
|
output = dataframe_output.postprocess([[2, True], [3, True]]).model_dump()
|
|
assert output == {
|
|
"headers": ["one", "two"],
|
|
"data": [[2, True], [3, True]],
|
|
"metadata": None,
|
|
}
|
|
dataframe_output = gr.Dataframe(headers=["one", "two", "three"])
|
|
output = dataframe_output.postprocess(
|
|
[[2, True, "ab", 4], [3, True, "cd", 5]]
|
|
).model_dump()
|
|
assert output == {
|
|
"headers": ["one", "two", "three", "4"],
|
|
"data": [[2, True, "ab", 4], [3, True, "cd", 5]],
|
|
"metadata": None,
|
|
}
|
|
|
|
dataframe_output = gr.Dataframe(headers=["one", "two", "three"])
|
|
output = dataframe_output.postprocess([(1, 2, 3), (4, 5, 6)]).model_dump()
|
|
assert output == {
|
|
"headers": ["one", "two", "three"],
|
|
"data": [[1, 2, 3], [4, 5, 6]],
|
|
"metadata": None,
|
|
}
|
|
|
|
def test_dataframe_postprocess_all_types(self):
|
|
df = pd.DataFrame(
|
|
{
|
|
"date_1": pd.date_range("2021-01-01", periods=2),
|
|
"date_2": pd.date_range("2022-02-15", periods=2).strftime(
|
|
"%B %d, %Y, %r"
|
|
),
|
|
"number": np.array([0.2233, 0.57281]),
|
|
"number_2": np.array([84, 23]).astype(np.int64),
|
|
"bool": [True, False],
|
|
"markdown": ["# Hello", "# Goodbye"],
|
|
}
|
|
)
|
|
component = gr.Dataframe(
|
|
datatype=["date", "date", "number", "number", "bool", "markdown"] # type: ignore
|
|
)
|
|
output = component.postprocess(df).model_dump()
|
|
assert output == {
|
|
"headers": list(df.columns),
|
|
"data": [
|
|
[
|
|
pd.Timestamp("2021-01-01 00:00:00"),
|
|
"February 15, 2022, 12:00:00 AM",
|
|
0.2233,
|
|
84,
|
|
True,
|
|
"# Hello",
|
|
],
|
|
[
|
|
pd.Timestamp("2021-01-02 00:00:00"),
|
|
"February 16, 2022, 12:00:00 AM",
|
|
0.57281,
|
|
23,
|
|
False,
|
|
"# Goodbye",
|
|
],
|
|
],
|
|
"metadata": None,
|
|
}
|
|
|
|
def test_dataframe_postprocess_only_dates(self):
|
|
df = pd.DataFrame(
|
|
{
|
|
"date_1": pd.date_range("2021-01-01", periods=2),
|
|
"date_2": pd.date_range("2022-02-15", periods=2),
|
|
}
|
|
)
|
|
component = gr.Dataframe(datatype=["date", "date"]) # type: ignore
|
|
output = component.postprocess(df).model_dump()
|
|
assert output == {
|
|
"headers": list(df.columns),
|
|
"data": [
|
|
[
|
|
pd.Timestamp("2021-01-01 00:00:00"),
|
|
pd.Timestamp("2022-02-15 00:00:00"),
|
|
],
|
|
[
|
|
pd.Timestamp("2021-01-02 00:00:00"),
|
|
pd.Timestamp("2022-02-16 00:00:00"),
|
|
],
|
|
],
|
|
"metadata": None,
|
|
}
|
|
|
|
def test_dataframe_postprocess_styler(self):
|
|
component = gr.Dataframe()
|
|
df = pd.DataFrame(
|
|
{
|
|
"name": ["Adam", "Mike"] * 4,
|
|
"gpa": [1.1, 1.12] * 4,
|
|
"sat": [800, 800] * 4,
|
|
}
|
|
)
|
|
s = df.style.format(precision=1, decimal=",")
|
|
output = component.postprocess(s).model_dump() # type: ignore
|
|
assert output == {
|
|
"data": [
|
|
["Adam", 1.1, 800],
|
|
["Mike", 1.12, 800],
|
|
["Adam", 1.1, 800],
|
|
["Mike", 1.12, 800],
|
|
["Adam", 1.1, 800],
|
|
["Mike", 1.12, 800],
|
|
["Adam", 1.1, 800],
|
|
["Mike", 1.12, 800],
|
|
],
|
|
"headers": ["name", "gpa", "sat"],
|
|
"metadata": {
|
|
"display_value": [
|
|
["Adam", "1,1", "800"],
|
|
["Mike", "1,1", "800"],
|
|
["Adam", "1,1", "800"],
|
|
["Mike", "1,1", "800"],
|
|
["Adam", "1,1", "800"],
|
|
["Mike", "1,1", "800"],
|
|
["Adam", "1,1", "800"],
|
|
["Mike", "1,1", "800"],
|
|
],
|
|
"styling": [
|
|
["", "", ""],
|
|
["", "", ""],
|
|
["", "", ""],
|
|
["", "", ""],
|
|
["", "", ""],
|
|
["", "", ""],
|
|
["", "", ""],
|
|
["", "", ""],
|
|
],
|
|
},
|
|
}
|
|
|
|
df = pd.DataFrame(
|
|
{
|
|
"A": [14, 4, 5, 4, 1],
|
|
"B": [5, 2, 54, 3, 2],
|
|
"C": [20, 20, 7, 3, 8],
|
|
"D": [14, 3, 6, 2, 6],
|
|
"E": [23, 45, 64, 32, 23],
|
|
}
|
|
)
|
|
|
|
t = df.style.highlight_max(color="lightgreen", axis=0)
|
|
output = component.postprocess(t).model_dump()
|
|
assert output == {
|
|
"data": [
|
|
[14, 5, 20, 14, 23],
|
|
[4, 2, 20, 3, 45],
|
|
[5, 54, 7, 6, 64],
|
|
[4, 3, 3, 2, 32],
|
|
[1, 2, 8, 6, 23],
|
|
],
|
|
"headers": ["A", "B", "C", "D", "E"],
|
|
"metadata": {
|
|
"display_value": [
|
|
["14", "5", "20", "14", "23"],
|
|
["4", "2", "20", "3", "45"],
|
|
["5", "54", "7", "6", "64"],
|
|
["4", "3", "3", "2", "32"],
|
|
["1", "2", "8", "6", "23"],
|
|
],
|
|
"styling": [
|
|
[
|
|
"background-color: lightgreen",
|
|
"",
|
|
"background-color: lightgreen",
|
|
"background-color: lightgreen",
|
|
"",
|
|
],
|
|
["", "", "background-color: lightgreen", "", ""],
|
|
[
|
|
"",
|
|
"background-color: lightgreen",
|
|
"",
|
|
"",
|
|
"background-color: lightgreen",
|
|
],
|
|
["", "", "", "", ""],
|
|
["", "", "", "", ""],
|
|
],
|
|
},
|
|
}
|
|
|
|
def test_dataframe_hidden_columns(self):
|
|
"""Test that hidden columns are properly excluded from the output"""
|
|
component = gr.Dataframe()
|
|
df = pd.DataFrame(
|
|
{"a": [1, 2, 3], "b": [4, 5, 6], "color": ["red", "blue", "green"]}
|
|
)
|
|
styled_df = df.style.hide(axis=1, subset=["color"])
|
|
output = component.postprocess(styled_df).model_dump()
|
|
assert output == {
|
|
"data": [
|
|
[1, 4],
|
|
[2, 5],
|
|
[3, 6],
|
|
],
|
|
"headers": ["a", "b"],
|
|
"metadata": {
|
|
"display_value": [
|
|
["1", "4"],
|
|
["2", "5"],
|
|
["3", "6"],
|
|
],
|
|
"styling": [
|
|
["", ""],
|
|
["", ""],
|
|
["", ""],
|
|
],
|
|
},
|
|
}
|
|
|
|
def test_is_empty(self):
|
|
"""Test is_empty method with various data types"""
|
|
df = gr.Dataframe()
|
|
assert df.is_empty([])
|
|
assert df.is_empty([[]])
|
|
assert df.is_empty(np.array([]))
|
|
assert df.is_empty(np.zeros((2, 0)))
|
|
assert df.is_empty(None)
|
|
assert df.is_empty({})
|
|
assert df.is_empty({"data": [], "headers": ["a", "b"]})
|
|
assert df.is_empty({"data": []})
|
|
assert not df.is_empty({"data": [1, 2]})
|
|
assert not df.is_empty([[1, 2], [3, 4]])
|
|
assert not df.is_empty(pd.DataFrame({"a": [1, 2]}))
|
|
assert not df.is_empty(pd.DataFrame({"a": [1, 2]}).style)
|
|
|
|
def test_get_headers(self):
|
|
"""Test get_headers method with various data types"""
|
|
df = gr.Dataframe()
|
|
test_df = pd.DataFrame({"col1": [1, 2], "col2": [3, 4]})
|
|
assert df.get_headers(test_df) == ["col1", "col2"]
|
|
assert df.get_headers(test_df.style) == ["col1", "col2"]
|
|
assert df.get_headers({"headers": ["a", "b"]}) == ["a", "b"]
|
|
assert df.get_headers(np.array([[1, 2], [3, 4]])) == []
|
|
assert df.get_headers(None) == []
|
|
|
|
def test_get_cell_data(self):
|
|
"""Test get_cell_data method with various data types"""
|
|
df = gr.Dataframe()
|
|
test_data = [[1, 2], [3, 4]]
|
|
test_df = pd.DataFrame({"col1": [1, 3], "col2": [2, 4]})
|
|
assert df.get_cell_data(test_data) == [[1, 2], [3, 4]]
|
|
assert df.get_cell_data(test_df) == [[1, 2], [3, 4]]
|
|
assert df.get_cell_data({"data": test_data}) == [[1, 2], [3, 4]]
|
|
|
|
styled_df = test_df.style
|
|
styled_df.hide(axis=1, subset=["col2"])
|
|
assert df.get_cell_data(styled_df) == [[1], [3]]
|
|
|
|
def test_static_columns(self):
|
|
# when static_columns is specified, it should be stored
|
|
dataframe = gr.Dataframe(static_columns=[0, 1])
|
|
assert dataframe.static_columns == [0, 1]
|
|
|
|
# when static_columns is specified with column_count
|
|
dataframe = gr.Dataframe(column_count=4, static_columns=[0, 1])
|
|
assert dataframe.static_columns == [0, 1]
|
|
assert dataframe.column_count == (4, "dynamic")
|
|
|
|
# when static_columns is empty
|
|
dataframe = gr.Dataframe(column_count=4, static_columns=[])
|
|
assert dataframe.static_columns == []
|
|
|
|
# when static_columns is None
|
|
dataframe = gr.Dataframe(column_count=4, static_columns=None)
|
|
assert dataframe.static_columns == []
|
|
|
|
# when static_columns is not specified at all
|
|
dataframe = gr.Dataframe(column_count=4)
|
|
assert dataframe.static_columns == []
|
|
|
|
def test_auto_datatype(self):
|
|
df_headers = [
|
|
"String",
|
|
"Int",
|
|
"Float",
|
|
"Pandas Time",
|
|
"Numpy Time",
|
|
"Datetime",
|
|
"Boolean",
|
|
]
|
|
|
|
list_data = [
|
|
[
|
|
"Irish Red Fox",
|
|
185000,
|
|
4.2,
|
|
pd.Timestamp("2017-01-01T12"),
|
|
np.datetime64("now"),
|
|
datetime(2022, 1, 1),
|
|
True,
|
|
],
|
|
[
|
|
"Irish Badger",
|
|
95000,
|
|
8.5,
|
|
pd.Timestamp("2018-01-01T12"),
|
|
np.datetime64("now"),
|
|
datetime(2023, 1, 1),
|
|
True,
|
|
],
|
|
[
|
|
"Irish Otter",
|
|
13500,
|
|
5.5,
|
|
pd.Timestamp("2025-01-01T12"),
|
|
np.datetime64("now"),
|
|
datetime(2024, 1, 1),
|
|
False,
|
|
],
|
|
]
|
|
np_data = np.array(list_data, dtype=object)
|
|
pl_data = pl.DataFrame(list_data, schema=df_headers)
|
|
|
|
pd_data = pd.DataFrame(list_data, columns=df_headers) # type: ignore
|
|
styler_data = pd_data.style.apply(
|
|
lambda row: [
|
|
"background-color: lightgreen" if row["Boolean"] else "" for _ in row
|
|
],
|
|
axis=1,
|
|
)
|
|
|
|
result = ["str", "number", "number", "date", "date", "date", "bool"]
|
|
|
|
dataframe = gr.Dataframe(
|
|
value=pd_data, headers=df_headers, interactive=True, datatype="auto"
|
|
)
|
|
assert dataframe.datatype == result
|
|
|
|
dataframe = gr.Dataframe(
|
|
value=list_data, headers=df_headers, interactive=True, datatype="auto"
|
|
)
|
|
assert dataframe.datatype == result
|
|
|
|
dataframe = gr.Dataframe(
|
|
value=np_data, headers=df_headers, interactive=True, datatype="auto"
|
|
)
|
|
assert dataframe.datatype == result
|
|
|
|
dataframe = gr.Dataframe(
|
|
value=styler_data, headers=df_headers, interactive=True, datatype="auto"
|
|
)
|
|
assert dataframe.datatype == result
|
|
|
|
dataframe = gr.Dataframe(
|
|
value=pl_data, headers=df_headers, interactive=True, datatype="auto"
|
|
)
|
|
result = ["str", "number", "number", "date", "str", "date", "bool"]
|
|
assert dataframe.datatype == result
|
|
|
|
dataframe = gr.Dataframe(value=[], datatype="auto")
|
|
assert dataframe.datatype == "str"
|
|
|
|
dataframe = gr.Dataframe(value=[1, 2, 3], datatype="auto")
|
|
assert dataframe.datatype == "str"
|
|
|
|
dataframe = gr.Dataframe(value=np.array([]), datatype="auto")
|
|
assert dataframe.datatype == "str"
|
|
|
|
dataframe = gr.Dataframe(value=np.array([1, 2, 3]), datatype="auto")
|
|
assert dataframe.datatype == "str"
|
|
|
|
dataframe = gr.Dataframe(value=[[1, 2], [3, 4]], datatype="auto")
|
|
assert dataframe.datatype == ["number", "number"]
|
|
|
|
dataframe = gr.Dataframe(value=np.array([[1, 2], [3, 4]]), datatype="auto")
|
|
assert dataframe.datatype == ["number", "number"]
|