pydantic: Mypy finds errors in the first example from the documentation
This example
from datetime import datetime
from typing import List
from pydantic import BaseModel
class User(BaseModel):
id: int
name = 'John Doe'
signup_ts: datetime = None
friends: List[int] = []
external_data = {
'id': '123',
'signup_ts': '2019-06-01 12:22',
'friends': [1, 2, '3']
}
user = User(**external_data)
print(user.id)
print(repr(user.signup_ts))
print(user.friends)
print(user.dict())
$ mypy --strict pydantic_example.py
pydantic_example.py:3: error: Module 'pydantic' has no attribute 'BaseModel'
pydantic_example.py:5: error: Class cannot subclass 'BaseModel' (has type 'Any')
pydantic_example.py:8: error: Incompatible types in assignment (expression has type "None", variable has type "datetime")
This is not false positive. Pydantic __init__.py
uses implicit reexport (this is an error, and I don’t want to ignore it using --no-implicit-reexport
), and this example uses None for a non-optional field (this is also an error).
In general, I can’t trust a library that suggests putting None in a non-optional field in the very first example. This violates the mypy type system.
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Comments: 17 (11 by maintainers)
Commits related to this issue
- restore pypy builds for x86_64 (#1072) — committed to alexdrydew/pydantic by davidhewitt 8 months ago
All right, well once your PEP to change mypy errors to syntax errors is accepted, we’ll get right on converting the behavior of every edge case in the use of this library.
@dmontagu and @samuelcolvin it seems like you’re both playing a new troll game 🤣
I agree that the implicit reexport is a nuisance; I think it is worth addressing. @samuelcolvin any ideas?
While mypy doesn’t like the following:
signup_ts: datetime = None
, there is an awkward inconsistency with mypy that this would be allowed as function arguments, and would imply the argument was optional. Given that it raises a mypy error, if you want to pass mypy, of course you should “correct” it. It is valid pydantic though if you choose not to care.For what it’s worth, there may be times when you explicitly choose to override the annotations and have mypy assume the type is str, but allow it to be None for pydantic (eg, if you know a validator will always make it non-None). Obviously you would want to add
# type: ignore
but this doesn’t necessarily need to be a part of the documentation.That said, I’m also not opposed to changing the docs here, PR welcome.
Well, if you can’t trust the library, you are absolutely free to not use it.
This kind of language is not appreciated by the people who put much of their spare time into this project, and are offering it up to you for free. If you care enough to create an issue, please be courteous and try to phrase your misgivings in a less incendiary way.
Just to chime in here, I also found the first example pretty weird for the same
signup_ts: datetime = None
reason.My background is in Typescript, which has two null-checking modes: one where
null
is implicitly assignable to every type (like most languages), and one where it is not (--strictNullChecks
, where you have to declare things likestring | null
). The root of the problem seems to be that mypy has a strict mode that people like to use (*raises hand*), whereas Python’s type system (and, by extension, Pydantic) doesn’t take a stance on strict versus lax null checking.Notably, the example in question does not mention
Optional
or mypy anywhere, and I understand that it is valid Python, as Python operates in the first mode (noted above). So I guess what I’m trying to say is: there is an audience for Pydantic that is coming from other type checked languages (or strict-mypy codebases), rather than from un-type-checked Python, and the first example as written is jarring and unexpected to us.The second line
name = 'John Doe'
also reads pretty strangely to me, since struct-like field declarations are a unusual place to have type inference (at least, coming from other typed languages).I don’t know what you want to do with that information, but hopefully it helps clear up why this example is odd to some people.
It absolutely does not mean this, and it feels like you are being purposely obtuse for the sake of being contrarian.
All it means is that this one documentation example is not valid with mypy. You absolutely do not need to make use of any mypy-violating patterns in your use of pydantic, and we have done our best to make sure that pydantic is completely compatible with complete mypy coverage.
I personally use the same mypy config that pydantic uses in all of my projects, and while it is not
--strict
, it is also far from lenient. (And I think we should absolutely strive toward better supporting--strict
mode.)Yes, there are some patterns that work with pydantic that are not valid with mypy. There is also an enormous amount of non-pydantic python code that is valid python and not valid with mypy.
You’ve miss quoted me, I said
Which is quite different. pydantic is type hinted.
I was talking about examples in documentation where it’s common (and I would argue good practice) to partially omit type hints to keep the code as succinct and easy to read as possible.
just for you 😉