-
-
Notifications
You must be signed in to change notification settings - Fork 473
Feat/builtin models types #2590
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
base: master
Are you sure you want to change the base?
Feat/builtin models types #2590
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, this is not a full review.
Resolved most of the comments so far Additionally wanted to ask do I go ahead and also add types for the Edit: Added types and tests |
…into feat/builtin-models-types
@sobolevn I think we should be all done, should we also update the
|
|
Thats because of the extension too, here is how the extension handles nulls, first it checks if the expression is nullable then based on that it fetches set_type, get_type = get_field_descriptor_types(
default_return_type.type,
is_set_nullable=is_set_nullable or is_nullable,
is_get_nullable=is_get_nullable or is_nullable,
) after that it fetches mapped types which is the types if generic params are provided and replace the default types gotten above with the mapped types # bail if either mapped_set_type or mapped_get_type have type Never
if not (isinstance(mapped_set_type, UninhabitedType) or isinstance(mapped_get_type, UninhabitedType)):
# always replace set_type and get_type with (non-Any) mapped types
set_type = helpers.convert_any_to_type(mapped_set_type, set_type)
get_type = get_proper_type(helpers.convert_any_to_type(mapped_get_type, get_type)) the reason it works currently is because mapped types are from django.db import models
from datetime import date as Date
from typing_extensions import reveal_type
class MyUser(models.Model):
date = models.DateField(null=True)
my_date = models.DateField[Date, Date](null=True)
u = MyUser()
reveal_type(u.date) # unknown for pyright/pylance, date | None for mypy
reveal_type(u.my_date) # type date for both
As you can see if types are mapped the default expression types are overwritten which is whats happening since all our fields are now inherently mapped with generic type params So to fix this I think we can just make the mapped types optional too if the field is nullable |
@sobolevn any thoughts on this? should we have the plugin enforce null for us if |
We 100% should, but I am pretty sure that it already works. If it does not, please open a new issue. |
It works with the current fields types because the generic parameters are unknown as soon as you add those generic type parameters it overwrites the nullable expression with the generic parameters provided instead, which what's happening here since each field now have specified generic params such as I'll open a new issue regarding this and reference it here |
Sorry, I missed this message #2590 (comment) Reading! |
Oh, now I see: |
But currently our internal fields are all typed with default types meaning our stubs already defines
The only cornercase in soln 2 (preferred to reduce redundancy) is that if user defines explicit typehints our plugin may override that and make it nullable if null=True is defined (imo thats expected behaviour) since currently I dont think there is a way to tell if the generic type params are coming from django-stubs types or user codebase, due to this raising an error might not work unless we can determine whether these explicit type hints are from stubs or user maybe we can use inspect.getFile() or |
One thing I noticed is that if the generic types are user defined the following mypyc attrs are filled or populated in the mypyc plugin, information such a Not sure how foolproof or reliable this method is any thoughts @sobolevn ? https://pastebin.com/7MMsJTMs Here is an example where the types were user defined, you see that line no. and column is populated, whereas in stubs it would just show -1 for both
from django.db import models
from typing_extensions import reveal_type
class TestModel(models.Model):
my_field = models.CharField[str, str](null=True)
t = TestModel()
reveal_type(t.my_field)
from django.db import models
from typing_extensions import reveal_type
class TestModel(models.Model):
my_field = models.CharField(null=True)
t = TestModel()
reveal_type(t.my_field) Difference between the 2 different types of type definition can be seen here https://www.diffchecker.com/0ujD9s30/ |
I have made things!
This PR aims to add type hints to builtin model fields, i.e for example models in
contrib
,admin
,auth
etc.Base generic fields are modified to use the
default=
from PEP-696 to allow the following behaviour for modelsIt eliminates the need to add generic arguments explicitly at each step whenever defining models, and ensures all the models being used internally have type hints.
This PR is not complete yet as I have a few questions to ask as I am not entirely clear about the whole process here are a few points I had doubts on, and wanted some feedback before proceeding
Is there any particular reason models forcontrib/gis/db/backends
are typed Any and not included inallowlist_todo.txt
, since they seem to have types here, should I type out the model fields as suchedit: models typed in line with source
I noticed the presence of most of the inbuilt model fields and methods inallowlist_todo.txt
so how would that be handled do I write tests under theassert_type
andtypecheck/contrib
folder for these models and move them toallowlist.txt
manually if they are type hinted and tested?edit: Resolved created a category
Final question, there is one case where the current model field typing system might need some explicit type hinting from the user in case of where fields become optional or with
null=True
, because with current system I don't think there is a way to infer types from the field parameters passed, Here is an exampleThis is fixable if we modify the
django-mypy-plugin
code but not sure if thats the best option, here is how one might go about itThis was the only way I could think of going about it, and user needs to have the plugin installed.
Related issues
TODO
contrib/gis/backends
models (most probably)