Skip to content

False positives when redefining property in subclass #5936

Description

@mthuurne

I'm running mypy on the following code: (no additional arguments)

class Base:
	def __init__(self, value):
		self._value = value

	@property
	def value(self):
		return self._value

class Sub(Base):
	@Base.value.setter
	def value(self, value):
		self._value = value

And it reports:

inherited_property.py:10: error: "Callable[[Any], Any]" has no attribute "setter"

Line 10 is @Base.value.setter, so it seems to have a problem with referring to a property defined in a different class. Python itself has no problem with this though.

If the property has a setter in the base class, a slightly different error is reported:

class Base:
	def __init__(self, value):
		self._value = value

	@property
	def value(self):
		return self._value

	@value.setter
	def value(self, value):
		self._value = value

class Sub(Base):
	@Base.value.setter
	def value(self, value):
		self._value = value

This results in mypy reporting:

inherited_property.py:14: error: overloaded function has no attribute "setter"

Again the error is reported on the @Base.value.setter line.

I'm using a mypy dev snapshot (0.650+) on Python 3.6.5.

I found the existing issue #220, but that is so generic that I figured it would make sense to open a new separate issue for tracking this specific use case.

Activity

  1. JukkaL commented on Nov 22, 2018

    @JukkaL
    Collaborator

    Yeah, this is something that mypy should perhaps support. This is the first time I've seen this idiom, so it's probably not very common though.

    This workaround works currently:

    class Base:
    	def __init__(self, value: int) -> None:
    		self._value = value
    
    	@property
    	def value(self) -> int:
    		return self._value
    
    class Sub(Base):
    	@property
    	def value(self) -> int:
    		return super().value
    
    	@value.setter
    	def value(self, value: int) -> None:
    		self._value = value
  2. mthuurne commented on Jun 22, 2019

    @mthuurne
    ContributorAuthor

    This problem might be a special case of #4644.

  3. mthuurne commented on Jun 22, 2019

    @mthuurne
    ContributorAuthor

    Actually it looks like both are a special case of #1465, as pointed out in this comment.

    Changing @value.setter to @Base.value.setter in your example is accepted by mypy. So it seems the problem is not the Base.value idiom, but the fact that mypy doesn't recognize a property setter unless it is directly preceded by the getter.

  4. RIS3V3N commented on Apr 1, 2022

    @RIS3V3N

    I had the same issue. Because I have a lot of sub-classes and I don't want to copy the property around so many times I ended up doing this:

    • added a type-ignore comment to the decorators where I overwrite the setters, like @Base.value.setter # type: ignore[attr-defined]
    • made a comment in the base-class explaining why I do this and pointing to this post
  5. Cleptomania commented on Feb 14, 2023

    @Cleptomania

    I've run in to this issue as well and created #14684, and I've found another work-around that produces no errors from MyPy. It seems to not be problematic if the property in the base class is declared using the property() function as opposed to the decorators, like below, note that my specific use case was attempting to only override the setter of the Base class.

    class Base:
        def __init__(self, value: int) -> None:
            self._value = value
    
        def _get_value(self) -> int:
            return self._value
    
        def _set_value(self, value: int):
            self._value = value
    
        value = property(_get_value, _set_value)
        
    class Sub(Base):
        def __init__(self, value: int) -> None:
            super().__init__(value)
            
        @Base.value.setter
        def value(self, value: int) -> None:
            Base.value.fset(self, value)
            print("Do Something after the Base setter")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions