Search code examples
python-3.xpython-decoratorsmypy

How can I use @property setters and make mypy happy?


I have the following example.py file:

class Location(object):
    def __init__(self, latitude, longitude):
        self.latitude = latitude
        self.longitude = longitude

    @property
    def latitude(self):
        return self._latitude

    @property
    def longitude(self):
        return self._longitude

    @latitude.setter
    def latitude(self, latitude):
        """Setter for latiutde."""
        if not (-90 <= latitude <= 90):
            raise ValueError('latitude was {}, but has to be in [-90, 90]'
                             .format(latitude))
        self._latitude = latitude

    @longitude.setter
    def longitude(self, longitude):
        """Setter for longitude."""
        if not (-180 <= longitude <= 180):
            raise ValueError('longitude was {}, but has to be in [-180, 180]'
                             .format(longitude))
        self._longitude = longitude

    def __repr__(self):
        return 'Location({}, {})'.format(self.latitude, self.longitude)

    __str__ = __repr__


munich = Location(48.137222222222, 11.57555)
print(munich)
try:
    munich.latitude = 200
    print("This should not work")
except ValueError:
    pass

When I run mypy example.py (mypy version 0.73) I get a couple of errors:

$ mypy example.py 
example.py:14: error: Name 'latitude' already defined on line 6
example.py:14: error: "Callable[[Any], Any]" has no attribute "setter"
example.py:22: error: Name 'longitude' already defined on line 10
example.py:22: error: "Callable[[Any], Any]" has no attribute "setter"
example.py:39: error: Property "latitude" defined in "Location" is read-only
Found 5 errors in 1 file (checked 1 source file)

Why do I get those and how can I fix it?


Solution

  • The issue is probably related to Property setter not accepted if not next to getter.

    The following code incorrectly raises an error:

    class Config(object):
    
        @property
        def my_proprty(self):
            return None
    
        def _other(self):
            pass
    
        @my_proprty.setter
        def my_proprty(self, val):
            pass Error:
    
    mypytest.py: note: In class "Config": mypytest.py:12: error:
    Callable[[Any], Any] has no attribute "setter"
    

    MyPy closed the issue and marked it as a false-positive. It appears they have no intentions of fixing it as of now.

    We don't have immediate plans for fix this issue, but we are happy to receive a PR. - JukkaL

    Moving your getters and setters next to each other (with the getter first) should fix the issue.

    Consider it a feature :)