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

Use location= for parent/child connection #125

Merged
merged 7 commits into from
Oct 12, 2023
Merged

Use location= for parent/child connection #125

merged 7 commits into from
Oct 12, 2023

Conversation

hanjinliu
Copy link
Owner

@hanjinliu hanjinliu commented Oct 11, 2023

Closes #124

<class name>.wraps and <class name>.field are kind of inconsistent.
Moving the location of widgets are now refactored as location= keyword argument.

  • @set_design(location=X) instead of @X.wraps.
  • field(..., location=X) instead of X.field(...).

Summary by CodeRabbit

New Features:

  • Added location parameter to field and vfield functions for specifying the location of fields.
  • Introduced get_level function to determine the level of a function in the class hierarchy.

Bug Fixes:

  • Fixed a bug in method_as_getter function where it was not correctly identifying the class name for nested classes.

Refactor:

  • Refactored _convert_attributes_into_widgets method in menu_gui.py and toolbar.py for improved modularity and readability.
  • Updated abstractapi class to handle a new optional location argument.

Test:

  • Added new test functions test_wrapped_with_external_field and test_wrapped_with_external_field_via_field in test_bind.py.
  • Updated test cases in test_callbacks.py and test_wraps.py to reflect changes in field and vfield functions.

@coderabbitai
Copy link

coderabbitai bot commented Oct 11, 2023

Walkthrough

This pull request introduces significant refactoring and enhancements to the magicclass library. The changes improve modularity, readability, and functionality across various modules. Key updates include the addition of a location parameter to field creation functions, the introduction of a get_level function, and the restructuring of widget creation processes in GUI classes.

Changes

File(s) Summary
magicclass/__init__.py, magicclass/fields/_define.py Removed import statements for unused modules.
magicclass/_gui/_base.py, magicclass/_gui/class_gui.py, magicclass/_gui/menu_gui.py, magicclass/_gui/toolbar.py Refactored widget creation and handling in GUI classes for improved modularity and readability.
magicclass/_gui/utils.py, magicclass/core.py Introduced changes to class copying and button retrieval functions.
magicclass/ext/pyqtgraph/graph_items.py Added a new attribute to the LayerItem class.
magicclass/fields/_fields.py Updated field creation methods to include a location parameter.
magicclass/utils/__init__.py, magicclass/utils/_functions.py Added a new function get_level to determine the level of a function in the class hierarchy.
magicclass/wrappers/_abstractapi.py, magicclass/wrappers/_misc.py Introduced changes to the abstractapi class and various function wrappers.
tests/test_bind.py, tests/test_callbacks.py, tests/test_wraps.py Updated test cases to reflect changes in the main codebase.

🐇💻

Code refactored, changes made,

In the magicclass, improvements laid.

With each keystroke, a new path paved,

Celebrate, for progress has been displayed! 🎉


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.
  • Additionally, enter @coderabbitai pause to halt the bot's ongoing reviews as any issue or review comment. To
    resume reviews, enter @coderabbitai resume as any issue or review comment.

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: 1

Commits Files that changed from the base of the PR and between a96fb16 and 06b7792.
Files selected for processing (17)
  • magicclass/init.py (1 hunks)
  • magicclass/_gui/_base.py (19 hunks)
  • magicclass/_gui/class_gui.py (2 hunks)
  • magicclass/_gui/menu_gui.py (1 hunks)
  • magicclass/_gui/toolbar.py (1 hunks)
  • magicclass/_gui/utils.py (1 hunks)
  • magicclass/core.py (1 hunks)
  • magicclass/ext/pyqtgraph/graph_items.py (1 hunks)
  • magicclass/fields/_define.py (1 hunks)
  • magicclass/fields/_fields.py (22 hunks)
  • magicclass/utils/init.py (1 hunks)
  • magicclass/utils/_functions.py (2 hunks)
  • magicclass/wrappers/_abstractapi.py (5 hunks)
  • magicclass/wrappers/_misc.py (7 hunks)
  • tests/test_bind.py (3 hunks)
  • tests/test_callbacks.py (1 hunks)
  • tests/test_wraps.py (2 hunks)
Files skipped from review due to trivial changes (6)
  • magicclass/init.py
  • magicclass/_gui/class_gui.py
  • magicclass/core.py
  • magicclass/ext/pyqtgraph/graph_items.py
  • magicclass/fields/_define.py
  • tests/test_callbacks.py
Additional comments (Suppressed): 51
magicclass/utils/__init__.py (1)
  • 3-9: The new function get_level has been added to the import list. Ensure that this function is implemented correctly and used appropriately throughout the codebase.
magicclass/_gui/menu_gui.py (1)
  • 89-97: The new hunk has removed the handling of FunctionGui and MagicField instances, and the binding of self to the first argument of FunctionGui. If these changes are intentional, ensure that they do not break existing functionality. Also, the new hunk does not handle the case where attr is a type. If this is intentional, verify that it does not affect the expected behavior.
-                if isinstance(attr, type):
-                    # Nested magic-menu
-                    widget = attr()
-                    object.__setattr__(self, name, widget)
-
-                elif isinstance(attr, MagicField):
-                    widget = self._create_widget_from_field(name, attr)
-                    if not widget.tooltip:
-                        widget.tooltip = _tooltips.attributes.get(name, "")
-
-                else:
-                    # convert class method into instance method
-                    widget = getattr(self, name, None)
-
-                if isinstance(widget, FunctionGui):
-                    widget = attr.copy()
-                    widget[0].bind(self)  # bind self to the first argument
-
-                elif isinstance(widget, BaseGui):
-                    connect_magicclasses(self, widget, name)
magicclass/_gui/toolbar.py (2)
  • 130-132: The new hunk has removed the checks for attr being a type, MagicField, or FunctionGui. Ensure that these cases are handled elsewhere or are no longer necessary. If they are still necessary, consider adding them back.
-                if isinstance(attr, type):
-                    # Nested magic-menu
-                    widget = attr()
-                    object.__setattr__(self, name, widget)
-
-                elif isinstance(attr, MagicField):
-                    widget = self._create_widget_from_field(name, attr)
-                    if not widget.tooltip:
-                        widget.tooltip = _tooltips.attributes.get(name, "")
-
-                else:
-                    # convert class method into instance method
-                    widget = getattr(self, name, None)
-
-                if isinstance(widget, FunctionGui):
-                    widget = attr.copy()
-                    widget[0].bind(self)  # bind self to the first argument
+                widget = self._convert_an_attribute_into_widget(name, attr, _tooltips)
  • 133-135: The new hunk has removed the connect_magicclasses(self, widget, name) call. Ensure that this connection is established elsewhere or is no longer necessary. If it is still necessary, consider adding it back.
-                    connect_magicclasses(self, widget, name)
+                    # No equivalent line in new hunk
tests/test_wraps.py (2)
  • 175-182: The field function now accepts a location parameter, which is a change from the previous version where the field function was called on an object. Ensure that all calls to this function throughout the codebase have been updated to match the new usage.
-        a = B.field(int)
-        b = B.field(str)
+        a = field(int, location=B)
+        b = field(str, location=B)
  • 203-210: Similar to the field function, the vfield function now also accepts a location parameter. Ensure that all calls to this function throughout the codebase have been updated to match the new usage.
-        a = B.vfield(int)
-        b = B.vfield(str)
+        a = vfield(int, location=B)
+        b = vfield(str, location=B)
magicclass/_gui/utils.py (2)
  • 46-65: The copy_class function has been updated to handle more cases. The new code checks if the attribute is an instance of FunctionType, abstractapi, or thread_worker. If it is, it updates the __qualname__ attribute. If the attribute is a type, it makes a recursive call to copy_class. The function also now skips copying the __original_class__ attribute and sets it on the copied class at the end. Ensure that these changes do not introduce any unexpected behavior or side effects.
-    if isinstance(attr, FunctionType):
+    if isinstance(attr, (FunctionType, abstractapi, thread_worker)):
-    attr = copy_class(attr, qualname, attr.__name__)
+    attr = copy_class(attr, qualname, attr.__qualname__.split(".")[-1])
+    out.__original_class__ = getattr(cls, "__original_class__", cls)
  • 52-53: The __original_class__ attribute is skipped during the copying process. This is a new behavior and should be verified to ensure it doesn't cause any issues.
