diff --git a/shiny/_typing_extensions.py b/shiny/_typing_extensions.py index 164b00c5f..a158d0533 100644 --- a/shiny/_typing_extensions.py +++ b/shiny/_typing_extensions.py @@ -7,6 +7,7 @@ "Concatenate", "ParamSpec", "TypeGuard", + "TypeIs", "Never", "Required", "NotRequired", @@ -43,9 +44,13 @@ assert_type, ) +if sys.version_info >= (3, 13): + from typing import TypeIs +else: + from typing_extensions import TypeIs # The only purpose of the following line is so that pyright will put all of the # conditional imports into the .pyi file when generating type stubs. Without this line, # pyright will not include the above imports in the generated .pyi file, and it will # result in a lot of red squiggles in user code. -_: 'Annotated |Concatenate[str, ParamSpec("P")] | ParamSpec | TypeGuard | NotRequired | Required | TypedDict | assert_type | Self' # type:ignore +_: 'Annotated |Concatenate[str, ParamSpec("P")] | ParamSpec | TypeGuard | TypeIs | NotRequired | Required | TypedDict | assert_type | Self' # type:ignore diff --git a/shiny/render/__init__.py b/shiny/render/__init__.py index 3a2678d45..59ceea625 100644 --- a/shiny/render/__init__.py +++ b/shiny/render/__init__.py @@ -13,7 +13,10 @@ data_frame, ) from ._data_frame_utils import CellSelection -from ._data_frame_utils._types import DataFrameLike, StyleInfo +from ._data_frame_utils._types import ( # noqa: F401 + StyleInfo, + DataFrameLikeT as _DataFrameLikeT, # pyright: ignore[reportUnusedImport] +) from ._deprecated import ( # noqa: F401 RenderFunction, # pyright: ignore[reportUnusedImport] RenderFunctionAsync, # pyright: ignore[reportUnusedImport] @@ -48,5 +51,4 @@ "CellValue", "CellSelection", "StyleInfo", - "DataFrameLike", ) diff --git a/shiny/render/_data_frame.py b/shiny/render/_data_frame.py index 1ff97b4be..67de14eda 100644 --- a/shiny/render/_data_frame.py +++ b/shiny/render/_data_frame.py @@ -2,13 +2,9 @@ import warnings -# TODO-barret; Make DataFrameLikeT generic bound to DataFrameLike. Add this generic type to the DataGrid and DataTable -# TODO-barret; Should `.input_cell_selection()` ever return None? Is that value even helpful? Empty lists would be much more user friendly. -# * For next release: Agreed to remove `None` type. -# * For this release: Immediately make PR to remove `.input_` from `.input_cell_selection()` # TODO-barret-render.data_frame; Docs # TODO-barret-render.data_frame; Add examples! -from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, Literal, Union, cast +from typing import TYPE_CHECKING, Any, Awaitable, Callable, Literal, Union, cast from htmltools import Tag @@ -36,18 +32,18 @@ ) from ._data_frame_utils._styles import as_browser_style_infos from ._data_frame_utils._tbl_data import ( - apply_frame_patches, - as_data_frame_like, + apply_frame_patches__typed, frame_columns, frame_shape, serialize_dtype, - subset_frame, + subset_frame__typed, ) from ._data_frame_utils._types import ( CellPatchProcessed, ColumnFilter, ColumnSort, - DataFrameLike, + DataFrameLikeT, + FrameDtype, FrameRender, cell_patch_processed_to_jsonifiable, frame_render_to_jsonifiable, @@ -59,7 +55,14 @@ if TYPE_CHECKING: from ..session import Session -from ._data_frame_utils._datagridtable import DataFrameResult +DataFrameResult = Union[ + None, + DataFrameLikeT, + "DataGrid[DataFrameLikeT]", + "DataTable[DataFrameLikeT]", +] +DataFrameValue = Union[None, DataGrid[DataFrameLikeT], DataTable[DataFrameLikeT]] + # # TODO-future; Use `dataframe-api-compat>=0.2.6` to injest dataframes and return standardized dataframe structures # # TODO-future: Find this type definition: https://github.com/data-apis/dataframe-api-compat/blob/273c0be45962573985b3a420869d0505a3f9f55d/dataframe_api_compat/polars_standard/dataframe_object.py#L22 @@ -92,7 +95,7 @@ @add_example() -class data_frame(Renderer[DataFrameResult]): +class data_frame(Renderer[DataFrameResult[DataFrameLikeT]]): """ Decorator for a function that returns a pandas `DataFrame` object (or similar) to render as an interactive table or grid. Features fast virtualized scrolling, sorting, @@ -164,11 +167,11 @@ class data_frame(Renderer[DataFrameResult]): objects you can return from the rendering function to specify options. """ - _value: reactive.Value[DataFrameResult | None] + _value: reactive.Value[DataFrameValue[DataFrameLikeT] | None] """ Reactive value of the data frame's rendered object. """ - _type_hints: reactive.Value[dict[str, str] | None] + _type_hints: reactive.Value[list[FrameDtype] | None] """ Reactive value of the data frame's type hints for each column. @@ -206,7 +209,7 @@ class data_frame(Renderer[DataFrameResult]): Reactive value of the data frame's edits provided by the user. """ - data: reactive.Calc_[DataFrameLike] + data: reactive.Calc_[DataFrameLikeT] """ Reactive value of the data frame's output data. @@ -217,17 +220,17 @@ class data_frame(Renderer[DataFrameResult]): Even if the rendered data value was not of type `pd.DataFrame` or `pl.DataFrame`, this method currently converts it to a `pd.DataFrame`. """ - _data_view_all: reactive.Calc_[DataFrameLike] + _data_view_all: reactive.Calc_[DataFrameLikeT] """ Reactive value of the full (sorted and filtered) data. """ - _data_view_selected: reactive.Calc_[DataFrameLike] + _data_view_selected: reactive.Calc_[DataFrameLikeT] """ Reactive value of the selected rows of the (sorted and filtered) data. """ @add_example(ex_dir="../api-examples/data_frame_data_view") - def data_view(self, *, selected: bool = False) -> DataFrameLike: + def data_view(self, *, selected: bool = False) -> DataFrameLikeT: """ Reactive function that retrieves the data how it is viewed within the browser. @@ -299,7 +302,7 @@ def data_view(self, *, selected: bool = False) -> DataFrameLike: The row numbers of the data frame that are currently being viewed in the browser after sorting and filtering has been applied. """ - _data_patched: reactive.Calc_[DataFrameLike] + _data_patched: reactive.Calc_[DataFrameLikeT] """ Reactive value of the data frame's patched data. @@ -339,8 +342,10 @@ def _init_reactives(self) -> None: from .. import req # Init - self._value: reactive.Value[DataFrameResult | None] = reactive.Value(None) - self._type_hints: reactive.Value[dict[str, str] | None] = reactive.Value(None) + self._value: reactive.Value[DataFrameValue[DataFrameLikeT] | None] = ( + reactive.Value(None) + ) + self._type_hints: reactive.Value[list[FrameDtype] | None] = reactive.Value(None) self._cell_patch_map = reactive.Value({}) @reactive.calc @@ -350,7 +355,7 @@ def self_cell_patches() -> list[CellPatchProcessed]: self.cell_patches = self_cell_patches @reactive.calc - def self_data() -> DataFrameLike: + def self_data() -> DataFrameLikeT: value = self._value() req(value) @@ -423,14 +428,14 @@ def self_data_view_rows() -> tuple[int, ...]: self.data_view_rows = self_data_view_rows @reactive.calc - def self__data_patched() -> DataFrameLike: - return apply_frame_patches(self.data(), self.cell_patches()) + def self__data_patched() -> DataFrameLikeT: + return apply_frame_patches__typed(self.data(), self.cell_patches()) self._data_patched = self__data_patched # Apply filtering and sorting # https://github.com/posit-dev/py-shiny/issues/1240 - def _subset_data_view(selected: bool) -> DataFrameLike: + def _subset_data_view(selected: bool) -> DataFrameLikeT: """ Helper method to subset data according to what is viewed in the browser; @@ -454,15 +459,15 @@ def _subset_data_view(selected: bool) -> DataFrameLike: else: rows = self.data_view_rows() - return subset_frame(self._data_patched(), rows=rows) + return subset_frame__typed(self._data_patched(), rows=rows) # Helper reactives so that internal calculations can be cached for use in other calculations @reactive.calc - def self__data_view() -> DataFrameLike: + def self__data_view() -> DataFrameLikeT: return _subset_data_view(selected=False) @reactive.calc - def self__data_view_selected() -> DataFrameLike: + def self__data_view_selected() -> DataFrameLikeT: return _subset_data_view(selected=True) self._data_view_all = self__data_view @@ -721,7 +726,7 @@ async def _attempt_update_cell_style(self) -> None: def auto_output_ui(self) -> Tag: return ui.output_data_frame(id=self.output_id) - def __init__(self, fn: ValueFn[DataFrameResult]): + def __init__(self, fn: ValueFn[DataFrameResult[DataFrameLikeT]]): super().__init__(fn) # Set reactives from calculated properties @@ -758,26 +763,22 @@ async def render(self) -> JsonifiableDict | None: return None if not isinstance(value, AbstractTabularData): - value = DataGrid( - as_data_frame_like( - value, - "@render.data_frame doesn't know how to render objects of type", - ) - ) + try: + value = DataGrid(value) + except TypeError as e: + raise TypeError( + "@render.data_frame doesn't know how to render objects of type ", + type(value), + ) from e # Set patches url handler for client patch_key = self._set_patches_handler() - self._value.set(value) + self._value.set(value) # pyright: ignore[reportArgumentType] # Use session context so `to_payload()` gets the correct session with session_context(self._get_session()): payload = value.to_payload() - - type_hints = cast( - Union[Dict[str, str], None], - payload.get("typeHints", None), - ) - self._type_hints.set(type_hints) + self._type_hints.set(payload["typeHints"]) ret: FrameRender = { "payload": payload, diff --git a/shiny/render/_data_frame_utils/_datagridtable.py b/shiny/render/_data_frame_utils/_datagridtable.py index 1bda4163a..15b3c0b27 100644 --- a/shiny/render/_data_frame_utils/_datagridtable.py +++ b/shiny/render/_data_frame_utils/_datagridtable.py @@ -1,9 +1,7 @@ from __future__ import annotations import abc - -# TODO-barret-future; make DataTable and DataGrid generic? By currently accepting `object`, it is difficult to capture the generic type of the data. -from typing import TYPE_CHECKING, Literal, Union +from typing import TYPE_CHECKING, Generic, Literal, Union from ..._docstring import add_example, no_example from ._selection import ( @@ -14,16 +12,15 @@ ) from ._styles import StyleFn, StyleInfo, as_browser_style_infos, as_style_infos from ._tbl_data import as_data_frame_like, serialize_frame -from ._types import DataFrameLike, FrameJson, PandasCompatible +from ._types import DataFrameLikeT, FrameJson if TYPE_CHECKING: DataFrameResult = Union[ None, - DataFrameLike, - "DataGrid", - "DataTable", - PandasCompatible, + DataFrameLikeT, + "DataGrid[DataFrameLikeT]", + "DataTable[DataFrameLikeT]", ] else: @@ -38,7 +35,7 @@ def to_payload(self) -> FrameJson: ... @add_example(ex_dir="../../api-examples/data_frame") -class DataGrid(AbstractTabularData): +class DataGrid(AbstractTabularData, Generic[DataFrameLikeT]): """ Holds the data and options for a :class:`~shiny.render.data_frame` output, for a spreadsheet-like view. @@ -100,18 +97,18 @@ class DataGrid(AbstractTabularData): * :class:`~shiny.render.DataTable` """ - data: DataFrameLike + data: DataFrameLikeT width: str | float | None height: str | float | None summary: bool | str filters: bool editable: bool selection_modes: SelectionModes - styles: list[StyleInfo] | StyleFn + styles: list[StyleInfo] | StyleFn[DataFrameLikeT] def __init__( self, - data: DataFrameLike | PandasCompatible, + data: DataFrameLikeT, *, width: str | float | None = "fit-content", height: str | float | None = None, @@ -119,14 +116,11 @@ def __init__( filters: bool = False, editable: bool = False, selection_mode: SelectionModeInput = "none", - styles: StyleInfo | list[StyleInfo] | StyleFn | None = None, + styles: StyleInfo | list[StyleInfo] | StyleFn[DataFrameLikeT] | None = None, row_selection_mode: RowSelectionModeDeprecated = "deprecated", ): - self.data = as_data_frame_like( - data, - "The DataGrid() constructor didn't expect a 'data' argument of type", - ) + self.data = as_data_frame_like(data) self.width = width self.height = height @@ -161,7 +155,7 @@ def to_payload(self) -> FrameJson: @no_example() -class DataTable(AbstractTabularData): +class DataTable(AbstractTabularData, Generic[DataFrameLikeT]): """ Holds the data and options for a :class:`~shiny.render.data_frame` output, for a spreadsheet-like view. @@ -223,17 +217,18 @@ class DataTable(AbstractTabularData): * :class:`~shiny.render.DataGrid` """ - data: DataFrameLike + data: DataFrameLikeT width: str | float | None height: str | float | None summary: bool | str filters: bool + editable: bool selection_modes: SelectionModes - styles: list[StyleInfo] | StyleFn + styles: list[StyleInfo] | StyleFn[DataFrameLikeT] def __init__( self, - data: DataFrameLike | PandasCompatible, + data: DataFrameLikeT, *, width: str | float | None = "fit-content", height: str | float | None = "500px", @@ -241,14 +236,10 @@ def __init__( filters: bool = False, editable: bool = False, selection_mode: SelectionModeInput = "none", + styles: StyleInfo | list[StyleInfo] | StyleFn[DataFrameLikeT] | None = None, row_selection_mode: Literal["deprecated"] = "deprecated", - styles: StyleInfo | list[StyleInfo] | StyleFn | None = None, ): - - self.data = as_data_frame_like( - data, - "The DataTable() constructor didn't expect a 'data' argument of type", - ) + self.data = as_data_frame_like(data) self.width = width self.height = height diff --git a/shiny/render/_data_frame_utils/_styles.py b/shiny/render/_data_frame_utils/_styles.py index 6a471da9f..514730326 100644 --- a/shiny/render/_data_frame_utils/_styles.py +++ b/shiny/render/_data_frame_utils/_styles.py @@ -1,18 +1,12 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Callable, List +from typing import Callable, List from ...types import ListOrTuple from ._tbl_data import frame_column_names -from ._types import BrowserStyleInfo, DataFrameLike, StyleInfo +from ._types import BrowserStyleInfo, DataFrameLikeT, StyleInfo -if TYPE_CHECKING: - - StyleFn = Callable[[DataFrameLike], List["StyleInfo"]] - -else: - StyleFn = Callable - DataFrameValue = object +StyleFn = Callable[[DataFrameLikeT], List["StyleInfo"]] def style_info_to_browser_style_info( @@ -164,8 +158,8 @@ def style_info_rows( def as_style_infos( - infos: StyleInfo | list[StyleInfo] | StyleFn | None, -) -> list[StyleInfo] | StyleFn: + infos: StyleInfo | list[StyleInfo] | StyleFn[DataFrameLikeT] | None, +) -> list[StyleInfo] | StyleFn[DataFrameLikeT]: if callable(infos): return infos @@ -180,9 +174,9 @@ def as_style_infos( def as_browser_style_infos( - infos: list[StyleInfo] | StyleFn, + infos: list[StyleInfo] | StyleFn[DataFrameLikeT], *, - data: DataFrameLike, + data: DataFrameLikeT, ) -> list[BrowserStyleInfo]: browser_column_names = frame_column_names(data) diff --git a/shiny/render/_data_frame_utils/_tbl_data.py b/shiny/render/_data_frame_utils/_tbl_data.py index 85ea5ef56..b8271ea5d 100644 --- a/shiny/render/_data_frame_utils/_tbl_data.py +++ b/shiny/render/_data_frame_utils/_tbl_data.py @@ -7,6 +7,7 @@ from htmltools import TagNode +from ..._typing_extensions import TypeIs from ...session import require_active_session from ._html import wrap_shiny_html @@ -15,6 +16,7 @@ CellPatchProcessed, ColsList, DataFrameLike, + DataFrameLikeT, FrameDtype, FrameJson, ListSeriesLike, @@ -29,7 +31,7 @@ __all__ = ( "is_data_frame_like", - "as_data_frame_like", + # "as_data_frame_like", "frame_columns", "apply_frame_patches", "serialize_dtype", @@ -44,48 +46,53 @@ # as_frame ----------------------------------------------------------------------------- -@singledispatch -def is_data_frame_like( - data: object, -) -> bool: - return False - +def as_data_frame_like( + data: DataFrameLikeT, +) -> DataFrameLikeT: + if is_data_frame_like(data): + return data -@is_data_frame_like.register -def _(data: PdDataFrame) -> bool: - return True + # Legacy support for non-Pandas/Polars data frames that were previously supported + # and converted to pandas + if isinstance(data, PandasCompatible): + from ..._deprecated import warn_deprecated + + warn_deprecated( + "Returned data frame data values that are not Pandas or Polars `DataFrame`s are currently not supported. " + "A `.to_pandas()` was found on your object and will be called. " + "To remove this warning, please call `.to_pandas()` on your data " + "and use the result in your return value. " + "In the future, this will raise an error." + ) + return data.to_pandas() + raise TypeError(f"Unsupported type: {type(data)}") -@is_data_frame_like.register -def _(data: PlDataFrame) -> bool: - return True +# @singledispatch +# def is_data_frame_like( +# data: object, +# ) -> bool: +# return False -@singledispatch -def as_data_frame_like( - data: DataFrameLike | PandasCompatible, - error_message_begin: str = "Unsupported type:", -) -> DataFrameLike: - raise TypeError( - f"{error_message_begin} {str(type(data))}\n" - "Please use either a `pandas.DataFrame`, a `polars.DataFrame`, " - "or an object that has a `.to_pandas()` method." - ) +# @is_data_frame_like.register +# def _(data: PdDataFrame) -> bool: +# return True -@as_data_frame_like.register -def _(data: PdDataFrame, error_message_begin: str = "ignored") -> DataFrameLike: - return data +# @is_data_frame_like.register +# def _(data: PlDataFrame) -> bool: +# return True -@as_data_frame_like.register -def _(data: PlDataFrame, error_message_begin: str = "ignored") -> DataFrameLike: - return data +def is_data_frame_like( + data: DataFrameLikeT | object, +) -> TypeIs[DataFrameLikeT]: + if isinstance(data, (PdDataFrame, PlDataFrame)): + return True -@as_data_frame_like.register -def _(data: PandasCompatible, error_message_begin: str = "ignored") -> DataFrameLike: - return data.to_pandas() + return False # frame_columns ------------------------------------------------------------------------ @@ -110,6 +117,12 @@ def _(data: PlDataFrame) -> ListSeriesLike: # apply_frame_patches -------------------------------------------------------------------- +def apply_frame_patches__typed( + data: DataFrameLikeT, patches: List[CellPatchProcessed] +) -> DataFrameLikeT: + return cast(DataFrameLikeT, apply_frame_patches(data, patches)) + + @singledispatch def apply_frame_patches( data: DataFrameLike, @@ -237,6 +250,10 @@ def wrap_shiny_html_with_session(x: TagNode): # subset_frame ------------------------------------------------------------------------- +def subset_frame__typed( + data: DataFrameLikeT, *, rows: RowsList = None, cols: ColsList = None +) -> DataFrameLikeT: + return cast(DataFrameLikeT, subset_frame(data, rows=rows, cols=cols)) @singledispatch diff --git a/shiny/render/_data_frame_utils/_types.py b/shiny/render/_data_frame_utils/_types.py index 163e37bbc..bc1ff0e83 100644 --- a/shiny/render/_data_frame_utils/_types.py +++ b/shiny/render/_data_frame_utils/_types.py @@ -10,6 +10,7 @@ Optional, Protocol, Tuple, + TypeVar, Union, cast, runtime_checkable, @@ -68,6 +69,8 @@ class DataFrameLike(ABC): ... DataFrameLike.register(PdDataFrame) DataFrameLike.register(PlDataFrame) +DataFrameLikeT = TypeVar("DataFrameLikeT", PdDataFrame, PlDataFrame) + # --------------------------------------------------------------------- @@ -164,8 +167,8 @@ class FrameJson(TypedDict): options: NotRequired[FrameJsonOptions] -RowsList = Optional[List[int]] -ColsList = Optional[List[Union[str, int]]] +RowsList = Optional[ListOrTuple[int]] +ColsList = Optional[ListOrTuple[Union[str, int]]] # --------------------------------------------------------------------- diff --git a/tests/playwright/deploys/express-dataframe/app.py b/tests/playwright/deploys/express-dataframe/app.py index 5902c0a78..db7c2d638 100644 --- a/tests/playwright/deploys/express-dataframe/app.py +++ b/tests/playwright/deploys/express-dataframe/app.py @@ -20,5 +20,5 @@ ui.h2("Below is a sample dataframe") @render.data_frame - def sample_data_frame(id: str = "dataframe"): + def sample_data_frame(): return df diff --git a/tests/playwright/shiny/components/busy_indicators/test_busy_indicators.py b/tests/playwright/shiny/components/busy_indicators/test_busy_indicators.py index ffb1f48e0..2f05df0b2 100644 --- a/tests/playwright/shiny/components/busy_indicators/test_busy_indicators.py +++ b/tests/playwright/shiny/components/busy_indicators/test_busy_indicators.py @@ -1,6 +1,8 @@ import os from urllib.parse import urlparse +import pytest +from examples.example_apps import reruns, reruns_delay from playwright.sync_api import Page, expect from shiny.playwright import controller @@ -23,6 +25,7 @@ def get_pulse_computed_property(page: Page, property_name: str) -> str: ) +@pytest.mark.flaky(reruns=reruns, delay=reruns_delay) def test_busy_indicators(page: Page, local_app: ShinyAppProc) -> None: page.goto(local_app.url) spinner_type = controller.InputRadioButtons(page, "busy_indicator_type") diff --git a/tests/playwright/shiny/components/data_frame/data_view_info/app.py b/tests/playwright/shiny/components/data_frame/data_view_info/app.py index e2c005cbd..88790e591 100644 --- a/tests/playwright/shiny/components/data_frame/data_view_info/app.py +++ b/tests/playwright/shiny/components/data_frame/data_view_info/app.py @@ -52,7 +52,7 @@ def mod_ui(): @module.server def mod_server( - input: Inputs, output: Outputs, session: Session, dt: render.DataFrameLike + input: Inputs, output: Outputs, session: Session, dt: render._DataFrameLikeT ): @render.data_frame def penguins_df(): diff --git a/tests/playwright/shiny/components/data_frame/edit/app.py b/tests/playwright/shiny/components/data_frame/edit/app.py index 482b92af7..c759bf9d7 100644 --- a/tests/playwright/shiny/components/data_frame/edit/app.py +++ b/tests/playwright/shiny/components/data_frame/edit/app.py @@ -29,6 +29,9 @@ null_values="NA", ) +pd_asdf = render.DataTable(pd_penguins) +pl_asdf = render.DataTable(pl_penguins) + # Load the dataset df = pd_penguins diff --git a/tests/playwright/shiny/components/data_frame/html_columns_df/df_organization/app.py b/tests/playwright/shiny/components/data_frame/html_columns_df/df_organization/app.py index 9d6aad37c..4cf702031 100644 --- a/tests/playwright/shiny/components/data_frame/html_columns_df/df_organization/app.py +++ b/tests/playwright/shiny/components/data_frame/html_columns_df/df_organization/app.py @@ -42,7 +42,10 @@ def mod_ui(): @module.server def mod_server( - input: Inputs, output: Outputs, session: Session, data: render.DataFrameLike + input: Inputs, + output: Outputs, + session: Session, + data: render._DataFrameLikeT, ): @render.text def df_type(): diff --git a/tests/playwright/shiny/components/data_frame/pandas_compatible/app.py b/tests/playwright/shiny/components/data_frame/pandas_compatible/app.py index 90e45b965..caf89a41d 100644 --- a/tests/playwright/shiny/components/data_frame/pandas_compatible/app.py +++ b/tests/playwright/shiny/components/data_frame/pandas_compatible/app.py @@ -17,24 +17,34 @@ def code_original(): return str(type(t)) -@render.data_frame +@render.data_frame # pyright: ignore[reportArgumentType] def df_astropy(): return t @render.code def code_astropy(): - return str(type(df_astropy.data())) + return str( + type( # pyright: ignore[reportUnknownArgumentType] + df_astropy.data() # pyright: ignore[reportUnknownArgumentType,reportUnknownMemberType] + ) + ) ui.h2(ui.code(".data()")) @render.data_frame -def df_data(): - return df_astropy.data() +def df_data(): # pyright: ignore[reportUnknownParameterType] + return ( + df_astropy.data() # pyright: ignore[reportUnknownVariableType,reportUnknownMemberType] + ) @render.code def code_data(): - return str(type(df_data.data())) + return str( + type( # pyright: ignore[reportUnknownArgumentType] + df_data.data() # pyright: ignore[reportUnknownArgumentType,reportUnknownMemberType] + ) + ) diff --git a/tests/playwright/shiny/components/data_frame/return_type/app.py b/tests/playwright/shiny/components/data_frame/return_type/app.py new file mode 100644 index 000000000..94f395cb9 --- /dev/null +++ b/tests/playwright/shiny/components/data_frame/return_type/app.py @@ -0,0 +1,23 @@ +from palmerpenguins import load_penguins_raw # pyright: ignore[reportMissingTypeStubs] + +from shiny.express import render + + +@render.code +def first_code(): + return str(type(first_df.data())) + + +@render.code +def second_code(): + return str(type(second_df.data())) + + +@render.data_frame +def first_df(): + return render.DataGrid(data=load_penguins_raw()) + + +@render.data_frame +def second_df(): + return first_df.data_view(selected=False) diff --git a/tests/playwright/shiny/components/data_frame/row_selection/app.py b/tests/playwright/shiny/components/data_frame/row_selection/app.py index 898379dd5..01307468c 100644 --- a/tests/playwright/shiny/components/data_frame/row_selection/app.py +++ b/tests/playwright/shiny/components/data_frame/row_selection/app.py @@ -41,7 +41,7 @@ def make_ui(title: str): ) -def make_server(input: Inputs, data: render.DataFrameLike): +def make_server(input: Inputs, data: render._DataFrameLikeT): @render.data_frame def grid(): return render.DataGrid( @@ -100,7 +100,7 @@ def mod_ui(title: str = "Module"): @module.server def mod_server( - input: Inputs, output: Outputs, session: Session, data: render.DataFrameLike + input: Inputs, output: Outputs, session: Session, data: render._DataFrameLikeT ): make_server(input, data) diff --git a/tests/playwright/shiny/components/data_frame/styles/app.py b/tests/playwright/shiny/components/data_frame/styles/app.py index 66f407f25..82e34f9d3 100644 --- a/tests/playwright/shiny/components/data_frame/styles/app.py +++ b/tests/playwright/shiny/components/data_frame/styles/app.py @@ -113,7 +113,7 @@ def mod_server( input: Inputs, output: Outputs, session: Session, - data: render.DataFrameLike, + data: render._DataFrameLikeT, ): @render.data_frame def fn_styles(): @@ -121,7 +121,7 @@ def fn_styles(): counter = 0 def df_styles_fn( - data: render.DataFrameLike, + data: render._DataFrameLikeT, ) -> list[render.StyleInfo]: nonlocal counter diff --git a/tests/playwright/shiny/components/data_frame/styles_class/app.py b/tests/playwright/shiny/components/data_frame/styles_class/app.py index b1a1df0cd..438666e0c 100644 --- a/tests/playwright/shiny/components/data_frame/styles_class/app.py +++ b/tests/playwright/shiny/components/data_frame/styles_class/app.py @@ -2,7 +2,6 @@ import polars as pl -# TODO-barret; Export render.DataFrameLike # pyright: reportMissingTypeStubs = false from palmerpenguins import load_penguins_raw diff --git a/tests/playwright/shiny/components/data_frame/validate_column_labels/app.py b/tests/playwright/shiny/components/data_frame/validate_column_labels/app.py index 8374d66d1..f49ecb618 100644 --- a/tests/playwright/shiny/components/data_frame/validate_column_labels/app.py +++ b/tests/playwright/shiny/components/data_frame/validate_column_labels/app.py @@ -22,7 +22,7 @@ def df_mod( output: Outputs, session: Session, name: str, - data: render.DataFrameLike, + data: render._DataFrameLikeT, ): ui.hr() diff --git a/tests/pytest/test_render_data_frame.py b/tests/pytest/test_render_data_frame.py index 11e44da79..1321c2ce5 100644 --- a/tests/pytest/test_render_data_frame.py +++ b/tests/pytest/test_render_data_frame.py @@ -44,7 +44,7 @@ def test_as_selection_modes_legacy(): assert dg.selection_modes.row == SelectionModes(selection_mode_set={"rows"}).row with pytest.raises(ValueError, match="Unknown row_selection_mode: foo"): - render.DataGrid( + render.DataGrid( # pyright: ignore[reportCallIssue] df, row_selection_mode="foo", # pyright: ignore[reportArgumentType,reportGeneralTypeIssues] )