-
-
Notifications
You must be signed in to change notification settings - Fork 276
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
Update brain_qt to work with pyside2 5.15.2 #900
Conversation
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.
@g-rocket thanks for this PR and sorry for the delay.
I'm really busy right now and understanding the changes in PySide2 was quite hard.
I eventually understood what was happening between version 5.15.1 and 5.15.2.
As you said the Signal
methods are no more inferred as SIgnal
instances but as FunctionDef
.
I believe it is due to the fact that starting with PySide2 5.15.2
the Signal
class does in fact implements the descriptor protocol. Thus astroid
when building its ast declares the node to be a FunctionDef
with an attibute __class__
pointing to Signal
.
You can find this mechanism in astroid
's raw_building
module at lines 365 - (222 - 232) - (65-75).
I have checked and the _add_dunder_class
is called only in the case of method descriptor, which is our case of interest here.
Thus your assumption to test the __class__
attribute seems to be finely scoped. I have no problem with it.
However i think the treatment of such node may be modified to better reflect what the interpreter is really using.
For example in a ipython
console you get:
In [1]: from PySide2.QtWidgets import QApplication
In [2]: from PySide2.QtCore import Signal
In [3]: QApplication.focusChanged
Out[3]: <PySide2.QtCore.Signal at 0x7f33d08e4b50>
Thus to me it makes sense that the corresponding focusChanged
node should be inferred as an instance of the Signal
class.
That is why i wonder if something like:
def transform_pyside_signal_descriptor(node):
src = """
class NotPySideSignal(object):
def connect(self, receiver, type=None):
pass
def disconnect(self, receiver):
pass
def emit(self, *args):
pass
"""
inst = extract_node(src).instantiate_class()
node.parent.locals[node.name] = [inst]
is not more pertinent.
As my understanding of PySide2
is extremely limited i would have your opinion on it.
Especially can you confirm that Signal
class has just the three methods connect
, disconnect
and emit
?
@@ -6,11 +6,16 @@ What's New in astroid 2.5.1? | |||
============================ | |||
Release Date: TBA | |||
|
|||
* Update qt brain to work with pylint2 5.15.2. |
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.
Should not be PySide2
?
Moreover you can be a little bit more explicit by saying that this is for taking into account the fact that Signal implements the descriptor protocol. (see the discussion below).
lambda node: ( | ||
node.qname().partition(".")[0] == "PySide2" | ||
and any( | ||
cls.qname() == "Signal" for cls in node.instance_attrs.get("__class__", []) | ||
) | ||
), |
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 think it would be more readable if you avoid a lambda here and defines a specific function for that.
They changed some things so now signals are detected as FunctionDefs instead of ClassDefs, so we need to update our logic to patch them. Fixes pylint-dev/pylint#4040
I needed a similar patch. This request almost works for PySide6 as well. (Note that PySide6 is the next version after PySide2). I saw the review as well, and I believe this version of the end of the file works for everyone. I can submit a new pull request if this request is dead. def _looks_like_pyside2_or_6_signal(node):
"""Detect a PySide2 or PySide6 signal. These changed locations as of QT 5ish apparently."""
is_pyside_node = node.qname().partition(".")[0] in ("PySide2", "PySide6")
is_named_signal = any(
cls.qname() == "Signal" for cls in node.instance_attrs.get("__class__", [])
)
return is_pyside_node and is_named_signal
register_module_extender(MANAGER, "PyQt4.QtCore", pyqt4_qtcore_transform)
MANAGER.register_transform(nodes.FunctionDef, transform_pyqt_signal, _looks_like_signal)
MANAGER.register_transform(
nodes.ClassDef,
transform_pyside_signal,
lambda node: node.qname() in ("PySide.QtCore.Signal", "PySide2.QtCore.Signal"),
)
MANAGER.register_transform(
nodes.FunctionDef,
transform_pyside_signal,
_looks_like_pyside2_or_6_signal
) |
Sorry it's been so long without updates. I have absolutely no pyside2 expertise (just work with a project that happens to use it that I want to be able to lint) and have been struggling to confirm that @jordanbray I'm happy to update the PR to your version, but I think @hippo91 wants us to also play around with how |
I'll answer your questions below, but first let me ask my own? In order to get rid of the ugly redefinition of the Ok, onto answering your questions:
It appears so. The docs for that page are down, but at least in As you can see, it exposes only those 3 functions.
It appears this is because they changed the syntax. I have not proven this, but here is a link to what the old syntax was vs the new syntax. https://wiki.qt.io/Qt_for_Python_Signals_and_Slots#Traditional_syntax:_SIGNAL_.28.29_and_SLOT.28.29
I'm not sure how to do it. The Example: >>> from PySide6.QtWidgets import QApplication, QPushButton
>>> a = QApplication([]) # boilerplate
>>> b = QPushButton("Test")
>>> b.clicked
<PySide6.QtCore.SignalInstance object at 0x7f66c8cf3210>
>>> QPushButton.clicked
<PySide6.QtCore.Signal object at 0x7f66c8dada10>
>>> 'emit' in dir(QPushButton.clicked)
False
>>> 'emit' in dir(b.clicked)
True
>>> How do we handle this? Well' see my above question. I'd be happy to submit an update given a gentle nudge in the correct direction. |
@jordanbray thanks for your remark. I'm afraid i do not understand what you are asking concerning the |
So, in the code I posted, the code you tried to merge, and the old
Instead, I would like to implement the following magic trick:
This means that at least we're defining the type to what it will be at runtime. |
@jordanbray thanks for your explanation. I understand now. This is probably possible. One trick could maybe to use
|
I can confirm this worked for me. What is the status of the patch? Can we move it forward? |
Both the reviewer and original submitter of this PR aren't really active within the project anymore. I think the best step forward would be a new PR so we can review that as is. Perhaps with a little reference to this PR in the PR description. |
You got it! Happy to help. I'll submit a new PR with a reference link shortly. |
Closing this then! Thanks! |
@DanielNoord Quick question. The Am I correct in assuming all the following should be considered when contributing? Are there any additional requirements?:
PS> git clone https://github.com/forked-repo/astroid.git
PS> cd astroid
PS> git remote add upstream https://github.com/PyCQA/astroid
PS> py -m venv .venv && .venv/Scripts/Activate.ps1
PS> py -m pip install -e .
PS> py -m pip install -r requirements_test.txt
PS> py -m pip install -r requirements_test_brain.txt
PS> py -m pip install -r requirements_test_min.txt
PS> py -m pip install -r requirements_test_pre_commit.txt
ps> pre-commit install
PS> git checkout -b your/branch
PS> git add -A && git commit # in your/branch
PS> git checkout master && git pull upstream master
PS> git push origin master
PS> git push -u origin your/branch
|
`pylint` detects `Qt` signals as `FunctionDef`s instead of `ClassDef`s, so they are not properly detected in PySide2 5.15.2+ and PySide6. The patch from PR pylint-dev#900 is used to fix this. Fixes PyCQA/pylint #4040 and PyCQA/pylint #5378
@DanielNoord @Pierre-Sassoulas Please see PR #1654 |
`pylint` detects `Qt` signals as `FunctionDef`s instead of `ClassDef`s, so they are not properly detected in PySide2 5.15.2+ and PySide6. The patch from PR pylint-dev#900 is used to fix this. Fixes PyCQA/pylint #4040 and PyCQA/pylint #5378
`pylint` detects `Qt` signals as `FunctionDef`s instead of `ClassDef`s, so they are not properly detected in PySide2 5.15.2+ and PySide6. The patch from PR pylint-dev#900 is used to fix this. Fixes PyCQA/pylint #4040 and PyCQA/pylint #5378
`pylint` detects `Qt` signals as `FunctionDef`s instead of `ClassDef`s, so they are not properly detected in PySide2 5.15.2+ and PySide6. The patch from PR pylint-dev#900 is used to fix this. Fixes PyCQA/pylint #4040 and PyCQA/pylint #5378
Steps
Description
We have some logic in
brain_qt
to detect Qt Signals and patchconnect
,disconnect
, andemit
methods on to them.However, in pyside2 5.15.2, they changed some things so now Qt Signals are detected as
FunctionDef
s insteadof
ClassDef
s, and as a result we're not detecting and patching them anymore. I add some new logic to patch these methods into any function in a module namedPySide2
with a__class__
namedSignal
. I've tested and this seems to work for me, and it should be tightly scoped enough to not break anything else. Unfortunately, I can't find a way to restrict it based on what module the Signal class was defined in, asasteroid
doesn't seem to know that. See pylint-dev/pylint#4040 for more discussion.Also -- not sure if I formatted the changelog right; I assume I missed the boat for 2.5?
Type of Changes
Related Issue
Closes pylint-dev/pylint#4040