Skip to content

Conversation

nicolaipre
Copy link

@nicolaipre nicolaipre commented Aug 31, 2025

Hey guys, hope you are doing all good.

After IDA moved onto PyQt6 I encountered this error when opening the FLARE Capa Explorer plugin:

Failed while executing plugin_t.run():
Traceback (most recent call last):
  File "/home/user/.idapro/.venv/lib/python3.13/site-packages/capa/ida/plugin/__init__.py", line 78, in run
    self.form = CapaExplorerForm(self.PLUGIN_NAME, arg)
                ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/user/.idapro/.venv/lib/python3.13/site-packages/capa/ida/plugin/form.py", line 240, in __init__
    self.analyze_program(analyze=analyze)
    ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
  File "/home/user/.idapro/.venv/lib/python3.13/site-packages/capa/ida/plugin/form.py", line 906, in analyze_program
    self.range_model_proxy.invalidate()
    ^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'CapaExplorerForm' object has no attribute 'range_model_proxy'

The following error also showed up in IDAs output window:

/home/user/.idapro/.venv/lib/python3.13/site-packages/capa/ida/plugin/item.py:55: RuntimeWarning: This bitwise operation relies on a PyQt5 shim feature. If PyQt5 is not imported, it won't work. For PySide6, use <EnumOrFlag>.value for bitwise operations instead
  self.flags = QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
/home/user/.idapro/.venv/lib/python3.13/site-packages/capa/ida/plugin/item.py:58: RuntimeWarning: This bitwise operation relies on a PyQt5 shim feature. If PyQt5 is not imported, it won't work. For PySide6, use <EnumOrFlag>.value for bitwise operations instead
  self.flags = self.flags | QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsTristate
IDAPython: Error while calling Python callback <OnCreate>:
Traceback (most recent call last):
  File "/home/user/.idapro/.venv/lib/python3.13/site-packages/capa/ida/plugin/form.py", line 254, in OnCreate
    self.load_interface()
    ~~~~~~~~~~~~~~~~~~~^^
  File "/home/user/.idapro/.venv/lib/python3.13/site-packages/capa/ida/plugin/form.py", line 280, in load_interface
    self.model_data = CapaExplorerDataModel()
                      ~~~~~~~~~~~~~~~~~~~~~^^
  File "/home/user/.idapro/.venv/lib/python3.13/site-packages/capa/ida/plugin/model.py", line 63, in __init__
    self.root_node = CapaExplorerDataItem(None, ["Rule Information", "Address", "Details"])
                     ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/user/.idapro/.venv/lib/python3.13/site-packages/capa/ida/plugin/item.py", line 58, in __init__
    self.flags = self.flags | QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsTristate
                                                              ^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: type object 'PySide6.QtCore.Qt' has no attribute 'ItemIsTristate'

This minimal PR resolves the problem and (according to ChatGPT, lol) has a fallback for PyQt5 which I have not tested. Plugin works as intended with PyQt6 with current implementation. I do not think a new test is needed considering this was caused by a major change in a library version.

Checklist

  • CHANGELOG has been updated
  • No new tests needed
  • No documentation update needed

Copy link

google-cla bot commented Aug 31, 2025

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Please add bug fixes, new features, breaking changes and anything else you think is worthwhile mentioning to the master (unreleased) section of CHANGELOG.md. If no CHANGELOG update is needed add the following to the PR description: [x] No CHANGELOG update needed

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Summary of Changes

Hello @nicolaipre, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses critical compatibility issues preventing the FLARE Capa Explorer plugin from functioning with IDA Pro after its update to PyQt6. The changes introduce a flexible mechanism to handle variations in PyQt API, ensuring the plugin remains operational across different PyQt versions without requiring separate codebases.

Highlights

  • PyQt6 Compatibility: Resolves AttributeError issues (range_model_proxy and ItemIsTristate) that arose in the FLARE Capa Explorer plugin after IDA's transition to PyQt6, ensuring the plugin functions correctly.
  • Cross-Version PyQt Support: Implements a robust compatibility layer in capa/ida/plugin/item.py to dynamically adapt to QtCore.Qt item flag differences between PyQt5 and PyQt6/PySide6, allowing the codebase to support both versions.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@github-actions github-actions bot dismissed their stale review August 31, 2025 16:53

CHANGELOG updated or no update needed, thanks! 😄

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request addresses an AttributeError that occurs when using the plugin with PyQt6 by introducing a compatibility layer for Qt flags. The changes correctly adapt the code for PyQt6's enum-based flags.

However, the implementation for the PyQt5 fallback is incorrect and will cause a crash, as it attempts to access a .value attribute on integer constants. I've provided a critical review comment with a suggested fix to create a proper shim for PyQt5, ensuring the plugin works correctly with both library versions.

Comment on lines +28 to +35
if hasattr(QtCore.Qt, 'ItemFlag'):
# PyQt6 / PySide6
ItemFlag = QtCore.Qt.ItemFlag
TRISTATE = ItemFlag.ItemIsAutoTristate
else:
# PyQt5
ItemFlag = QtCore.Qt
TRISTATE = ItemFlag.ItemIsTristate

Choose a reason for hiding this comment

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

critical

This compatibility layer is a great idea to support both PyQt5 and PyQt6. However, the current implementation for the PyQt5 fallback will not work as intended. In PyQt5, ItemFlag is set to QtCore.Qt, and attributes like QtCore.Qt.ItemIsEnabled are integers. Accessing .value on an integer (e.g., ItemFlag.ItemIsEnabled.value) will raise an AttributeError.

To ensure PyQt5 compatibility while keeping the usage sites clean, you can create a shim for PyQt5 that mimics the structure of PyQt6 enums. This will make the code work for both versions without further changes.

Suggested change
if hasattr(QtCore.Qt, 'ItemFlag'):
# PyQt6 / PySide6
ItemFlag = QtCore.Qt.ItemFlag
TRISTATE = ItemFlag.ItemIsAutoTristate
else:
# PyQt5
ItemFlag = QtCore.Qt
TRISTATE = ItemFlag.ItemIsTristate
if hasattr(QtCore.Qt, 'ItemFlag'):
# PyQt6 / PySide6
ItemFlag = QtCore.Qt.ItemFlag
TRISTATE = ItemFlag.ItemIsAutoTristate
else:
# PyQt5
# Create a shim to behave like PyQt6's enums which have a .value attribute.
class EnumValue:
def __init__(self, value):
self.value = value
class QtEnumShim:
ItemIsEnabled = EnumValue(QtCore.Qt.ItemIsEnabled)
ItemIsSelectable = EnumValue(QtCore.Qt.ItemIsSelectable)
ItemIsUserCheckable = EnumValue(QtCore.Qt.ItemIsUserCheckable)
ItemIsEditable = EnumValue(QtCore.Qt.ItemIsEditable)
ItemFlag = QtEnumShim()
TRISTATE = EnumValue(QtCore.Qt.ItemIsTristate)

@nicolaipre
Copy link
Author

I decided to give it a test with IDA 9.1 too and in its current state it does not work for both PyQt5 and PyQt6. Geminis suggestion did not work either so the PR needs some changes.

Before testing further I will wait for your feedback. There are probably more files that need changes to make the plugin fully compatible with PyQt6 but this works temporarily...

@Still34
Copy link

Still34 commented Sep 15, 2025

I think it'd be more ideal to check for IDA versioning number and decide to import PyQt5 or PySide6 accordingly instead of doing a simple attr-based check

@nicolaipre
Copy link
Author

I think it'd be more ideal to check for IDA versioning number and decide to import PyQt5 or PySide6 accordingly instead of doing a simple attr-based check

You are absolutely right. I will see what I can do when I find time. I'll leave the PR open until then.

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.

2 participants