python-desert / desert

Deserialize to objects while staying DRY
https://desert.readthedocs.io
MIT License
158 stars 10 forks source link

Type hinting complains on desert.field() (maybe .ib()?) #101

Open altendky opened 4 years ago

altendky commented 4 years ago

https://github.com/python-desert/desert/blob/631de5f98d0f3edd3ecbab57100c5664a43bf3d5/src/desert/__init__.py#L55-L66

Below is my attempt to minimally recreate the issue and show some not-working options and output.

https://mypy-play.net/?mypy=latest&python=3.8&flags=strict%2Ccheck-untyped-defs&gist=7956a372fa34375077012351364a3aec

import dataclasses
import typing

T = typing.TypeVar('T')

def make_a_field_t() -> T:
    return dataclasses.field()

def make_a_field_field() -> dataclasses.Field:
    return dataclasses.field()

def make_a_field_field_any() -> dataclasses.Field[typing.Any]:
    return dataclasses.field()

def make_a_field_nothing():
    return dataclasses.field()

@dataclasses.dataclass
class C:
    a: int = dataclasses.field()
    b: int = dataclasses.Field()
    c: int = make_a_field_t()
    d: int = make_a_field_field()
    e: int = make_a_field_field_any()
    f: int = make_a_field_nothing()

reveal_type(dataclasses.field)
reveal_type(dataclasses.Field)
reveal_type(make_a_field_t)
reveal_type(make_a_field_field)
reveal_type(make_a_field_field_any)
reveal_type(make_a_field_nothing)
main.py:9: error: Returning Any from function declared to return "T"
main.py:12: error: Missing type parameters for generic type "Field"
main.py:13: error: Returning Any from function declared to return "Field[Any]"
main.py:17: error: Returning Any from function declared to return "Field[Any]"
main.py:20: error: Function is missing a return type annotation
main.py:27: error: Incompatible types in assignment (expression has type "Field[<nothing>]", variable has type "int")
main.py:29: error: Incompatible types in assignment (expression has type "Field[Any]", variable has type "int")
main.py:30: error: Incompatible types in assignment (expression has type "Field[Any]", variable has type "int")
main.py:31: error: Call to untyped function "make_a_field_nothing" in typed context
main.py:34: note: Revealed type is 'Overload(def [_T] (*, default: _T`-1, init: builtins.bool =, repr: builtins.bool =, hash: Union[builtins.bool, None] =, compare: builtins.bool =, metadata: Union[typing.Mapping[builtins.str, Any], None] =) -> _T`-1, def [_T] (*, default_factory: def () -> _T`-1, init: builtins.bool =, repr: builtins.bool =, hash: Union[builtins.bool, None] =, compare: builtins.bool =, metadata: Union[typing.Mapping[builtins.str, Any], None] =) -> _T`-1, def (*, init: builtins.bool =, repr: builtins.bool =, hash: Union[builtins.bool, None] =, compare: builtins.bool =, metadata: Union[typing.Mapping[builtins.str, Any], None] =) -> Any)'
main.py:35: note: Revealed type is 'def [_T] () -> dataclasses.Field[_T`1]'
main.py:36: note: Revealed type is 'def [T] () -> T`-1'
main.py:37: note: Revealed type is 'def () -> dataclasses.Field[Any]'
main.py:38: note: Revealed type is 'def () -> dataclasses.Field[Any]'
main.py:39: note: Revealed type is 'def () -> Any'
Found 9 errors in 1 file (checked 1 source file)
amin-nejad commented 2 years ago

I have also experienced this issue when running mypy on my desert code. Here is an even more minimal example:

import desert
from dataclasses import dataclass
from typing import List
from marshmallow import fields

@dataclass
class Foo:

    list_of_strings: List[str] = desert.field(
        fields.List(fields.String()), default_factory=list
    )

Running mypy gives the following error:

blah.py: note: In class "Foo":
blah.py:10:34: error: Incompatible types in assignment (expression has type "Field[Any]", variable has type "List[str]")  [assignment]
        list_of_strings: List[str] = desert.field(
                                     ^
Found 1 error in 1 file (checked 1 source file) 

Is there any way we can make the desert field return type match what we have specified?