Skip to content

pyqtProperty setter always raises an error #9911

Open

Description

@BoboTiG

Bug Report

It seems that Mypy is not enjoying pyqtProperty setters. Someone already asked on the Qt mailing-list.

To Reproduce

from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal


class FeatureModel(QObject):

    stateChanged = pyqtSignal()

    def __init__(self, enabled: bool, /) -> None:
        super().__init__()
        self._enabled = enabled

    @pyqtProperty(bool, notify=stateChanged)
    def enabled(self):
        return self._enabled

    @enabled.setter
    def enabled(self, enabled):
        self._enabled = enabled
        self.stateChanged.emit()

Expected Behavior

Mypy should be happy.

Actual Behavior

$ python -m mypy file.py
file.py:16: error: Name 'enabled' already defined on line 12
Found 1 error in 1 file (checked 1 source file)

Your Environment

  • Mypy version used: 0.790
  • Mypy command-line flags: None
  • Mypy configuration options from mypy.ini (and other config files): None
  • Python version used: 3.8.6
  • Operating system and version: macOS Big Sur (arm64) and Windows 10.

Activity

  1. changed the title [-]pyqtProperty setter always raise an error[/-] [+]pyqtProperty setter always raises an error[/+] on Jan 14, 2021
  2. freundTech commented on Jan 15, 2021

    @freundTech
    Contributor

    On the Qt mailing-list someone already posted these two links to the mypy source-code:
    https://github.com/python/mypy/blob/v0.782/mypy/semanal.py#L964-L973
    https://github.com/python/mypy/blob/v0.782/mypy/semanal.py#L842-L869

    mypy has some special casing to make pythons @property (and a few other decorators) work.
    The easy solution would probably be to add a special case for @pyqtProperty.
    Cleaner solutions would be to either somehow make it possible to annotate that a decorator creates a property (This could be problematic, as I don't know if existing type annotations can handle this) or write a mypy plugin. I only took a quick look at the plugin interface and think it should be possible to do this. The problem here would be that the plugin API is unstable (See #6617).

    EDIT:
    Looks like writing a plugin for this isn't that easy after all. There is a "get_class_decorator_hook", but no corresponding hook for methods and functions. If such hooks would be added writing a plugin would be possible.

  3. gvanrossum commented on Jan 15, 2021

    @gvanrossum
    Member

    The way to do that without baking knowledge of qt into mypy is to write a mypy extension.

  4. freundTech commented on Jan 16, 2021

    @freundTech
    Contributor

    Should the plugin hook I suggested in #9915 get added to mypy you could solve this using the following mypy plugin (function and class names subject to change):

    from typing import Callable, Optional
    from typing import Type
    
    from mypy.plugin import (
        Plugin, DecoratorContext,
    )
    
    
    def plugin(version: str) -> Type[Plugin]:
        return PyQt5Plugin
    
    
    class PyQt5Plugin(Plugin):
        """PyQt5 Plugin"""
    
        def get_decorator_hook(self, fullname: str
                               ) -> Optional[Callable[[DecoratorContext], None]]:
            if fullname == 'PyQt5.QtCore.pyqtProperty':
                return pyqt_property_callback
            return None
    
    
    def pyqt_property_callback(ctx: DecoratorContext):
        ctx.decorator.func.is_property = True
  5. added a commit that references this issue on May 9, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugmypy got something wrongtopic-descriptorsProperties, class vs. instance attributes

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions