-
Notifications
You must be signed in to change notification settings - Fork 63
feat: Enable multi-column sorting in anywidget mode #2360
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
base: main
Are you sure you want to change the base?
Conversation
- Replaced RLock with Lock in TableWidget for a stricter threading model. - Refactored _set_table_html to defer state updates (specifically self.page) until the lock is released. - This ensures that when a page update triggers an observer, the subsequent re-entry into _set_table_html is not nested within the original lock's scope, avoiding deadlocks. - Added a targeted unit test to verify that navigating to an invalid page correctly resets to a valid page without deadlocking.
- Added a mock fallback for 'traitlets' in bigframes/display/anywidget.py to avoid NameError during class definition when the package is not installed. - Moved 'pytest.importorskip' calls in tests/unit/display/test_anywidget.py to the top to ensure tests are skipped before attempting any imports from the module under test. - Added noqa: E402 to suppress flake8 warnings for imports after importorskip.
…cies missing - Reverted changes to bigframes/display/anywidget.py to keep source code clean (no mock injection). - Refactored tests/unit/display/test_anywidget.py to avoid top-level import of bigframes.display.anywidget. - Imported TableWidget inside the test method, ensuring pytest.importorskip runs first and skips the test file if dependencies are missing, preventing the NameError crash.
- Refactored test_navigation_to_invalid_page_resets_to_valid_page_without_deadlock to follow established project conventions. - Use internal import from bigframes.display.anywidget. - Employ bigframes.option_context for setting widget state. - Improved docstring to follow behavior-driven (Given/When/Then) style.
# Conflicts: # bigframes/display/html.py
Updates the TableWidget to support sorting by multiple columns. - Python: Changes sort_column/sort_ascending traits to lists. - JS: Adds Shift+Click support for multi-column sorting. - JS: Displays sort indicators for all sorted columns. - Tests: Adds unit tests for multi-column sorting logic.
|
Check out this pull request on See visual diffs & provide feedback on Jupyter Notebooks. Powered by ReviewNB |
bigframes/display/anywidget.py
Outdated
| sort_columns = traitlets.List(traitlets.Unicode(), []).tag(sync=True) | ||
| sort_ascending = traitlets.List(traitlets.Bool(), []).tag(sync=True) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suspect that this data structure allows content that make little sense in practice. For example:
sort_columns: ["my_col", "my_col"] # Same column appears twice
sort_ascending: [True, False]
Does it make sense to merge these two fields into a dictionary (ordered map), which maps the column name to whether it's ascending?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the suggestion! I’ve refactored the two fields into a single sort_context traitlet to ensure atomic updates and maintain a consistent data state.
Regarding the concern about practical validity: the frontend logic is designed to ensure the list remains sane. When a user clicks a column header, we verify if the column is orderable and handle the list logic so that the same column cannot be inserted multiple times (it instead cycles through states or is removed).
While the original design was safe within these UI constraints, I agree that merging them is a cleaner architectural choice. I chose a List of Dictionaries (e.g., {'column': 'col1', 'ascending': True}]) instead of a Map for a few reasons:
- Order Precedence: In multi-column sorting, the order of columns matters. A List explicitly guarantees that sort priority (primary, secondary, etc.) is preserved during synchronization, whereas JSON dictionary keys are technically unordered.
- Extensibility: This structure is much more flexible. It allows us to add additional per-column sort parameters in the future (like nulls_first or na_position) without breaking the schema.
- Validation: It ensures that every column entry is always bundled with its corresponding direction, removing any risk of length mismatch.
The result is verified at here: screen/BxN5ZX3QTG6TEU8 (the description box is updated accordingly)
This update brings multi-column sorting capabilities to the interactive DataFrame display (anywidget mode), allowing for more powerful data exploration directly within our notebook.
What's New:
How to use:
Verified at here: screen/BxN5ZX3QTG6TEU8
Fixes #<459835971> 🦕