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

Type checking unexpectedly passing when misusing default and converter #604

Open
alexpizarroj opened this issue Dec 27, 2019 · 4 comments
Open
Labels
Bug Typing Typing/stub/Mypy/PyRight related bugs.

Comments

@alexpizarroj
Copy link

alexpizarroj commented Dec 27, 2019

I was playing around with type annotations and Attrs, and I think I may have found an issue.

Given the following Python 2/3 script:

from __future__ import absolute_import

from pprint import pprint as pp

import attr


def _convert_int(val):
    # type: (int) -> int
    if val <= 0:
        return -1
    return val


@attr.s
class A(object):
    x = attr.ib(default=0, converter=_convert_int, type=int)
    y = attr.ib(default=None, converter=_convert_int, type=int)  # `default` is incompatible with our declared `type`


def main():
    # type: () -> None
    a1 = A(5, 5)        # correct: type checks
    pp(a1)
    a2 = A(5, None)     # correct: doesn't type check
    pp(a2)
    a3 = A(5)           # incorrect: type checks but shouldn't!
    pp(a3)


if __name__ == '__main__':
    main()

I'd expect MyPy to complain about the relationship between default and converter for y. Nevertheless, I'm getting:

image

Which is one error less than expected.

Should I perhaps report this to MyPy or Typeshed as well/instead?

@hynek
Copy link
Member

hynek commented Dec 30, 2019

Is this related to #518? 🤔

I remember that there were issues with converters and typing but I don't remember the details.

@euresti
Copy link
Contributor

euresti commented Dec 31, 2019

Yeah this is the same bug. Converters are extremely hard to handle. :)

@wsanchez wsanchez added Bug Typing Typing/stub/Mypy/PyRight related bugs. labels Jan 3, 2020
@stevetarver
Copy link

Just cause I am reading through issues: is the root problem not using converters.optional? Or alternatively, adding a None check in the local validator?

from pprint import pprint as pp

import attr
from attr import converters


def _convert_int(val):
    # type: (int) -> int
    if val <= 0:
        return -1
    return val


@attr.s
class A(object):
    x = attr.ib(default=0, converter=_convert_int, type=int)
    y = attr.ib(default=None, converter=converters.optional(_convert_int), type=int)  # `default` is incompatible with our declared `type`


def main():
    # type: () -> None
    a1 = A(5, 5)  # correct: type checks
    pp(a1)
    a2 = A(5, None)  # correct: doesn't type check
    pp(a2)
    a3 = A(5)  # incorrect: type checks but shouldn't!
    pp(a3)


if __name__ == '__main__':
    main()

seems to produce the expected response

@ghost
Copy link

ghost commented Aug 3, 2020

@stevetarver In my example, I want y to not allow None.

In any case, I believe this goes beyond being an issue with converters only, since I would have expected the following excerpt to fail at class definition:

import attr


@attr.s
class Foo(object):
    bar = attr.ib(default=None, type=int)


a = Foo(5)
b = Foo(None)  # L10
c = Foo()

Strict MyPy v0.782 output (mypy --strict ~/tmp/test.py):

/Users/apizarro/tmp/test.py:10: error: Argument 1 to "Foo" has incompatible type "None"; expected "int"
Found 1 error in 1 file (checked 1 source file)

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

No branches or pull requests

5 participants