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

Fix segfault take 2 #129

Merged
merged 6 commits into from
Sep 24, 2023
Merged

Fix segfault take 2 #129

merged 6 commits into from
Sep 24, 2023

Conversation

hanjinliu
Copy link
Owner

@hanjinliu hanjinliu commented Sep 24, 2023

Summary by CodeRabbit

  • Refactor: Enhanced the make_tabulous_viewer fixture in tests/conftest.py to support different types of viewers (main, widget, and magic), improving test flexibility.
  • Test: Updated various test functions in tests/test_keyboard_ops.py and tests/test_main_window.py to use the refactored make_tabulous_viewer fixture.
  • Refactor: Simplified tests/test_core.py by removing unused dependencies and test functions.
  • Chore: Commented out unused code in tests/test_colormap.py and tests/test_magicwidget.py.
  • New Feature: Added methods for connecting and disconnecting signals in tabulous/_qt/_table/_base/_table_base.py, enhancing control over event handling.
  • Refactor: Improved cleanup steps in tests/conftest.py to ensure proper deletion of viewer objects and processing of events.

@coderabbitai
Copy link

coderabbitai bot commented Sep 24, 2023

Rate Limit Exceeded

You have exceeded the limit for the number of files or commits that can be reviewed per hour. Please wait 11 minutes and 59 seconds before requesting another review.

To re-trigger a review, convert this PR to a draft and then mark it as ready for review again. Alternatively, you can push a new commit to this PR.

@coderabbitai
Copy link

coderabbitai bot commented Sep 24, 2023

Rate Limit Exceeded

You have exceeded the limit for the number of files or commits that can be reviewed per hour. Please wait 4 minutes and 40 seconds before requesting another review.

To re-trigger a review, convert this PR to a draft and then mark it as ready for review again. Alternatively, you can push a new commit to this PR.

@coderabbitai
Copy link

coderabbitai bot commented Sep 24, 2023

Walkthrough

This pull request introduces significant changes to the testing suite and some core components of the application. It enhances flexibility by allowing different types of viewers to be created in tests, refactors code by removing unused dependencies and test functions, and improves signal handling in the table viewer.

Changes

File(s) Summary
tests/conftest.py Enhanced the make_tabulous_viewer fixture to support different types of viewers. Added cleanup steps in the session fixture.
tests/test_colormap.py, tests/test_core.py, tests/test_magicwidget.py Removed unused dependencies and test functions, indicating a refactor or simplification of the code.
tests/test_keyboard_ops.py, tests/test_main_window.py Modified test functions to use the enhanced make_tabulous_viewer fixture. Replaced TableViewerWidget with TableViewerBase.
tabulous/_qt/.../_table_base.py, tabulous/widgets/_mainwindow.py Added methods for connecting and disconnecting signals. Improved event handling in paste_data and _pass_pytable functions.

🐇 "In the land of code, where logic is king,

Changes are made, improvements they bring.

Tests now can spawn viewers galore,

Unused imports shown the door.

Signals connect, then take flight,

Making our tables just right.

So here's to the devs, their efforts we cheer,

For making our codebase more clear!" 🎉


Tips

Chat with CodeRabbit Bot (@coderabbitai)

  • Mention @coderabbitai in any review comment for bot assistance.
  • Note: Review comments are made on code diffs or files, not on the PR overview.

Pause Incremental Reviews

  • Insert @coderabbitai: ignore in the PR description to halt the bot's ongoing reviews. Remove the line to resume.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 2

Commits Files that changed from the base of the PR and between d76ca97 and 3d83f97.
Files selected for processing (6)
  • tests/conftest.py (1 hunks)
  • tests/test_colormap.py (1 hunks)
  • tests/test_core.py (1 hunks)
  • tests/test_keyboard_ops.py (3 hunks)
  • tests/test_magicwidget.py (1 hunks)
  • tests/test_main_window.py (5 hunks)
Files skipped from review due to trivial changes (3)
  • tests/test_colormap.py
  • tests/test_core.py
  • tests/test_magicwidget.py
Additional comments (Suppressed): 8
tests/conftest.py (1)
  • 6-23: The new fixture make_tabulous_viewer now supports creation of different types of viewers (TableViewer, TableViewerWidget, and MagicTableViewer) based on the cls parameter. Ensure that all test functions using this fixture have been updated to handle this change, especially if they rely on specific properties or methods unique to a particular viewer type.
tests/test_keyboard_ops.py (3)
  • 9-13: The make_tabulous_viewer fixture is now being used to create the TableViewer instance instead of directly instantiating it. Ensure that this fixture is correctly set up and provides a valid TableViewer instance for the tests.

  • 20-26: Similar to the previous comment, the make_tabulous_viewer fixture is now being used to create the TableViewer instance. Verify that this fixture is correctly set up and provides a valid TableViewer instance for the tests.

  • 42-46: Again, the make_tabulous_viewer fixture is now being used to create the TableViewer instance. Ensure that this fixture is correctly set up and provides a valid TableViewer instance for the tests.

