Skip to content
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

mypy returns "Incompatible types in assignment" with on_setattr=setters.convert #889

Open
anthrotype opened this issue Dec 16, 2021 · 3 comments
Labels
Typing Typing/stub/Mypy/PyRight related bugs.

Comments

@anthrotype
Copy link

anthrotype commented Dec 16, 2021

Not sure if this is a mypy issue or attrs or both.

I have an attrs class that defines a field with a converter that coerces the input value to a custom dict subclass.
It works fine when creating a new object, I pass in a dict and the field gets converted to the my dict subclass. I think #710 is responsible for inferring __init__ annotations from the converters'.

Now I would like to do the same with on_setattr, by using attrs.setters.convert: i.e. I want to set the field to a plain dict and have it converted automatically using the field converter to my dict subclass.

This works fine at runtime, but when I run mypy on my code, it complains that Incompatible types in assignment (expression has type Dict[...], variable has type "...") when I do that.

Here's a made up example:

from __future__ import annotations
from typing import Any, Dict, Mapping, Union
from attr import define, field, setters


class Lib(Dict[str, Any]):
    pass


def _convert_lib(v: Mapping[str, Any]) -> Lib:
    return v if isinstance(v, Lib) else Lib(v)


@define(on_setattr=setters.convert)
class Glyph:
    lib: Lib = field(factory=Lib, converter=_convert_lib)


g = Glyph({"foo": [1, 2, 3]})

assert isinstance(g.lib, Lib)

g.lib = {"bar": [4, 5, 6]}

assert isinstance(g.lib, Lib)
$ mypy /tmp/test_mypy_attrs.py
/tmp/test_mypy_attrs.py:23: error: Incompatible types in assignment (expression has type "Dict[str, List[int]]", variable has type "Lib")

Is there a way around this besides an ugly # type: ignore on every line I use the attrs generated setter-cum-converter?

thanks in advance

@anthrotype
Copy link
Author

I think this is the same issue as python/mypy#10187

@anthrotype
Copy link
Author

anthrotype commented Dec 16, 2021

Even without the on_setattr feature, just using a plain property setter decorator that calls the converter, I get the same error from mypy about Incompatible types in assignment.

@define(on_setattr=setters.NO_OP)
class Glyph:
    _lib: Lib = field(factory=Lib, converter=_convert_lib)

    @property
    def lib(self) -> Lib:
        return self._lib

    @lib.setter
    def lib(self, value: Mapping[str, Any]) -> None:
        self._lib = _convert_lib(value)

It appears that mypy doesn't support "asymmetric properties" whereby the setter annotation is different from the getter => python/mypy#3004

But am not sure if that's the same or related issue to the on_setattr=convert one.

@hynek hynek added the Typing Typing/stub/Mypy/PyRight related bugs. label Dec 17, 2021
@hynek
Copy link
Member

hynek commented Dec 17, 2021

My gut feeling is that mypy's support for setters is simply too spotty but maybe the typing experts could chime in. :-/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Typing Typing/stub/Mypy/PyRight related bugs.
Projects
None yet
Development

No branches or pull requests

2 participants