8000 Improve robustness of Python completion model by jcfr · Pull Request #1229 · commontk/CTK · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Improve robustness of Python completion model #1229

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

Merged
merged 5 commits into from
Apr 14, 2025

Conversation

jcfr
Copy link
Member
@jcfr jcfr commented Apr 11, 2025

This pull request includes a series of improvements and bug fixes to the ctkAbstractPythonManager::pythonAttributes and related methods improving the autocompletion model. The changes focus on simplifying the code, improving memory management, and ensuring more accurate and error-resilient behavior when inspecting Python objects.

  • Exclude property-like accessors from autocompletion
  • Fix reference management for classToInstantiate
  • Improve error handling during attribute lookup
  • Simplify logic by removing unused dict variable

Related pull request:

@jcfr jcfr requested review from lassoan and jamesobutler April 11, 2025 16:36
jcfr added 2 commits April 11, 2025 12:38
Remove the `dict` variable from `ctkAbstractPythonManager::pythonAttributes` as
it was redundant. All relevant operations now directly use object.
@lassoan
Copy link
Member
lassoan commented Apr 11, 2025

It all looks good except hiding Get... and Set... methods in auto-completion. For a couple of years (until most code switch to obj.property syntax) it would be really confusing to not see the methods that are still used everywhere. We could just move them to the back of the auto-complete list, similarly to private members (that start with _).

else
{
// failed to invoke callable, no completions are available
PyErr_Clear();
Copy link
Member Author
@jcfr jcfr Apr 11, 2025

Choose a reason for hiding this comment

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

Suggested change
PyErr_Clear();
this->handleErrorAsWarning();

Instead of blindly discarding error ... we could introduce a new function handleErrorAsWarning allowing those to be logged while avoiding them to be reported in the Python console.

Copy link
Member Author

Choose a reason for hiding this comment

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

Attempt to replace the calls PyErr_Clear(); with PythonQt::self()->handleError(); allows to identify the "problematic" properties but polluted the output:

image

Copy link
Contributor
@jamesobutler jamesobutler left a comment

Choose a reason for hiding this comment

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

Using the updated VTK version in Slicer/Slicer#8238, I can confirm that SetInterpolate is showing again:
{3821FCDF-EEAA-4D71-A559-019CAD2BAD1D}

I'm not sure what excludeGetSet is really doing here as I see both the property and Getter related to getting the Slicer node ID. Unless this is an artifact of doing an incremental build rather than a fresh clean build.
{2ED8855C-B4F0-40B0-B335-ED88A99058DF}
{230F2F8E-DD3B-495C-A3C4-D9E9C10CF259}

Copy link
Contributor
@jamesobutler jamesobutler left a comment

Choose a reason for hiding this comment

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

Also not sure if this is again an incremental build issue, but some more observations:

With Slicer latest Preview:
{0AC4EAF2-6E79-4782-80DE-4FCCEAD813F1}
With the changes here with an incremental Slicer build, the window property seems to be unavailable in autocomplete:
{A37609EE-0047-4770-B6E5-B740E54577BD}

@jcfr
Copy link
Member Author
jcfr commented Apr 11, 2025

After adding Q_INVOKABLE in front of pythonAttributes in ctkAbstractPythonManager, we can observe the effect of the excludeGetSet parameter:

dn = n.GetDisplayNode()
appendParenthesis = False

excludeGetSet = False
withGetSets = set(slicer.app.pythonManager().pythonAttributes("dn", "__main__", appendParenthesis, excludeGetSet))

excludeGetSet = True
withoutGetSets = set(slicer.app.pythonManager().pythonAttributes("dn", "__main__", appendParenthesis, excludeGetSet))

print(withGetSets - withoutGetSets)
{'ReferencedNodeModifiedEvent', 'level', 'modified_event_pending', 'singleton_tag', '__ge__', 'in_mrml_callback_flag', 'auto_window_level', 'InterpolationType', 'background_image_stencil_data', 'is_in_memkind', 'image_data_connection', '__gt__', '__dict__', 'output_image_data', 'ReferenceModifiedEvent', 'slice_intersection_opacity', 'diffuse', 'input_image_data', 'save_with_scene', 'opacity', 'backface_culling', 'selected_ambient', 'output_image_data_connection', 'tensor_visibility', '__init__', 'ContentModifiedObserveType', 'edge_color', '__le__', '__delattr__', 'reference_count', 'ambient', 'scene', 'ReferenceAddedEvent', 'scalar_range_flag', 'visibility3d', '__getattribute__', 'slice_intersection_visibility', 'active_scalar_name', 'upper_threshold', '__ne__', 'invert_display_scalar_range', 'global_warning_display', 'color_node', 'content_modified
8000
_events', 'input_image_data_connection', 'active_attribute_location', 'metallic', 'selected', 'ReferenceRemovedEvent', 'scalar_visibility', 'color', 'edge_visibility', 'selected_specular', 'visibility2d', 'show_mode', 'vector_visibility', 'disable_modified_event', 'selected_color', 'visibility', '__setattr__', 'representation', 'auto_threshold', 'specular', 'number_of_window_level_presets', 'ShowModeType', 'active_scalar_array', '__lt__', 'lookup_table', 'undo_enabled', '__hash__', 'lighting', 'power', 'window_level_max', 'using_memkind', 'lower_threshold', 'clipping', 'interpolate_texture', 'slice_intersection_thickness', 'window', 'line_width', 'clip_node', 'IDChangedEvent', '__repr__', 'window_level_locked', 'displayable_node', 'interpolation', 'RepresentationType', 'apply_threshold', 'HierarchyModifiedEvent', 'folder_display_override_allowed', '__eq__', 'shading', 'roughness', 'background_image_stencil_data_connection', 'debug', 'add_to_scene', 'selectable', 'texture_image_data_connection', 'point_size', 'volume_node', 'interpolate', 'scalar_data_set', 'frontface_culling', 'ScalarRangeFlagType', 'window_level_min', 'MenuEvent', 'description', 'hide_from_editors', 'auto_scalar_range', '__str__', 'm_time'}

There are few issues:

  • id is still listed
  • built-in method like __str__ or __hash__ are excluded

As pointed out by @lassoan excluding the property from completion will be confusing, instead we want them to be listed by with a lower priority (e.g at the end)

@jamesobutler
Copy link
Contributor
jamesobutler commented Apr 11, 2025

Yeah putting the older Get/Set methods at the end could be a good way to discourage that auto-complete in favor of the more python property ones as part of a transition period starting now. Then ultimately the Get/Set autocompletion could be removed in the future. ITK had already switched to the more python interface as part of ITK 5 (https://discourse.itk.org/t/itk-5-0-beta-1-pythonic-interface/1271).

jcfr added 3 commits April 14, 2025 15:08
…ython

This may be useful to inspect the input provided to the Python completion model.
…_object`

Clear error if attempt to get an object or dictionary attribute failed
while iterating over items.
Ensure `classToInstantiate` is properly managed by incrementing its reference
count when retrieved via `PyDict_GetItemString`, which returns a borrowed
reference. This allows safely calling `Py_DECREF` afterward, regardless of
whether the object came from a dictionary or via `PyObject_GetAttrString`,
whic
8000
h returns a new reference.
@jcfr jcfr force-pushed the fix-python-autocomplete branch from c741fd9 to c1ed918 Compare April 14, 2025 19:54
@jcfr
Copy link
Member Author
jcfr commented Apr 14, 2025

Frmo @lassoan

[...] It all looks good except hiding Get... and Set... methods in auto-completion. [...] We could just move them to the back of the auto-complete list [...]

From @jamesobutler

[...] putting the older Get/Set methods at the end [...]

It turns out that those methods are already added last.

I just removed the attempt to remove the GetSet descriptors, this was an oversight and now the completion model is more robustly created, I think there is no need for additional change.

This topic is now ready for integration 🚀

@jcfr jcfr enabled auto-merge (rebase) April 14, 2025 19:58
@jamesobutler jamesobutler self-requested a review April 14, 2025 20:00
@jcfr
Copy link
Member Author
jcfr commented Apr 14, 2025

@lassoan @jamesobutler Once this is approved & integrated, I will then:

  • update the CTK version used in Slicer
  • update the Slicer pull-request adding support for VTK 9.4 by rebasing it

Copy link
Contributor
@jamesobutler jamesobutler left a comment

Choose a reason for hiding this comment

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

I've done an incremental build locally to try out the SetInterpolate/interpolate options and used them successfully with autocomplete.

@jcfr jcfr merged commit c68d1c9 into commontk:master Apr 14, 2025
5 checks passed
@jcfr jcfr deleted the fix-python-autocomplete branch April 14, 2025 20:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

3 participants
0