tests/test_main_window.py (4)
  • 13-18: The make_tabulous_viewer fixture now accepts a string parameter viewer_cls instead of a class type. Ensure that the fixture correctly handles these string inputs and creates the appropriate viewer objects.

  • 74-79: The make_tabulous_viewer fixture now accepts a string parameter viewer_cls instead of a class type. Ensure that the fixture correctly handles these string inputs and creates the appropriate viewer objects.

  • 91-96: The make_tabulous_viewer fixture now accepts a string parameter viewer_cls instead of a class type. Ensure that the fixture correctly handles these string inputs and creates the appropriate viewer objects.

  • 110-115: The make_tabulous_viewer fixture now accepts a string parameter viewer_cls instead of a class type. Ensure that the fixture correctly handles these string inputs and creates the appropriate viewer objects.

Comment on lines +12 to +20
cls: Literal["main", "widget", "magic"] = "main",
show: bool = False,
):
if cls == "main":
viewer = TableViewer(show=show)
elif cls == "widget":
viewer = TableViewerWidget(show=show)
elif cls == "magic":
viewer = MagicTableViewer(show=show)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code block lacks error handling for invalid cls values. If a value other than "main", "widget", or "magic" is passed, the function will complete without creating a viewer, which could lead to unexpected behavior in the tests. Consider adding an else clause to raise an exception when an unsupported cls value is provided.

    ):
        if cls == "main":
            viewer = TableViewer(show=show)
        elif cls == "widget":
            viewer = TableViewerWidget(show=show)
        elif cls == "magic":
            viewer = MagicTableViewer(show=show)
+        else:
+            raise ValueError(f"Unsupported viewer type: {cls}")
         viewers.add(viewer)
         return viewer

Comment on lines +28 to 33
@pytest.mark.parametrize("viewer_cls", classes_to_test)
def test_renaming(make_tabulous_viewer, viewer_cls: str):
viewer: TableViewerBase = make_tabulous_viewer(viewer_cls, show=False)
table0 = viewer.add_table(test_data, name="Data")
assert table0.name == "Data"
assert get_tabwidget_tab_name(viewer, 0) == "Data"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test function test_renaming no longer accepts a pos parameter. If this parameter was used to test different tab positions, consider reintroducing it to ensure comprehensive testing.

@coderabbitai
Copy link

coderabbitai bot commented Sep 24, 2023

Rate Limit Exceeded

You have exceeded the limit for the number of files or commits that can be reviewed per hour. Please wait 2 minutes and 45 seconds before requesting another review.

To re-trigger a review, convert this PR to a draft and then mark it as ready for review again. Alternatively, you can push a new commit to this PR.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 2

Commits Files that changed from the base of the PR and between 3d83f97 and 6728cc8.
Files selected for processing (2)
  • tests/conftest.py (2 hunks)
  • tests/test_main_window.py (5 hunks)
Files skipped from review due to trivial changes (1)
  • tests/test_main_window.py
Additional comments (Suppressed): 1
tests/conftest.py (1)
  • 12-24: The make_tabulous_viewer fixture has been updated to support the creation of different types of viewers (main, widget, and magic). This is a significant change that enhances the flexibility of the test suite. However, it's important to ensure that all tests using this fixture have been updated accordingly to handle the new cls parameter.

viewers.add(viewer)
return viewer

yield factory

for viewer in viewers:
viewer.close()

viewer.native.deleteLater()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The viewer object is being closed and deleted in the same loop iteration. This could potentially lead to issues if the viewer object is still referenced elsewhere after being deleted. It would be safer to first close all viewers in one loop, then delete them in a separate loop.

- for viewer in viewers:
-     viewer.close()
-     viewer.native.deleteLater()

+ for viewer in viewers:
+     viewer.close()
+
+ for viewer in viewers:
+     viewer.native.deleteLater()

Comment on lines +56 to +60
QApplication.closeAllWindows()
QMainWindow._instances.clear()
N_PROCESS_EVENTS = 50
for _ in range(N_PROCESS_EVENTS):
QApplication.processEvents()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The addition of QApplication.closeAllWindows() and the loop calling QApplication.processEvents() multiple times are intended to ensure that all windows are properly closed and all events are processed before proceeding. While this is generally a good practice, it might introduce unnecessary delays in the test suite if there are no pending events. Consider adding a condition to break the loop if there are no more events to process.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 3

Commits Files that changed from the base of the PR and between 6728cc8 and 525d0c5.
Files selected for processing (5)
  • tabulous/_qt/_table/_base/_table_base.py (2 hunks)
  • tabulous/widgets/_mainwindow.py (2 hunks)
  • tests/conftest.py (2 hunks)
  • tests/test_core.py (2 hunks)
  • tests/test_main_window.py (5 hunks)
Files skipped from review due to trivial changes (1)
  • tests/test_core.py
Additional comments (Suppressed): 10
tabulous/widgets/_mainwindow.py (2)
  • 485-491: The close method has been updated to disconnect the ItemChangedSignal from each table in the viewer before clearing the tables and closing the viewer. This is a good practice as it ensures that no lingering connections remain after the viewer is closed, which could potentially lead to memory leaks or unexpected behavior.

  • 587-600: A new method _unlink_events has been introduced to disconnect various events from both _tablist and _qtablist. This is a good practice for event management, especially when the object is about to be destroyed or reused, to prevent any potential issues such as memory leaks or unexpected behavior due to lingering event connections.

tests/conftest.py (3)
  • 12-24: The make_tabulous_viewer fixture has been enhanced to support different types of viewers (main, widget, and magic). This is a significant change that increases the flexibility of the fixture. However, it's important to ensure that all test functions using this fixture have been updated accordingly to specify the viewer type or can handle the default "main" viewer type.

  • 57-61: Closing all application windows and processing events multiple times are new steps added to the session cleanup process. While these steps can help ensure a clean state at the end of the session, they might also introduce unexpected behavior if there are still active tasks or threads when these operations are performed. Therefore, it's recommended to verify that these steps do not interfere with any ongoing processes or tests.

  • 63-64: The check for remaining QMainWindow instances has been moved after the event processing loop. This change makes sense because the event processing might result in some windows being closed. However, it's important to verify that this check still works as expected and that it correctly identifies any instances that were not properly cleaned up.

tests/test_main_window.py (5)
  • 13-18: The make_tabulous_viewer fixture now takes a string parameter viewer_cls instead of a class type. Ensure that the fixture correctly handles these string inputs and creates the appropriate viewer objects.

  • 28-33: Similar to the previous comment, ensure that the make_tabulous_viewer fixture correctly handles the string input for viewer_cls.

  • 74-79: Again, verify that the make_tabulous_viewer fixture correctly handles the string input for viewer_cls.

  • 91-96: Ensure that the make_tabulous_viewer fixture correctly handles the string input for viewer_cls.

  • 110-115: Once more, verify that the make_tabulous_viewer fixture correctly handles the string input for viewer_cls.

Comment on lines +278 to +282
def connectItemChangedSignal(self, *args, **kwargs) -> None:
pass

def disconnectItemChangedSignal(self):
pass
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The connectItemChangedSignal and disconnectItemChangedSignal methods are defined but do not perform any operations. If these methods are intended to be overridden in subclasses, consider making them abstract methods or adding a comment to clarify their purpose.

-    def connectItemChangedSignal(self, *args, **kwargs) -> None:
-        pass
-
-    def disconnectItemChangedSignal(self):
-        pass
+    @abstractmethod
+    def connectItemChangedSignal(self, *args, **kwargs) -> None:
+        pass
+
+    @abstractmethod
+    def disconnectItemChangedSignal(self):
+        pass

Comment on lines +1231 to +1242
def disconnectItemChangedSignal(self):
for signal in [
self.itemChangedSignal,
self.rowChangedSignal,
self.columnChangedSignal,
self.evaluatedSignal,
]:
try:
signal.disconnect()
except TypeError:
pass
return None
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The disconnectItemChangedSignal method attempts to disconnect all signals even if they were not connected before. This can lead to unnecessary TypeError exceptions being raised and caught. Consider checking if the signal is connected before attempting to disconnect it.

def disconnectItemChangedSignal(self):
    for signal in [
        self.itemChangedSignal,
        self.rowChangedSignal,
        self.columnChangedSignal,
        self.evaluatedSignal,
    ]:
        if signal.is_connected():
            try:
                signal.disconnect()
            except TypeError:
                pass
    return None

Comment on lines 28 to 32
for viewer in viewers:
viewer._unlink_events()
viewer.close()

viewer.native.deleteLater()

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cleanup steps for each viewer in the WeakSet now include unlinking events and deleting the native object. These additional steps improve the thoroughness of the cleanup process, but they also introduce potential risks if any of these operations fail or have side effects. It would be beneficial to add error handling around these operations to catch and log any exceptions, allowing the cleanup process to continue for the remaining viewers.

     for viewer in viewers:
        try:
            viewer._unlink_events()
            viewer.close()
            viewer.native.deleteLater()
        except Exception as e:
            print(f"Error cleaning up viewer {viewer}: {e}")

@hanjinliu hanjinliu merged commit 15cba80 into main Sep 24, 2023
@hanjinliu hanjinliu deleted the fix-segfault-2 branch September 24, 2023 15:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant