Skip to content

Commit 40a33d5

Browse files
committed
Merge branch 'add-more-dataframe-backends' of https://github.com/posit-dev/py-shiny into add-more-dataframe-backends
2 parents 6e2970b + c05105d commit 40a33d5

File tree

23 files changed

+937
-205
lines changed

23 files changed

+937
-205
lines changed

.github/workflows/pytest.yaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,3 +242,19 @@ jobs:
242242
name: "playright-examples-${{ runner.os }}-${{ matrix.python-version }}-results"
243243
path: test-results/
244244
retention-days: 5
245+
246+
test-narwhals-integration:
247+
runs-on: ubuntu-latest
248+
steps:
249+
- uses: actions/checkout@v4
250+
with:
251+
fetch-depth: 0
252+
- name: Setup py-shiny
253+
id: install
254+
uses: ./.github/py-shiny/setup
255+
- name: Run test commands
256+
env:
257+
UV_SYSTEM_PYTHON: 1
258+
run: |
259+
make narwhals-install-shiny
260+
make narwhals-test-integration

CHANGELOG.md

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1717

1818
### New features
1919

20-
* 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)
20+
* New features for `@render.data_frame`:
21+
22+
* 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 now leverage the `narwhals` API to be data frame agnostic. (#1570)
23+
24+
* Added `.data_patched()` reactive calculation that applies all `.cell_patches()` to `.data()`. (#1719)
25+
26+
* Added `.update_cell_value()` method to programmatically update the contents of a data frame cell. (#1719)
27+
28+
* Added `.update_data()` method to update the rendered data without resetting any user sort or filter. Note, all user edits will be forgotten. (#1719)
29+
30+
* 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)
2131

2232
### Other changes
2333

@@ -41,8 +51,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4151

4252
* Added `.expect_class()` and `.expect_multiple()` for `Accordion` in `shiny.playwright.controllers` (#1710)
4353

44-
* 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)
45-
4654
### Bug fixes
4755

4856
* A few fixes for `ui.Chat()`, including:
@@ -66,6 +74,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6674

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

77+
* Fixed output controller `OutputDataFrame` in `shiny.playwright.controller` to correctly assert the number of rows in `.expect_nrow()` as the total number of virtual rows, not the number of currently displaying rows. (#1719)
78+
6979

7080
## [1.1.0] - 2024-09-03
7181

Makefile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ SUB_FILE:=
150150
PYTEST_BROWSERS:= --browser webkit --browser firefox --browser chromium
151151
PYTEST_DEPLOYS_BROWSERS:= --browser chromium
152152

153+
153154
# Full test path to playwright tests
154155
TEST_FILE:=tests/playwright/$(SUB_FILE)
155156
# Default `make` values that shouldn't be directly used; (Use `TEST_FILE` instead!)
@@ -247,3 +248,10 @@ upgrade-html-deps: FORCE ## Upgrade Shiny's HTMLDependencies
247248
exit 1; \
248249
fi
249250
@scripts/htmlDependencies.R
251+
252+
narwhals-install-shiny: FORCE
253+
@echo "-------- Install py-shiny ----------"
254+
$(MAKE) ci-install-deps
255+
narwhals-test-integration: FORCE
256+
@echo "-------- Running py-shiny tests ----------"
257+
$(MAKE) test playwright TEST_FILE="tests/playwright/shiny/components/data_frame" PYTEST_BROWSERS="--browser chromium"

docs/_quartodoc-core.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ quartodoc:
99
renderer:
1010
style: _renderer.py
1111
show_signature_annotations: false
12+
table_style: description-list
1213
sections:
1314
- title: Page containers
1415
desc: Create a user interface page container.
@@ -357,3 +358,4 @@ quartodoc:
357358
contents:
358359
- name: experimental.ui.card_image
359360
dynamic: false
361+

docs/_quartodoc-express.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ quartodoc:
99
renderer:
1010
style: _renderer.py
1111
show_signature_annotations: false
12+
table_style: description-list
1213
sections:
1314
- title: Input components
1415
desc: Gather user input.

docs/_renderer.py

Lines changed: 8 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@
1212
from griffe import (
1313
Alias,
1414
DocstringAttribute,
15-
DocstringParameter,
16-
DocstringSectionParameters,
1715
DocstringSectionText,
1816
Expr,
1917
ExprName,
@@ -22,8 +20,8 @@
2220
)
2321
from plum import dispatch
2422
from quartodoc import MdRenderer
25-
from quartodoc.pandoc.blocks import DefinitionList
2623
from quartodoc.renderers.base import convert_rst_link_to_md, sanitize
24+
from quartodoc.renderers.md_renderer import ParamRow
2725

2826
# from quartodoc.ast import preview
2927

@@ -101,12 +99,12 @@ def render_annotation(self, el: str):
10199
# TODO-future; Can be removed once we use quartodoc 0.3.5
102100
# Related: https://github.com/machow/quartodoc/pull/205
103101
@dispatch
104-
def render(self, el: DocstringAttribute):
105-
row = [
106-
sanitize(el.name),
107-
self.render_annotation(el.annotation),
108-
sanitize(el.description or "", allow_markdown=True),
109-
]
102+
def render(self, el: DocstringAttribute) -> ParamRow:
103+
row = ParamRow(
104+
el.name,
105+
el.description or "",
106+
annotation=self.render_annotation(el.annotation),
107+
)
110108
return row
111109

112110
@dispatch
@@ -170,28 +168,6 @@ def summarize(self, obj: Union[Object, Alias]) -> str:
170168

171169
return ""
172170

173-
# Consolidate the parameter type info into a single column
174-
@dispatch
175-
def render(self, el: DocstringParameter):
176-
param = f'<span class="parameter-name">{el.name}</span>'
177-
annotation = self.render_annotation(el.annotation)
178-
if annotation:
179-
param = f'{param}<span class="parameter-annotation-sep">:</span> <span class="parameter-annotation">{annotation}</span>'
180-
if el.default:
181-
param = f'{param} <span class="parameter-default-sep">=</span> <span class="parameter-default">{el.default}</span>'
182-
183-
# Wrap everything in a code block to allow for links
184-
param = "<code>" + param + "</code>"
185-
186-
return (param, el.description)
187-
188-
@dispatch
189-
def render(self, el: DocstringSectionParameters):
190-
rows = list(map(self.render, el.value))
191-
# rows is a list of tuples of (<parameter>, <description>)
192-
193-
return str(DefinitionList(rows))
194-
195171
@dispatch
196172
def signature(self, el: Function, source: Optional[Alias] = None):
197173
if el.name == "__call__":
@@ -279,7 +255,7 @@ def read_file(file: str | Path, root_dir: str | Path | None = None) -> FileConte
279255

280256

281257
def check_if_missing_expected_example(el, converted):
282-
if re.search(r"(^|\n)#{2,6} Examples\n", converted):
258+
if re.search(r"(^|\n)#{2,6} Examples", converted):
283259
# Manually added examples are fine
284260
return
285261

js/data-frame/cell-edit-map.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ export const useCellEditMap = () => {
4848
cellEditMap,
4949
// setCellEditMap,
5050
setCellEditMapAtLoc,
51+
resetCellEditMap: () => {
52+
setCellEditMap(new Map<string, CellEdit>());
53+
},
5154
} as const;
5255
};
5356

js/data-frame/data-update.tsx

Lines changed: 63 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,62 @@ export type CellPatchPy = {
1717
// prev: unknown;
1818
};
1919

20+
type SetDataFn = (fn: (draft: unknown[][]) => void) => void;
21+
22+
export function addPatchToData({
23+
setData,
24+
newPatches,
25+
setCellEditMapAtLoc,
26+
}: {
27+
setData: SetDataFn;
28+
newPatches: CellPatch[];
29+
setCellEditMapAtLoc: SetCellEditMapAtLoc;
30+
}): void {
31+
// Update data
32+
setData((draft) => {
33+
newPatches.forEach(({ rowIndex, columnIndex, value }) => {
34+
draft[rowIndex]![columnIndex] = value;
35+
});
36+
});
37+
// Set the new patches in cell edit map info
38+
newPatches.forEach(({ rowIndex, columnIndex, value }) => {
39+
setCellEditMapAtLoc(rowIndex, columnIndex, (obj_draft) => {
40+
obj_draft.value = value;
41+
obj_draft.state = CellStateEnum.EditSuccess;
42+
// Remove save_error if it exists
43+
obj_draft.errorTitle = undefined;
44+
});
45+
});
46+
}
47+
48+
export function cellPatchPyArrToCellPatchArr(
49+
patchesPy: CellPatchPy[]
50+
): CellPatch[] {
51+
const patches: CellPatch[] = patchesPy.map(
52+
(patch: CellPatchPy): CellPatch => {
53+
return {
54+
rowIndex: patch.row_index,
55+
columnIndex: patch.column_index,
56+
value: patch.value,
57+
};
58+
}
59+
);
60+
return patches;
61+
}
62+
63+
export function cellPatchArrToCellPatchPyArr(
64+
patches: CellPatch[]
65+
): CellPatchPy[] {
66+
const patchesPy: CellPatchPy[] = patches.map((patch) => {
67+
return {
68+
row_index: patch.rowIndex,
69+
column_index: patch.columnIndex,
70+
value: patch.value,
71+
};
72+
});
73+
return patchesPy;
74+
}
75+
2076
export function updateCellsData({
2177
patchInfo,
2278
patches,
@@ -31,20 +87,13 @@ export function updateCellsData({
3187
onSuccess: (values: CellPatch[]) => void;
3288
onError: (err: string) => void;
3389
columns: readonly string[];
34-
setData: (fn: (draft: unknown[][]) => void) => void;
90+
setData: SetDataFn;
3591
setCellEditMapAtLoc: SetCellEditMapAtLoc;
3692
}) {
3793
// // Skip page index reset until after next rerender
3894
// skipAutoResetPageIndex();
3995

40-
const patchesPy: CellPatchPy[] = patches.map((patch) => {
41-
return {
42-
row_index: patch.rowIndex,
43-
column_index: patch.columnIndex,
44-
value: patch.value,
45-
// prev: patch.prev,
46-
};
47-
});
96+
const patchesPy = cellPatchArrToCellPatchPyArr(patches);
4897

4998
makeRequestPromise({
5099
method: patchInfo.key,
@@ -70,21 +119,7 @@ export function updateCellsData({
70119
}
71120
newPatchesPy = newPatchesPy as CellPatchPy[];
72121

73-
const newPatches: CellPatch[] = newPatchesPy.map(
74-
(patch: CellPatchPy): CellPatch => {
75-
return {
76-
rowIndex: patch.row_index,
77-
columnIndex: patch.column_index,
78-
value: patch.value,
79-
};
80-
}
81-
);
82-
83-
setData((draft) => {
84-
newPatches.forEach(({ rowIndex, columnIndex, value }) => {
85-
draft[rowIndex]![columnIndex] = value;
86-
});
87-
});
122+
const newPatches = cellPatchPyArrToCellPatchArr(newPatchesPy);
88123

89124
// Set the old patches locations back to success state
90125
// This may be overkill, but it guarantees that the incoming patches exit the saving state
@@ -99,15 +134,10 @@ export function updateCellsData({
99134
obj_draft.errorTitle = undefined;
100135
});
101136
});
102-
// Set the new patches
103-
newPatches.forEach(({ rowIndex, columnIndex, value }) => {
104-
setCellEditMapAtLoc(rowIndex, columnIndex, (obj_draft) => {
105-
obj_draft.value = value;
106-
obj_draft.state = CellStateEnum.EditSuccess;
107-
// Remove save_error if it exists
108-
obj_draft.errorTitle = undefined;
109-
});
110-
});
137+
138+
// Update data and cell edit map with new patches
139+
addPatchToData({ setData, newPatches, setCellEditMapAtLoc });
140+
111141
onSuccess(newPatches);
112142
})
113143
.catch((err: string) => {

0 commit comments

Comments
 (0)