+    if key == ("__original_class__"):
+        continue
magicclass/utils/_functions.py (3)
  • 129-133: The get_level function is introduced to determine the level of a callable or type based on its qualified name. It subtracts one from the level if the input is callable. This function seems to be used for determining the nesting level of a function or class within a module or class. Ensure that this function is used correctly throughout the codebase.

  • 136-136: The _LOCALS variable is moved from its previous location. This change doesn't affect the functionality, but it's important to ensure that this variable is defined before it's used in the code.

  • 146-161: The logic for determining the namespace of a method and checking its visibility from a class has been slightly modified. The self_cls variable is now determined before the visibility check, and the ins variable is initialized earlier. These changes don't seem to affect the overall functionality, but it's important to verify that they don't introduce any subtle bugs.

tests/test_bind.py (4)
  • 1-6: The import of set_design, get_button, magicmenu, and abstractapi has been added, and set_options has been removed. Ensure that these changes are reflected in the rest of the codebase.

  • 185-191: The Bound type has been replaced with Annotated for binding. This change should be verified across the codebase to ensure consistency.

-        def func(self, x: Bound[b.x]):
+        def func(self, x: Annotated[int, {"bind": b.x}]):
  • 198-234: Two new test cases test_wrapped_with_external_field and test_wrapped_with_external_field_via_field have been added. These tests seem to be checking the functionality of the set_design function with the location parameter and the Annotated type for binding. Ensure that these tests cover all edge cases and possible scenarios.

  • 270-270: The test_multi_gui function has been moved down in the file. Ensure that this does not affect the execution order of the tests or their dependencies.

magicclass/wrappers/_abstractapi.py (5)
  • 1-13: The import of inspect module is new in this hunk. It is used later in the code for creating an empty signature for the abstractapi when a location is provided. The rest of the imports and the class definition remain unchanged.

  • 34-42: The __init__ method of abstractapi now accepts an additional keyword argument location. This argument is expected to be of type MagicTemplate or MagicField or None. The func argument validation remains the same.

  • 46-52: The location argument is stored in the _location attribute of the abstractapi instance. If a location is provided and the abstractapi instance does not have a __signature__ attribute, an empty inspect.Signature is assigned to __signature__. This change might affect the behavior of the abstractapi instance when it is used as a decorator for functions or methods without a signature.

  • 58-66: In the __set_name__ method, the abstractapi instance now checks if there is an abstractapi instance in its _location with the same name. If there is, it resolves that abstractapi instance and assigns an empty inspect.Signature to it if it does not have a __signature__. This change might affect the behavior of the abstractapi instance when it is used as a decorator for functions or methods without a signature.

  • 84-97: A new method get_location is added to the abstractapi class. This method returns the _location attribute of the abstractapi instance. If the _location is an instance of MagicField, it returns the constructor attribute of the MagicField instance. This method provides a way to retrieve the location of the abstractapi instance.

magicclass/_gui/_base.py (17)
  • 44-62: Imports have been modified. Ensure that the removed import PushButtonPlus is not used elsewhere in the codebase. Also, verify that the newly added imports FunctionGuiPlus, Clickable, is_clickable are used correctly in the codebase.

  • 72-78: The import MagicValueField has been removed. Ensure that it is not used elsewhere in the codebase.

  • 192-214: The type of _component_class has been changed from type[Action | PushButtonPlus] to type[Clickable] and the type of _list has been changed from list[Action | Widget] to list[AbstractAction | Widget]. Ensure that these changes do not break the existing functionality.

  • 362-365: The type function has been replaced with _typeof function. Ensure that _typeof function is defined and works as expected.

  • 440-452: The logic for handling predefined has been modified. Ensure that the new logic works as expected and does not introduce any bugs.

  • 474-483: The variable cls has been replaced with into_cls. Ensure that into_cls is defined and works as expected.

  • 515-528: The variable child_instance has been replaced with child_ins. Ensure that child_ins is defined and works as expected.

  • 532-558: The variable child_instance has been replaced with child_ins. Ensure that child_ins is defined and works as expected.

  • 566-573: The error message in the RuntimeError has been modified. Ensure that the new error message is accurate and informative.

  • 580-600: > Note: This review was outside of the patch, so it was mapped to the patch with the greatest overlap. Original lines [580-623]

The field and vfield methods have been modified to include a deprecation warning. Ensure that the new methods work as expected and the deprecation warning is accurate.

  • 746-753: The len function has been replaced with a sum function. Ensure that the new logic works as expected and does not introduce any bugs.

  • 830-835: The type of widget_ has been changed from Action | PushButtonPlus to Clickable. Ensure that this change does not break the existing functionality.

  • 843-857: The _iter_child_magicclasses method has been modified to include level and max_level parameters. Ensure that the new logic works as expected and does not introduce any bugs.

  • 1149-1152: The type of widget_ has been changed from Action | PushButtonPlus to Clickable. Ensure that this change does not break the existing functionality.

  • 1229-1239: The variable child_clsname has been replaced with into. Ensure that into is defined and works as expected.

  • 1308-1320: The logic for handling obj has been modified. Ensure that the new logic works as expected and does not introduce any bugs.

  • 1342-1345: The type of widget has been changed from PushButtonPlus | Action to Clickable. Ensure that this change does not break the existing functionality.

magicclass/fields/_fields.py (7)
  • 32-38: The import of get_level is new. Ensure that it is used appropriately in the code and that it does not conflict with any existing names.

  • 216-219: The method with_widget_type no longer creates a new copy of the object before setting the _widget_type. This changes the behavior of the method and may have unintended side effects if the original object was intended to be immutable. Verify that this change is intentional and does not introduce bugs.

  • 365-379: The logic for determining the namespace of MagicField has been changed. Previously, it was based on the class name, but now it's based on the qualified name. This could potentially change the behavior of the _func method. Ensure that this change is intentional and that it does not introduce bugs.

  • 609-612: The return value of the _wrapper function has been changed from _afunc.replace(force_async=False) to self.connect(_afunc). This could potentially change the behavior of the function. Ensure that this change is intentional and that it does not introduce bugs.

  • 1037-1041: The location parameter has been added to several function signatures. Ensure that all calls to these functions throughout the codebase have been updated to match the new signatures.

  • 934-947: The location parameter is now used to set the destination of the MagicField object. Ensure that this change is intentional and that it does not introduce bugs.

  • 1086-1092: The location parameter is now used to set the destination of the MagicValueField object. Ensure that this change is intentional and that it does not introduce bugs.

magicclass/wrappers/_misc.py (7)
  • 9-14: The import statements have been updated to reflect the changes in the codebase. The BaseGui class has been replaced with MagicTemplate and MagicField classes. Ensure that these classes provide the same or improved functionality as the BaseGui class.

  • 61-74: The set_options function now checks if the options parameter is not empty before proceeding with the function logic. This is a good practice as it prevents unnecessary operations when options is empty.

  • 95-101: The set_design function now accepts a location parameter of type MagicTemplate or MagicField. This allows the function to set the location of the widget in the GUI. Ensure that this change is reflected in all calls to set_design.

  • 130-136: The set_design function now removes the location parameter from caller_options before proceeding with the function logic. This is necessary because location is not a valid option for the upgrade_signature function.

  • 145-152: The set_design function now checks if location is not None and if so, calls the wraps method on location with obj as the argument. This sets the location of the widget in the GUI. Ensure that the wraps method is implemented correctly in the MagicTemplate and MagicField classes.

  • 246-252: The setup_function_gui function now accepts a setup parameter of type Callable[[MagicTemplate, FunctionGui], None] instead of Callable[[BaseGui, FunctionGui], None]. This change reflects the replacement of the BaseGui class with the MagicTemplate class. Ensure that this change is reflected in all calls to setup_function_gui.

  • 255-261: The _setup function inside the setup_function_gui function now accepts a self parameter of type MagicTemplate instead of BaseGui. This change reflects the replacement of the BaseGui class with the MagicTemplate class. Ensure that this change is reflected in all calls to _setup.

Comment on lines 552 to 558
@wraps(fn)
def _func(self: MagicTemplate, *args, **kwargs):
nonlocal _running, _last_run, _last_run_id, _abort_begin

with threading.Lock():
_this_id = default_timer()
f = _afunc.__get__(self)
Copy link

Choose a reason for hiding this comment

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

The use of threading.Lock() without a variable assignment means that a new lock is created and immediately discarded each time the with block is entered. This will not provide any synchronization because each thread will have its own lock. If the intention was to synchronize access to _running, _last_run, _last_run_id, and _abort_begin, a shared lock should be used.

- with threading.Lock():
+ with shared_lock:

@hanjinliu hanjinliu merged commit 6348e25 into main Oct 12, 2023
13 of 16 checks passed
@hanjinliu hanjinliu deleted the location-args branch October 13, 2023 04:37
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.

Deprecate wraps and use location= argument for this purpose
1 participant