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 doesn't complain about nonexistent attrs on pydantic models #245

Closed
toolness opened this issue Aug 16, 2018 · 3 comments · Fixed by #373
Closed

mypy doesn't complain about nonexistent attrs on pydantic models #245

toolness opened this issue Aug 16, 2018 · 3 comments · Fixed by #373

Comments

@toolness
Copy link

Hi! Thank you for making this great tool, it is very helpful.

I realized, though, that I am running into some interesting problems because pydantic itself doesn't seem to have any mypy type stubs, nor does it seem to tell mypy that it has type information via some PEP 561 mechanism (though I could be wrong, I'm still pretty unfamiliar with that PEP).

Anyways, the ultimate effect is that if I have a simple pydantic model as per your usage with mypy example:

class Model(BaseModel):
    age: int

myModel = Model(age=5)
print(f"myModel's age is {myModel.agee}")

(The typo on the last line there was intentional.)

While mypy does think that myModel.age is an int, it doesn't know anything about BaseModel, so I think it sees all other attributes on the class as Any. This means that mypy doesn't complain when I write e.g. myModel.agee.

To fix this, I made a trivial type stub for pydantic and told mypy about it:

# mypy-stubs/pydantic.py
from typing import Dict, Any


class BaseModel:
    def __init__(self, *args, **kwargs) -> None:
        ...

    def dict(self) -> Dict[str, Any]:
        ...

Now, with this type information, mypy has enough information to complain about that last line.

The only downside is that mypy still doesn't know how to type-check pydantic constructor arguments. I think this is one benefit of allowing dataclasses (or at least named tuples) to be compatible with pydantic, as mentioned in #178: mypy understands that their constructor supports keyword arguments corresponding to their instance properties, so it'd be nice if that understanding extended to pydantic model constructors too. (I tried some "hacks" to make this work, e.g. making BaseModel a dataclass or named tuple in the type stub, but none of them seemed to fool mypy, alas.)

Anyways, I was curious if this kind of thing was a concern, or if I am just missing something obvious, so I figured I'd bring it up here.

@samuelcolvin
Copy link
Member

Thanks for reporting and sorry for the long delay in replying, I guess somehow this slipped through the net when you first reported it.

I'm not at all clear how adding types to __init__ and dict helps mypy infer that .age exists but .agee doesn't, but I guess that's a parcularity of mypy.

Do we have to use stub files, or would type hints added to the python file suffice?

@samuelcolvin
Copy link
Member

My understanding is that mypy can't (and likely won't) be able to effectively inspect a pydantic model because pydantic uses __getattr__:

https://github.com/samuelcolvin/pydantic/blob/9f874a7e97bb1ed291c015b40b3f63e84ae01433/pydantic/main.py#L168-L172

so mypy sees pydantic models as "dynamic". That said perhaps mypy is clever enough to assume the properties of the class definition will be mirrored on instances, I don't know.

@toolness's suggestion above indicates it's nothing to do with __getattr__ and instead a problem with finding type annoations.

if the problem is with __getattr__, it's a (very understandable) limitation of mypy and no pydantic.

As suggested above the solution is probably to use dataclasses.

@samuelcolvin
Copy link
Member

samuelcolvin commented Jan 20, 2019

Solution here is to use dataclasses which I think mypy should work with properly.

as of version 1.1 the mypy plugin should allow full use of mypy with pydantic.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants