Fix programmatic dataframe selections to return AttributeDictionary#14455
Fix programmatic dataframe selections to return AttributeDictionary#14455lukasmasuch merged 9 commits intodevelopfrom
Conversation
When setting dataframe selection state programmatically via session_state, wrap the validated state in AttributeDictionary to enable attribute-style access (e.g., event.selection.rows). Closes #14454 Co-Authored-By: Claude Opus 4.6 <[email protected]>
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
✅ PR preview is ready!
|
There was a problem hiding this comment.
Pull request overview
Fixes a regression where programmatic st.dataframe(..., on_select=...) selection state returned a plain dict instead of an AttributeDictionary, breaking attribute-style access like event.selection.rows.
Changes:
- Wrap programmatically-validated dataframe selection state in
AttributeDictionaryto match user-driven selection return behavior. - Add a Python unit test covering attribute-style access for programmatic selections.
- Extend the existing Playwright E2E coverage to assert attribute access works in the programmatic selection scenario.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
lib/streamlit/elements/arrow.py |
Ensures programmatic selection return values use AttributeDictionary for event.selection.* attribute access. |
lib/tests/streamlit/elements/arrow_dataframe_test.py |
Adds regression unit test verifying programmatic selection supports attribute-style access. |
e2e_playwright/st_dataframe_selections.py |
Emits selection.selection.rows to validate attribute access in the E2E app. |
e2e_playwright/st_dataframe_selections_test.py |
Adds E2E assertions for the new attribute-access output. |
There was a problem hiding this comment.
Summary
This PR fixes a bug (#14454) where programmatic dataframe selection via st.session_state returned a plain dict instead of an AttributeDictionary, causing AttributeError when users accessed selection attributes with dot notation (e.g., result.selection.rows). The fix is a one-line change in lib/streamlit/elements/arrow.py (line 999) wrapping validated_state in AttributeDictionary(...) before returning, consistent with the deserialize path at line 231. The PR includes a unit test and E2E test additions.
Reviewer consensus: 3/3 expected models completed their reviews. Two reviewers (claude-4.6-opus-high-thinking, gpt-5.3-codex-high) approved; one (gemini-3.1-pro) requested changes citing a potential edge case on subsequent reruns. After independent verification, the edge case does not manifest in practice (see Code Quality section).
Code Quality
The fix is minimal, correct, and follows existing codebase patterns. The DataframeSelectionSerde.deserialize method (line 231) already wraps its return in AttributeDictionary; this change makes the programmatic-set path consistent.
Resolved disagreement: gemini-3.1-pro raised a concern that on subsequent reruns (where widget_state.value_changed is False), widget_state.value at line 1002 would be a plain dict, causing the bug to reappear. After tracing the full widget lifecycle through SessionState.register_widget → _getitem → WStates.__getitem__, this concern is not valid:
- On every rerun (both in production and AppTest), widget states are sent back from the frontend (or simulated via
ElementTree.get_widget_states()), populating_new_widget_statewith serialized proto values. WStates.__getitem__deserializes these throughserde.deserialize(line 231), which wraps the result inAttributeDictionary.- Therefore
widget_state.valueat line 1002 is already anAttributeDictionaryon subsequent reruns through the normal deserialization path.
A defensive AttributeDictionary wrap at line 1002 is a reasonable hardening but is not required for correctness. See inline comments for details.
Test Coverage
Coverage is adequate for this bug fix:
- Unit test:
test_programmatic_selection_returns_attribute_dictionaryvalidates both initial (empty) and programmatically-set states with attribute-style access using AppTest. It would have failed before the fix, providing good regression coverage. Adding a third run (without modifying session state) would further strengthen coverage. - E2E test: The existing
test_programmatic_row_selection_via_session_stateis extended withexpect_prefixed_markdownassertions forselection.selection.rowsattribute access, covering both the initial and updated selection states.
Backwards Compatibility
No breaking changes. AttributeDictionary is a subclass of dict, so all existing code using dict-style access (result["selection"]["rows"]) continues to work. This change only adds the ability to also use attribute-style access on the programmatic-set path, matching the behavior of the non-programmatic path.
Security & Risk
No security concerns. The change wraps a return value in AttributeDictionary, a thin dict subclass. No routing, auth, WebSocket, embedding, asset serving, cross-origin, or session management behavior is affected. Regression risk is very low.
External test recommendation
- Recommend external_test: No
- Triggered categories: None
- Evidence:
lib/streamlit/elements/arrow.py: Pure return-type wrapping change; no routing, auth, WebSocket, embedding, asset, CORS, SiS, storage, or header changes.e2e_playwright/st_dataframe_selections*.py: Test additions only; no infrastructure changes.
- Suggested external_test focus areas: N/A
- Confidence: High
- Assumptions and gaps: None. The change is entirely internal to the Python return value of
st.dataframeand has no observable effect on network, embedding, or cross-origin behavior.
Accessibility
No frontend changes; not applicable.
Recommendations
- Consider adding a third
at.run()to the unit test (without modifyingst.session_state) to verify attribute access is preserved across subsequent reruns — this would strengthen regression coverage at minimal cost.
Verdict
APPROVED: A correct, minimal bug fix that makes programmatic dataframe selection return AttributeDictionary consistent with user-driven selections, with adequate unit and E2E test coverage. The edge case raised by one reviewer was independently verified to not manifest due to the widget deserialization lifecycle.
Consolidated AI review by claude-4.6-opus-high-thinking. Individual reviews: claude-4.6-opus-high-thinking (APPROVED), gemini-3.1-pro (CHANGES REQUESTED), gpt-5.3-codex-high (APPROVED). All 3/3 expected models completed reviews.
This review also includes 2 inline comment(s) on specific code lines.
- Add type: ignore comment for mypy error in E2E test - Move AppTest import to module level per test guidance - Add defensive AttributeDictionary wrapping on all return paths - Add third at.run() to verify selection persists across reruns Addresses review feedback from Copilot and AI review bots. Co-Authored-By: Claude Opus 4.6 <[email protected]>
## Describe your changes Automated snapshot updates for #14455 created via the snapshot autofix CI workflow. This workflow was triggered by adding the `update-snapshots` label to a PR after Playwright E2E tests failed with snapshot mismatches. **Updated snapshots:** 2 file(s)⚠️ **Please review the snapshot changes carefully** - they could mask visual bugs if accepted blindly. This PR targets a feature branch and can be merged without review approval. Co-authored-by: Streamlit Bot <[email protected]>
There was a problem hiding this comment.
Summary
This PR fixes a bug (#14454) where programmatic dataframe selection via st.session_state returned a plain dict instead of an AttributeDictionary, causing AttributeError when users accessed selection attributes with dot notation (e.g., event.selection.rows). The fix wraps the return value in AttributeDictionary on both code paths in st.dataframe(): the value_changed path (the actual bug) and the normal widget-state path (defensive hardening).
All three reviewers (claude-4.6-opus-high-thinking, gemini-3.1-pro, gpt-5.3-codex-high) are in full agreement on every aspect of this PR.
Code Quality
The fix is minimal, well-targeted, and follows existing patterns. Two lines changed in lib/streamlit/elements/arrow.py, each wrapping a return value in AttributeDictionary(...) with an appropriate cast. The pattern is consistent with other chart widgets (plotly_chart, vega_charts, deck_gl_json_chart) that all wrap selections in AttributeDictionary via their deserializers. This PR closes the gap where st.dataframe was the only widget that could return a plain dict on the programmatic-selection path. No structural or maintainability concerns were raised by any reviewer.
Test Coverage
All reviewers agree coverage is thorough and appropriately layered:
- Unit test (
test_programmatic_selection_returns_attribute_dictionary): UsesAppTest.from_functionto exercise both thevalue_changedpath (second run sets selection via session state) and the normal path (third run verifies persistence). Asserts attribute-style access works, which would fail withAttributeErroron the old code. - E2E test: Adds
expect_prefixed_markdownassertions in the existingtest_programmatic_row_selection_via_session_statetest to verify attribute access in the full stack, covering both the initial programmatic selection and after changing selection via a button click. - Updated snapshots for
st_dataframe-programmatic_col_cell_selectionare expected — the newst.writeline shifts the layout.
Backwards Compatibility
Fully backward compatible. AttributeDictionary extends dict, so all existing code using dict-style access (result["selection"]) continues to work unchanged. The fix restores the intended behavior (matching the deserializer path) that was broken specifically for the programmatic-selection path. All three reviewers concur.
Security & Risk
No security concerns. The change is limited to wrapping an already-validated return value in a dict subclass. No new dependencies, network calls, execution paths, or security-sensitive surfaces are modified. Regression risk is low and localized to dataframe selection event return typing/shape.
External test recommendation
- Recommend external_test: No
- Triggered categories: None
- Evidence:
lib/streamlit/elements/arrow.py: Pure data-wrapping change in the return path ofst.dataframe(). No routing, auth, embedding, asset serving, cross-origin, or security header changes.
- Suggested external_test focus areas: N/A
- Confidence: High (unanimous across all three reviewers)
- Assumptions and gaps: None. The change is entirely within the Python return value construction and has no network, iframe, or deployment-sensitive implications.
Accessibility
No frontend component changes — the fix is backend-only (Python return value wrapping). The E2E snapshot updates are layout shifts from the new st.write line, not visual changes to the dataframe component itself. No accessibility impact.
Recommendations
No blocking issues. Two minor observations (covered by inline comments):
- (Optional) The defensive wrapping on the normal widget-state path (line 1004) is redundant for current code since
DataframeSelectionSerde.deserialize()already returnsAttributeDictionary, but is reasonable as a guard against future changes. - (Optional) The import comment in the test file (line 38) restates default import conventions and could be removed.
Verdict
APPROVED: Clean, minimal bug fix that correctly wraps programmatic dataframe selection return values in AttributeDictionary for attribute-style access consistency. All three reviewers unanimously approve with no blocking issues, strong test coverage (unit + E2E), full backward compatibility, and no security concerns.
Consolidated AI review by claude-4.6-opus-high-thinking from 3 of 3 expected reviews (claude-4.6-opus-high-thinking, gemini-3.1-pro, gpt-5.3-codex-high). All reviews completed successfully.
This review also includes 2 inline comment(s) on specific code lines.
lib/streamlit/elements/arrow.py
Outdated
| return widget_state.value | ||
| # Defensive wrapping: ensure AttributeDictionary on all code paths for | ||
| # attribute-style access consistency (hardening for edge cases). | ||
| return cast("DataframeState", AttributeDictionary(widget_state.value)) |
There was a problem hiding this comment.
thought: This wrapping is defensive — widget_state.value already comes from DataframeSelectionSerde.deserialize() which returns an AttributeDictionary (line 231). Re-wrapping creates a shallow copy that behaves identically. The defensive approach is reasonable as it guards against future changes to the deserializer, but worth noting it's not strictly necessary for the current code path.
Address AI reviewer nitpick - top-level imports don't need justification. Co-Authored-By: Claude Opus 4.6 <[email protected]>
|
@lukasmasuch I tested this wheel. Although it doesn't give the attribute error anymore, it still won't set the state from a manually created selection state dict. (Same problem if I pass it as a default value instead of try to set it with Session State.) |
|
For reference, this is one of the examples I want to add to the docstrings: |
|
Yeah, I can reproduce this. The tricky part is the in-place modification of the |
When users tried to modify selections via nested assignment like
`st.session_state.data.selection = {"rows": [0]}`, the modification
happened in-place but wasn't detected by the session state system,
causing silent failures where selections weren't applied visually.
This change makes the dataframe selection state read-only by introducing
`ReadOnlyAttributeDictionary`, which extends `AttributeDictionary` to
maintain backwards compatibility for isinstance checks. When users attempt
nested modifications, they now receive a clear TypeError with guidance to
use full assignment: `st.session_state["key"] = {"selection": {...}}`.
Changes:
- Add `ReadOnlyAttributeDictionary` class in util.py
- Update arrow.py to use `ReadOnlyAttributeDictionary` for selection state
- Add comprehensive tests for the new read-only behavior
Co-Authored-By: Claude Opus 4.6 <[email protected]>
There was a problem hiding this comment.
Summary
This PR fixes #14454 where programmatically setting dataframe selection state via st.session_state returned a plain dict instead of an AttributeDictionary, causing AttributeError when users accessed selection attributes (e.g., event.selection.rows).
The fix introduces a ReadOnlyAttributeDictionary class in lib/streamlit/util.py that extends AttributeDictionary with read-only enforcement, and wraps all return paths in lib/streamlit/elements/arrow.py to consistently produce this type. Comprehensive unit tests and an E2E regression test are included.
Reviewed by: claude-4.6-opus-high-thinking (APPROVED), gemini-3.1-pro (CHANGES_REQUESTED), gpt-5.3-codex-high (CHANGES_REQUESTED). All three expected reviewers completed their reviews.
Code Quality
The implementation is clean and follows existing patterns in streamlit.util. The ReadOnlyAttributeDictionary class properly extends AttributeDictionary, maintaining isinstance compatibility, and comprehensively blocks mutation methods (__setitem__, __delitem__, clear, pop, popitem, setdefault, update). The __copy__/__deepcopy__ implementations are correct.
All three reviewers flagged a gap in read-only enforcement for dict-style nested access. Currently, __getattr__ wraps nested dicts dynamically (line 132 of util.py), but __getitem__ and get() inherit from dict and return the raw inner dict. This means d["selection"]["rows"] = [3, 4] silently succeeds, bypassing read-only protection. Two reviewers (gemini, gpt) considered this blocking; one (claude) acknowledged it as an acceptable trade-off. The fix — wrapping nested dicts at init time — is small, clean, and closes the gap (see inline comment for the suggested approach).
Additionally, the error message string _READ_ONLY_ERROR_MSG uses {{ and }} in a plain string literal (not an f-string), causing these to display literally as doubled braces rather than single braces. This is a cosmetic bug in the error output.
Test Coverage
Test coverage is thorough across all three tiers, and all reviewers agreed on this:
- Unit tests (
util_test.py): 13 tests covering attribute/dict read access, all mutation methods, nested modification, copy/deepcopy, isinstance checks, and JSON serialization. - Unit tests (
arrow_dataframe_test.py): TwoAppTest-based tests verifying programmatic selection returnsAttributeDictionarywith working attribute access across multiple reruns, and that state is read-only. - E2E tests (
st_dataframe_selections_test.py): Regression test verifying attribute access works viaselection.selection.rowsin both initial and updated programmatic selections.
One gap: the tests only verify attribute-style nested modification is blocked (d.selection.rows = ...) but not dict-style (d["selection"]["rows"] = ...). Tests for the latter should be added once the init-time wrapping fix is applied.
Backwards Compatibility
All reviewers agreed this change is backwards compatible. ReadOnlyAttributeDictionary extends AttributeDictionary extends dict, so all existing isinstance checks continue to work. The only behavioral change is that mutation of returned selection state now raises TypeError — previously, mutations would silently succeed but have no effect on the UI, so any code doing this was already buggy.
Security & Risk
No security concerns. All reviewers agreed this change is purely Python-side data wrapping with no impact on WebSocket handling, server endpoints, file serving, authentication, or frontend rendering.
External test recommendation
- Recommend external_test: No
- Triggered categories: None
- Evidence:
lib/streamlit/util.py: New utility class — pure Python data wrapping, no network/embedding/auth impact.lib/streamlit/elements/arrow.py: Return value wrapping only — no changes to protobuf serialization, widget registration, or frontend communication.
- Suggested external_test focus areas: N/A
- Confidence: High (all three reviewers unanimously agreed)
- Assumptions and gaps: None. Changes are confined to Python-side return value wrapping.
Accessibility
No frontend changes in this PR — only backend Python code and snapshot updates. No accessibility impact. All reviewers agreed.
Recommendations
-
Wrap nested dicts at init time (blocking — all reviewers flagged this): Add an
__init__toReadOnlyAttributeDictionarythat recursively converts nesteddictvalues toReadOnlyAttributeDictionaryusingdict.__setitem__. This ensures dict-style access (d["selection"]),get(),values(), and iteration all return read-only objects, closing the mutation gap. -
Fix double braces in error message: Replace
{{with{and}}with}in_READ_ONLY_ERROR_MSG(lines 110, 112 ofutil.py) so the example code renders correctly. -
Add dict-style nested mutation tests: After applying fix #1, add test assertions for
d["selection"]["rows"] = [0]in bothutil_test.pyandarrow_dataframe_test.py. -
Consider applying
ReadOnlyAttributeDictionaryto other chart elements (follow-up):vega_charts.py,plotly_chart.py, anddeck_gl_json_chart.pystill use mutableAttributeDictionaryfor selection state. A follow-up PR could bring consistency across all chart elements.
Verdict
CHANGES_REQUESTED: Well-implemented bugfix with thorough test coverage and strong reviewer consensus. Two of three reviewers flagged the dict-style nested access gap as blocking — wrapping nested dicts at init time is a small, low-risk fix that should be applied before merge. The double-brace issue in the error message is non-blocking but should also be addressed.
This is a consolidated AI review by claude-4.6-opus-high-thinking, synthesizing reviews from claude-4.6-opus-high-thinking, gemini-3.1-pro, and gpt-5.3-codex-high.
This review also includes 4 inline comment(s) on specific code lines.
- Fix double braces in error message (now displays correctly) - Add __getitem__ override to wrap nested dicts for bracket access - Add __ior__ override for |= operator - Add test for nested bracket-style modification being read-only - Consolidate mutation tests into parameterized format - Simplify comment in arrow.py Co-Authored-By: Claude Opus 4.6 <[email protected]>
There was a problem hiding this comment.
Summary
This PR fixes #14454 where programmatic dataframe selections via st.session_state returned a plain dict instead of an AttributeDictionary, causing AttributeError when users accessed selection attributes (e.g., event.selection.rows).
The fix introduces a ReadOnlyAttributeDictionary class (extending AttributeDictionary) and uses it in all return paths of st.dataframe when on_select is enabled. Beyond restoring attribute-style access, the read-only enforcement prevents users from modifying widget state in-place — mutations that previously silently succeeded but never triggered UI updates — and provides a clear error message guiding them toward correct usage patterns.
Reviewer consensus: All three reviewers (claude-4.6-opus-high-thinking, gemini-3.1-pro, gpt-5.3-codex-high) agreed this is a focused, well-implemented bug fix. Two approved outright; one requested changes based on a mutable-leaf limitation assessed here as non-blocking (see Recommendations).
Code Quality
The code is well-structured and follows existing Streamlit patterns. All three reviewers confirmed high code quality:
ReadOnlyAttributeDictionaryproperly inherits fromAttributeDictionary, preserving backward compatibility forisinstancechecks.- All dict mutation operations are comprehensively blocked (
__setitem__,__delitem__,clear,pop,popitem,setdefault,update,__ior__,__setattr__). - Custom
__copy__and__deepcopy__ensure the read-only wrapper survives copy operations. - The
_READ_ONLY_ERROR_MSGconstant is clear, actionable, and follows project conventions for private module-level declarations. - The three return paths in
arrow.py(deserialize, programmatic selection, normal widget state) are consistently wrapped.
One minor optimization opportunity exists in __getattr__ (covered in inline comments): the current implementation creates unnecessary intermediate AttributeDictionary objects due to the super-call chain. This is functionally correct but slightly wasteful.
Test Coverage
All reviewers confirmed thorough, well-layered test coverage:
- Unit tests (
util_test.py): Comprehensive coverage ofReadOnlyAttributeDictionaryincluding attribute/dict access,isinstancecompatibility, all mutation operations (via@pytest.mark.parametrize), bracket access returning read-only nested dicts, deep/shallow copy, and JSON serialization. - Unit tests (
arrow_dataframe_test.py): End-to-endAppTestverification across multiple runs (initial, programmatic set, persistence) plus read-only enforcement via both attribute and bracket access. - E2E tests: Adds
expect_prefixed_markdownassertions for attribute access in the existing programmatic selection test.
All tests follow project conventions including meaningful docstrings and parameterization patterns.
Backwards Compatibility
This change is backward compatible for intended usage patterns:
ReadOnlyAttributeDictionaryextendsAttributeDictionary, so existingisinstancechecks continue to work.- All read-access patterns (attribute and bracket style) work identically to before.
- The only behavioral change is that in-place mutation of returned state now raises
TypeErrorinstead of silently succeeding without triggering UI updates. This is a positive breaking change that replaces confusing silent failure with an actionable error message. json.dumps()serialization continues to work since the class is adictsubclass.
Security & Risk
No security concerns identified. All three reviewers confirmed:
- No new dependencies added.
- No changes to WebSocket handling, server endpoints, authentication, or session management.
- No changes to file handling, HTML/Markdown rendering, or JavaScript execution.
- Changes are entirely within Python-side return-value wrapping logic.
External test recommendation
- Recommend external_test: No
- Triggered categories: None
- Evidence: All changed files (
lib/streamlit/util.py,lib/streamlit/elements/arrow.py, test files) are limited to Python return-value wrapping. No routing, auth, websocket/session transport, embedding, assets, cross-origin behavior, or security header changes. - Suggested external_test focus areas: None needed.
- Confidence: High (unanimous across all three reviewers)
- Assumptions and gaps: None. Changes are purely to internal Python return types.
Accessibility
No frontend changes are included in this PR. Snapshot updates are a consequence of E2E test app script changes (adding st.write lines), not visual or interactive changes. No accessibility concerns. All reviewers concurred.
Recommendations
- Follow-up: Mutable leaf objects —
ReadOnlyAttributeDictionarydoes not freeze mutable leaf values such as lists.event.selection.rows.append(1)will silently mutate without triggering a UI update or raising an error. This is a pre-existing limitation (not introduced by this PR) and Python lacks a built-in immutable list type. One reviewer (gpt-5.3-codex-high) flagged this as blocking; the other two acknowledged it as a known limitation. Assessment: This is a valid improvement opportunity but is out of scope for this bug-fix PR. Consider returningtupleinstead oflistfor leaf sequences in a follow-up. - Minor optimization in
__getattr__— Delegate directly toself[key]instead ofsuper().__getattr__(key)to avoid triple-wrapping through the parent class. See inline comment for details. - Apply to other chart widgets — Consider applying
ReadOnlyAttributeDictionaryto other chart elements (st.plotly_chart,st.pydeck_chart,st.vega_lite_chart) that useAttributeDictionaryfor selection state and suffer from the same in-place mutation confusion.
Verdict
APPROVED: Clean, well-tested bug fix that correctly wraps dataframe selection state in a read-only attribute dictionary, fixing attribute access on programmatic selections while adding helpful guardrails against in-place mutation. The mutable-leaf limitation noted by one reviewer is a pre-existing condition outside this PR's scope and is recommended as a follow-up.
Reviewer agreement summary
| Reviewer | Verdict | Notes |
|---|---|---|
| claude-4.6-opus-high-thinking | APPROVED | No blocking issues |
| gemini-3.1-pro | APPROVED | No blocking issues |
| gpt-5.3-codex-high | CHANGES_REQUESTED | Mutable leaf gap (assessed as non-blocking follow-up) |
This is a consolidated AI review by claude-4.6-opus-high-thinking, synthesizing reviews from claude-4.6-opus-high-thinking, gemini-3.1-pro, and gpt-5.3-codex-high. Please verify the feedback and use your judgment.
This review also includes 2 inline comment(s) on specific code lines.
|
If you look at the originial bug report, it was also not working when I created the entire dictionary (with or without the empty "rows" and "columns"). If we have any limitation on how to set and modify state because of technical difficulty, I can also just document it. I was trying to test out some different cases and cover 1) setting a default selection, 2) overriding the entire selection, 3) appending to/removing from the existing selection |
…tr__ Fix __getattr__ to delegate directly to self[key] instead of calling super().__getattr__(key). The previous implementation caused nested dicts to be wrapped twice in ReadOnlyAttributeDictionary because: 1. AttributeDictionary.__getattr__ calls self.__getitem__ 2. ReadOnlyAttributeDictionary.__getitem__ wraps dicts 3. Then __getattr__ was wrapping again Also added test to verify attribute access returns ReadOnlyAttributeDictionary. Co-Authored-By: Claude Opus 4.6 <[email protected]>
|
(I had a typo in my example.) I just tried the latest wheel and I think it's working. |
Resolved conflicts: - lib/streamlit/elements/arrow.py: Combined single-row-required auto-select logic from develop with ReadOnlyAttributeDictionary from HEAD - lib/tests/streamlit/elements/arrow_dataframe_test.py: Kept both test sets (single-row-required tests and read-only state tests) - e2e_playwright/__snapshots__/*/st_dataframe-programmatic_col_cell_selection[chromium].png: Accepted develop's version Co-Authored-By: Claude Opus 4.6 <[email protected]>
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Describe your changes
Fixes programmatic dataframe selection returning a plain
dictinstead ofAttributeDictionary, which causedAttributeErrorwhen accessing selection attributes (e.g.,event.selection.rows).Changes:
ReadOnlyAttributeDictionaryto match the behavior of user-driven selectionsReadOnlyAttributeDictionaryextendsAttributeDictionarysoisinstancechecks still workGitHub Issue Link (if applicable)
Testing Plan
lib/tests/streamlit/elements/arrow_dataframe_test.py::test_programmatic_selection_returns_attribute_dictionary— Verifies programmatic selection returnsAttributeDictionarywith working attribute accesslib/tests/streamlit/elements/arrow_dataframe_test.py::test_selection_state_is_read_only— Verifies selection state is read-onlylib/tests/streamlit/util_test.py::TestReadOnlyAttributeDictionary— Comprehensive tests for the newReadOnlyAttributeDictionaryclasse2e_playwright/st_dataframe_selections_test.py— Verifies attribute access works viaselection.selection.rowsContribution License Agreement
By submitting this pull request you agree that all contributions to this project are made under the Apache 2.0 license.
Agent metrics