-
Notifications
You must be signed in to change notification settings - Fork 31
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 WindowType and WindowFlags integer operations #145
Fix WindowType and WindowFlags integer operations #145
Conversation
Before the fix, the test would report: windowflags.py:10: error: Incompatible types in assignment (expression has type "int", variable has type "WindowType") windowflags.py:18: error: Incompatible types in assignment (expression has type "int", variable has type "WindowFlags") windowflags.py:19: error: Incompatible types in assignment (expression has type "int", variable has type "WindowFlags") windowflags.py:20: error: Incompatible types in assignment (expression has type "int", variable has type "WindowFlags") windowflags.py:21: error: Incompatible types in assignment (expression has type "int", variable has type "WindowFlags") windowflags.py:22: error: Incompatible types in assignment (expression has type "int", variable has type "WindowFlags") windowflags.py:27: error: Unsupported operand types for + ("WindowFlags" and "WindowType") windowflags.py:27: error: Incompatible types in assignment (expression has type "int", variable has type "WindowFlags") windowflags.py:28: error: Unsupported operand types for - ("WindowFlags" and "WindowType") windowflags.py:28: error: Incompatible types in assignment (expression has type "int", variable has type "WindowFlags") windowflags.py:29: error: Unsupported operand types for | ("WindowFlags" and "WindowType") windowflags.py:29: error: Incompatible types in assignment (expression has type "int", variable has type "WindowFlags") windowflags.py:30: error: Unsupported operand types for & ("WindowFlags" and "WindowType") windowflags.py:30: error: Incompatible types in assignment (expression has type "int", variable has type "WindowFlags") windowflags.py:31: error: Unsupported operand types for ^ ("WindowFlags" and "WindowType") windowflags.py:31: error: Incompatible types in assignment (expression has type "int", variable has type "WindowFlags") windowflags.py:34: error: Incompatible types in assignment (expression has type "int", variable has type "WindowFlags") windowflags.py:34: error: Unsupported operand types for + ("WindowType" and "WindowFlags") windowflags.py:35: error: Incompatible types in assignment (expression has type "int", variable has type "WindowFlags") windowflags.py:35: error: Unsupported operand types for - ("WindowType" and "WindowFlags") windowflags.py:36: error: Incompatible types in assignment (expression has type "int", variable has type "WindowFlags") windowflags.py:36: error: Unsupported operand types for | ("WindowType" and "WindowFlags") windowflags.py:37: error: Incompatible types in assignment (expression has type "int", variable has type "WindowFlags") windowflags.py:37: error: Unsupported operand types for & ("WindowType" and "WindowFlags") windowflags.py:38: error: Incompatible types in assignment (expression has type "int", variable has type "WindowFlags") windowflags.py:38: error: Unsupported operand types for ^ ("WindowType" and "WindowFlags") windowflags.py:41: error: Unsupported left operand type for + ("WindowFlags") windowflags.py:42: error: Unsupported left operand type for - ("WindowFlags") windowflags.py:43: error: Unsupported left operand type for | ("WindowFlags") windowflags.py:44: error: Unsupported left operand type for & ("WindowFlags") windowflags.py:45: error: Unsupported left operand type for ^ ("WindowFlags")
fixes #142 |
Looks like these changes cause the tests to fail in CI. Also can you add an entry to the |
This was more difficult than I expected, I had to cover all cases to make sure it works correctly. Let's see what CI says. |
I just re-read carefully the Qt documentation about QFlags ( https://doc.qt.io/qt-5/qflags.html#details ) . The following points are interesting :
Then makes me think that combinations of QFlags with int is discouraged and maybe, I should raise a TypeError there just like in C++. Looking at the operator implemented in QFlags for int, I see :
So, we could be theorically faithful to QFlags by removing int support in OR and XOR operators, or we could just reflect the PyQt reality that it works fine also it is discouraged. I lean toward the first case (limiting int support) because it provides more typesafety in using those QFlags. |
I think so far we've been maintaining these stubs to be true to the PyQt implementation so far, so I'd be a bit hesitant to start making them opinionated. Maybe we could reach out to Phil on the mailing list to see if he'd want to make PyQt itself better reflect Qt's design? He's already been changing a lot of things about the Enums/Flags in PyQt6; maybe he'd be open to making this change too. |
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.
@BryceBeagle, had you gone over this in any detail? Definitely no need for us both to do so.
But yeah, this is all a horrible mess having three different 'interchangeable' types. The reality of the library, that is, not this PR. The PR is appreciated. :]
tests/windowflags.py
Outdated
try: | ||
windowFlagsTest = windowFlags1 + windowFlags2 # type: ignore | ||
assert False | ||
except TypeError: | ||
pass |
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 this would work.
with pytest.raises(TypeError):
windowFlagsTest = windowFlags1 + windowFlags2
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 ran it without py.test because it would not work very well on my computer. I am not sure how much you have tested all the flow on Windows but I was not interested in debugging it. Anyway, none of the other tests rely on py.test so this was as good as it gets.
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.
Thanks for the explanation. I've only done #123 for Windows so far... :[
PyQt5-stubs/QtCore.pyi
Outdated
@@ -2376,6 +2376,20 @@ class Qt(sip.simplewrapper): | |||
WindowActive = ... # type: Qt.WindowState | |||
|
|||
class WindowType(int): | |||
def __init__(self, f: typing.Union['Qt.WindowType', int]) -> None: ... | |||
def __int__(self) -> int: ... | |||
def __invert__(self) -> 'Qt.WindowFlags': ... |
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.
Actually, this is wrong. I need to ditch away all the WindowType
modifications because they break the Liskov principle : if you inherit int
, overloaded method should return something which is int
compatible, so clearlyQt.WindowFlags
which does not inherit int
is not a good candidate. This applies to all the methods below.
I guess we'll have to do just like in C++, where WindowType
operations are not correctly checked because they become int
after an operation.
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.
That's why I initially ditched the int
inheritance.
def __rxor__(self, other: int) -> 'Qt.WindowFlags': ... | ||
def __radd__ (self, other: int) -> 'Qt.WindowFlags': ... | ||
def __rsub__ (self, other: int) -> 'Qt.WindowFlags': ... | ||
|
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.
Same comment as above, this all has to go.
I'll try to reach to Phil Thompson but there a decision to be made here anyway :-) . I like it how it is now as far as I am concerned. |
@bluebird75, I see you left a couple comments saying things need to change and then one saying you like it how it is. CI is failing so something needs to change somewhere. I haven't yet looked to figure out where. We certainly do have plenty of ignores strewn about for places where accurately representing what PyQt does results in Mypy complaining that we are doing bad things. You can see the Let us know if you want to make some changes or if you'd like us to jump in and help figure this out. |
…dowtype-operations
No, I haven't had a chance to look into this in detail. I've just been making some high-level comments/observations. 👍 |
Breaking Liskov subsitution is OK because that's what PyQt5 does
finally, ready to be merged ! |
Are you still working on this or would you like me to see if I can help with the failures? |
I would like to get this one done, because on the principle, it's a much more appropriate way to describe a QFlag based class. Assuming we get it to work here, all I need to do on other qflag based classes is change the inheritance. I'll give it a short attempt on fixing it and ask for help if I am unsuccessful. |
The CI fails on the mypy check but it passes fine on my computer :
Which brings two questions :
I can probably fix it by using co/contra variants but I would like to be settled on the mypy version first. |
OK. One clear problem is that I did not setup my local requirements per requirements\develop.in . If I do that, I am sure that I will use the expected version of mypy. Still, it would be a good idea to have a CI run with the latest mypy to make sure there are no differences for users using the most recent version. |
I tried to setup the equivalent of the CI on my computer using the classical :
This mandates I guess tox.ini has the final word on this and I should install these versions ? |
I'm not going to do a "complete" fixup of the requirements structure, but I am updating them over in #161. The mypy reference in tox was not great and is being removed. It was for some stubtest fix that wasn't released at that point in time. It shouldn't have been left like that without a note explaining it. In general, I try to keep things working with latest versions of dependencies. Aside, I have had various issues where I can't recreate the CI results locally. It's been awhile so I forget how much I did figure out about that but I know some days I've just given up and gone off what CI says. This is certainly not a great state of affairs but I haven't yet prioritized addressing that. |
Welp, I gave up on the reviews for #161 and merged it. I'll take the liberty to handle the catch up merge conflicts to avoid back and forth here. Just be sure to pull my changes. |
This PR has been superseded by #153 . Closing. |
Before the fix, the test would report: