Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support DataFrames via narwhals #1570

Merged
merged 97 commits into from
Oct 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
97 commits
Select commit Hold shift + click to select a range
bbe6261
feat: start rough draft of narwhals support
machow Jul 25, 2024
12d39cf
Merge branch 'main' into feat-narwhals
schloerke Jul 26, 2024
57fe20f
add narwhals to deps
schloerke Jul 26, 2024
81058ea
lint
schloerke Jul 26, 2024
c86ec1e
Merge branch 'main' into machow-feat-narwhals
schloerke Jul 26, 2024
869aa7a
Fix typing issues
schloerke Jul 26, 2024
271ec5f
Rename fixtures
schloerke Jul 26, 2024
a5714e4
Partial for narwhals serialize frame. Still needs work. Need datetime…
schloerke Jul 26, 2024
f11769f
Merge branch 'main' into machow-feat-narwhals
schloerke Aug 26, 2024
db83bc7
add missing narwhals dep in config
schloerke Aug 26, 2024
1155868
Merge branch 'main' into machow-feat-narwhals
schloerke Aug 30, 2024
be6985a
Merge branch 'main' into machow-feat-narwhals
schloerke Sep 5, 2024
9318a5b
Disable timedelta dtype within pandas (as moving towards all narwhals…
schloerke Sep 5, 2024
42d0353
Scaffold out more dtypes
schloerke Sep 5, 2024
3e3c8c0
Use `orjson` instead of `json` package to serialize json. (Native dat…
schloerke Sep 5, 2024
ef2ee1c
Update tests, trying to support more dtypes (more to go). Disable non…
schloerke Sep 5, 2024
824f6b4
Merge branch 'main' into feat-narwhals
schloerke Sep 16, 2024
563bc52
Bump min version of narwhals to v1.8.0 for `nw.Series.scatter(rows, v…
schloerke Sep 16, 2024
861329a
Expose narwhals types of `DataFrame`, `DataFrameT`, `IntoDataFrame`, …
schloerke Sep 16, 2024
5172734
Add narwhals for types and integrate into _tbl_data.py
schloerke Sep 16, 2024
ce6cd35
Styles function will accept `IntoDataFrameT` type
schloerke Sep 16, 2024
3fea35c
Be explicit on type of object being inspected
schloerke Sep 16, 2024
dc8179f
Disable all pandas specific functions for data frame
schloerke Sep 16, 2024
002ba7e
DataGrid and DataTable can now accept `IntroDataFrameT`
schloerke Sep 16, 2024
2eacaa5
Upgrade `render.data_frame` to handle `IntoDataFrameT`. Add `._nw_dat…
schloerke Sep 16, 2024
32fe466
Update typing
schloerke Sep 16, 2024
00088be
Update tests to handle the new types
schloerke Sep 16, 2024
cf6b283
Fix port test to be more robust
schloerke Sep 16, 2024
9cf92d3
Add quick short circuit
schloerke Sep 17, 2024
cfb5ac7
Add TODO with hint from Marco
schloerke Sep 17, 2024
aa0d8dd
Fix html column bug
schloerke Sep 17, 2024
8b88353
Fix narwhals subset bugs
schloerke Sep 17, 2024
cf5a845
Do not convert data early
schloerke Sep 17, 2024
08e70ad
Update _data_frame.py
schloerke Sep 17, 2024
3b4be82
Fix another html column issue
schloerke Sep 17, 2024
1f8c9c6
lint
schloerke Sep 18, 2024
6273885
Update tests for narwhals. Note: py-htmltools is holding up PR's test…
schloerke Sep 18, 2024
815baa4
Merge branch 'main' into machow-feat-narwhals
schloerke Sep 23, 2024
d0282aa
Remove `SeriesLike` type
schloerke Sep 23, 2024
c9009e8
Tighten up code. Remove mention of singledispatch
schloerke Sep 23, 2024
e7d1fe8
Merge branch 'main' into machow-feat-narwhals
schloerke Sep 24, 2024
a0681b7
Use latest narwhals (1.8.3) and dev htmltools
schloerke Sep 24, 2024
d0772e4
Remove many TODOs
schloerke Sep 24, 2024
77d6085
Update _html.py
schloerke Sep 25, 2024
d66e1de
Update docs
schloerke Sep 25, 2024
a950d7d
Test more narwhals types. Add comment for file.
schloerke Sep 25, 2024
baed7f9
Add more cell types to data frame js. Add test app for all types and …
schloerke Sep 25, 2024
90276a3
Update app.py
schloerke Sep 27, 2024
246819f
If the cell value is `null`, display `""` for table cell value
schloerke Sep 27, 2024
0e57199
Test that `DataFrame.data()` returns the same type as render method
schloerke Sep 27, 2024
f4caa1f
Temporarily disable htmltools install
schloerke Sep 27, 2024
cb960c4
Install htmltools from `taglist_not_list` branch on GH
schloerke Sep 27, 2024
df72692
Add new narwhals types
schloerke Sep 27, 2024
6e22352
Update app.py
schloerke Sep 27, 2024
71ead9f
Update htmltools dep to include new TagList inheriting from UserList
schloerke Sep 30, 2024
f8d7c13
Add note about narwhals inspecting only the first sentence
schloerke Sep 30, 2024
46a1203
Be sure to return the cell text
schloerke Sep 30, 2024
da98bd8
Rename internal method `cell_contains_htmltoolslike` to `ui_must_be_p…
schloerke Sep 30, 2024
4fc66b3
Leverage htmltools's new HTML/UserString and TagList/UserList objects…
schloerke Sep 30, 2024
98d40e1
Update app to use html within the first row to tell narwhals it's an …
schloerke Sep 30, 2024
17c3b2a
Add `reactive_method()` and tests
schloerke Oct 1, 2024
f7c2e65
Utilize new `@reactive_calc_method` decorator in data frame class
schloerke Oct 1, 2024
5a47cc8
Merge branch 'main' into machow-feat-narwhals
schloerke Oct 1, 2024
ee2f485
Update _reactive_method.py
schloerke Oct 1, 2024
f817510
Update date warning when non-narwhals compatible data is received
schloerke Oct 1, 2024
8f68a4b
Update narwhals
schloerke Oct 1, 2024
758fd86
Change equality subclass checks to isinstance checks
schloerke Oct 1, 2024
f5e2c6a
Update test_render_data_frame_tbl_data.py
schloerke Oct 1, 2024
47b855c
Update test_render_data_frame_tbl_data.py
schloerke Oct 1, 2024
1a9aa96
Update _data_frame.py
schloerke Oct 1, 2024
a9d3c67
Add another future impl for reactive_value_method
schloerke Oct 1, 2024
a25a3d1
Restore odd skip as it is still required
schloerke Oct 1, 2024
fddd87f
Drop testing of python 3.8 (minimal changes)
schloerke Oct 1, 2024
a992b2f
Revert "chore: revert check where airmass skips on python 3.9 (#1708)"
schloerke Oct 1, 2024
12544b6
Update Makefile
schloerke Oct 1, 2024
6f28b06
Revert htmltools install as it isn't needed anymore
schloerke Oct 1, 2024
3f3b9be
Drop `is_into_data_frame`
schloerke Oct 2, 2024
bc99573
Code review
schloerke Oct 2, 2024
e0caacd
Remove unused `._type_hints`
schloerke Oct 2, 2024
3791a3b
Update _data_frame.py
schloerke Oct 3, 2024
e2444d1
Collect all JS deps of a data frame sep from the content. For each ce…
schloerke Oct 3, 2024
1eef355
Update test_render_data_frame_tbl_data.py
schloerke Oct 3, 2024
f5e9099
Try requiring py-htmltools directly. Do not install py-htmltools manu…
schloerke Oct 3, 2024
c397e82
Use htmltools github ref
schloerke Oct 3, 2024
3ce71f9
Remove small and unused narwals wrapper methods
schloerke Oct 3, 2024
1a6e917
Update Makefile
schloerke Oct 3, 2024
6142df3
Store the reactive calc on self directly. This adds a two-way pointer…
schloerke Oct 3, 2024
529f371
Move py-shinylive github install to pyproject
schloerke Oct 3, 2024
1a26d7d
Revert "Move py-shinylive github install to pyproject"
schloerke Oct 3, 2024
b29e037
Perform the install in a single command
schloerke Oct 3, 2024
1fcef38
Merge branch 'main' into feat-narwhals
schloerke Oct 3, 2024
1f46fb3
Un-export `render.[DataFrame, DataFrameT, IntoDataFrame, IntoDataFram…
schloerke Oct 3, 2024
6630f61
Add narwhals support for `@render.table`; Remove no longer necessary …
schloerke Oct 3, 2024
d1b53ff
Update CHANGELOG.md
schloerke Oct 3, 2024
9f2a29d
Fix test
schloerke Oct 3, 2024
a1a9e54
Add changelog for `@render.table`
schloerke Oct 3, 2024
6ee52a9
Set max version of narwhals to `<1.10.0`
schloerke Oct 3, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/workflows/pytest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install https://github.com/rstudio/py-htmltools/tarball/main
make install-deps
make install
- name: "Build Package"
Expand Down
12 changes: 9 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### New features

* Added [narwhals](https://posit-dev.github.io/py-narwhals) support for `@render.data_frame`. This allows for any eager data frame supported by narwhals to be returned from a `@render.data_frame` output method. All internal methods and helper methods leverage the `narwhals` API to be data frame agnostic. (#1570)

### Other changes

* Incorporated `orjson` for faster data serialization in `@render.data_frame` outputs. (#1570)

* Added `PageNavbar` class to the list of `shiny.playwright.controllers` for testing `ui.page_navbar()`. (#1668)

* Added `.expect_widths()` to `NavsetPillList` in `shiny.playwright.controllers` for testing `ui.navset_pill_list(widths=)`. (#1668)
Expand All @@ -27,15 +31,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

* Small improvements to the default pulse busy indicator to better blend with any background. It's also now slightly smaller by default.(#1707)

* Added [narwhals](https://posit-dev.github.io/py-narwhals) support for `@render.table`. This allows for any eager data frame supported by narwhals to be returned from a `@render.table` output method. (#1570)

### Bug fixes

* A few fixes for `ui.Chat()`, including:
* Fixed a bug with `Chat()` sometimes silently dropping errors. (#1672)
* Fixed a bug with `Chat()` sometimes not removing it's loading icon (on error or a `None` transform). (#1679)
* `.messages(format="anthropic")` correctly removes non-user starting messages (once again). (#1685)

* `shiny create` now uses the template `id` rather than the directory name as the default directory.
(#1666)
* `shiny create` now uses the template `id` rather than the directory name as the default directory. (#1666)

* `ui.Theme()` now works correctly on Windows when the theme requires Sass compilation. (thanks @yuuuxt, #1684)

Expand All @@ -45,11 +50,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

* Fixed input controller `InputTextArea` in `shiny.playwright.controller` to correctly validate the `resize` style property in `.expect_resize()`. (#1705)


* Fixed a bug in `ui.conditional_panel()` that would cause the panel to repeatedly show/hide itself when the provided condition did not evaluate to a boolean value. (#1707)

* Fixed a bug with `ui.input_slider()` when used as a range slider that made it impossible to change the slider value when both handles were at the maximum value. (#1707)

* Fixed bug in `@render.data_frame` where `bool` or `object` columns were not being rendered. (#1570)


## [1.1.0] - 2024-09-03

Expand Down
7 changes: 2 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -216,17 +216,14 @@ ci-install-wheel: dist FORCE
install-deps: FORCE ## install dependencies
pip install -e ".[dev,test]" --upgrade
ci-install-deps: FORCE
uv pip install "htmltools @ git+https://github.com/posit-dev/py-htmltools.git"
uv pip install -e ".[dev,test]"

install-docs: FORCE
pip install -e ".[dev,test,doc]"
pip install https://github.com/posit-dev/py-htmltools/tarball/main
pip install https://github.com/posit-dev/py-shinylive/tarball/main
ci-install-docs: FORCE
uv pip install -e ".[dev,test,doc]"
uv pip install "htmltools @ git+https://github.com/posit-dev/py-htmltools.git" \
"shinylive @ git+https://github.com/posit-dev/py-shinylive.git"
uv pip install -e ".[dev,test,doc]" \
"shinylive @ git+https://github.com/posit-dev/py-shinylive.git"

ci-install-rsconnect: FORCE
uv pip install "rsconnect-python @ git+https://github.com/rstudio/rsconnect-python.git"
Expand Down
5 changes: 3 additions & 2 deletions js/data-frame/cell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,9 @@ const isShinyHtml = (x: any): x is CellHtmlValue => {
};
type CellValue = string | CellHtmlValue | null;
const getCellValueText = (cellValue: CellValue) => {
if (isShinyHtml(cellValue)) return cellValue.obj.html;
if (cellValue === null) return "";
return cellValue as string;
if (isShinyHtml(cellValue)) return cellValue.obj.html;
return cellValue;
};

interface TableBodyCellProps {
Expand Down Expand Up @@ -472,6 +472,7 @@ export const TableBodyCell: FC<TableBodyCellProps> = ({

// TODO-future; Use faster way to make a deep copy
const cellValueObjDeepCopy = JSON.parse(JSON.stringify(cellValue.obj));
// Render the Shiny content asynchronously to the table's cell
window.Shiny.renderContentAsync(tdRef.current, cellValueObjDeepCopy);

const curTdRef = tdRef.current;
Expand Down
38 changes: 37 additions & 1 deletion js/data-frame/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ const ShinyDataGrid: FC<ShinyDataGridProps<unknown>> = ({
fill: false,
styles: [],
},
htmlDeps,
} = payload;
const {
width,
Expand Down Expand Up @@ -197,7 +198,36 @@ const ShinyDataGrid: FC<ShinyDataGridProps<unknown>> = ({
typeHint,
},
cell: ({ getValue }) => {
return getValue() as string;
const ret = getValue();

// Regardless of type, if the value is null or undefined,
// return an empty string
if (ret === null || ret === undefined) {
return "";
}
switch (typeHint?.type) {
// Return the value as is
case "numeric":
case "date":
case "datetime":
case "duration":
case "categorical":
case "html":
return ret;
// Convert the value to a string
case "string":
case "boolean":
return String(ret);
// Convert the value to a JSON string if it isn't a string already
case "unknown":
case "object":
if (typeof ret === "string") {
return ret;
}
return JSON.stringify(ret);
default:
return ret;
}
},
enableSorting,
};
Expand Down Expand Up @@ -427,6 +457,12 @@ const ShinyDataGrid: FC<ShinyDataGridProps<unknown>> = ({
};
}, [id, selection, tableData]);

useEffect(() => {
if (!htmlDeps) return;
// Register the Shiny HtmlDependencies
Shiny.renderDependenciesAsync([...htmlDeps]);
}, [htmlDeps]);

useEffect(() => {
const handleColumnSort = (
event: CustomEvent<{ sort: { col: number; desc: boolean }[] }>
Expand Down
15 changes: 14 additions & 1 deletion js/data-frame/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { StyleInfo } from "./style-info";

import type { HtmlDep } from "rstudio-shiny/srcts/types/src/shiny/render";

export type ValueOf<T> = T[keyof T];

export const EditModeEnum = {
Expand All @@ -9,7 +11,17 @@ export const EditModeEnum = {
export type EditMode = ValueOf<typeof EditModeEnum>;

export interface TypeHint {
type: "string" | "numeric" | "categorical" | "unknown" | "html";
type:
| "string"
| "numeric"
| "boolean"
| "date"
| "datetime"
| "duration"
| "object"
| "unknown"
| "html"
| "categorical";
}

export interface CategoricalTypeHint extends TypeHint {
Expand All @@ -34,6 +46,7 @@ export interface PandasData<TIndex> {
data: unknown[][];
options: DataGridOptions;
typeHints?: ReadonlyArray<TypeHint>;
htmlDeps?: ReadonlyArray<HtmlDep>;
}

export interface PatchInfo {
Expand Down
5 changes: 4 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ dependencies = [
"starlette",
"websockets>=10.0",
"python-multipart",
"htmltools>=0.5.2",
# "htmltools>=0.5.3.9002",
"htmltools@git+https://github.com/posit-dev/py-htmltools@main",
"click>=8.1.4;platform_system!='Emscripten'",
"markdown-it-py>=1.1.0",
"mdit-py-plugins>=0.3.0",
Expand All @@ -46,6 +47,8 @@ dependencies = [
"prompt-toolkit;platform_system!='Emscripten'",
"python-multipart>=0.0.7;platform_system!='Emscripten'",
"setuptools;python_version>='3.12'",
"narwhals>=1.9.0,<1.10.0",
schloerke marked this conversation as resolved.
Show resolved Hide resolved
"orjson>=3.10.7",
]

[project.optional-dependencies]
Expand Down
4 changes: 2 additions & 2 deletions shiny/_deprecated.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ class ShinyDeprecationWarning(RuntimeWarning):
warnings.simplefilter("always", ShinyDeprecationWarning)


def warn_deprecated(message: str):
warnings.warn(message, ShinyDeprecationWarning, stacklevel=3)
def warn_deprecated(message: str, stacklevel: int = 3):
warnings.warn(message, ShinyDeprecationWarning, stacklevel=stacklevel)


def render_text():
Expand Down
1 change: 0 additions & 1 deletion shiny/render/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
from ._data_frame_utils._selection import CellSelection
from ._data_frame_utils._types import ( # noqa: F401
StyleInfo,
DataFrameLikeT as _DataFrameLikeT, # pyright: ignore[reportUnusedImport]
)
from ._deprecated import ( # noqa: F401
RenderFunction, # pyright: ignore[reportUnusedImport]
Expand Down
Loading
Loading