Issue
I have the following type-annotated Django code:
from typing import Optional, Type, TypeVar
from django.db import models
T = TypeVar('T', bound=models.Model)
def get_obj_or_none(model: Type[T], obj_id: int) -> Optional[T]:
try:
return model.objects.get(pk=obj_id)
except model.DoesNotExist:
return None
The function expects a class derived from django.db.models.Model
as the first parameter and an int
id as the second parameter:
# obj is now the Person with id 123, or None if that didn't exist.
obj = get_obj_or_none(Person, 123)
But when I run mypy on the code, I get the error:
error: "Type[T]" has no attribute "objects" error: "Type[T]" has no attribute "DoesNotExist"
But if change my code to this and run mypy again, I get no errors:
from typing import Optional, Type
from django.db import models
def get_obj_or_none(model: Type[models.Model], obj_id: int) -> Optional[models.Model]:
try:
return model.objects.get(pk=obj_id)
except model.DoesNotExist:
return None
Why doesn't the first example work? I would really prefer to use it since the second example doesn't tie the return value in any way to the model
parameter, so the function could be returning an instance, which is completely unrelated to the class given as the first parameter.
I'm using Python 3.8.1 with mypy 0.761.
Edit:
Here's a self contained example, which can be tested as is:
from typing import Dict, Optional, Type, TypeVar
class Model:
objects: Dict[int, 'Model'] = {}
T = TypeVar('T', bound=Model)
def get_obj_or_none(model: Type[T], obj_id: int) -> Optional[T]:
try:
return model.objects[obj_id]
except KeyError:
return None
Running mypy on this gives (to my surprise) a completely different error:
type_example.py:17: error: Incompatible return value type (got "Model", expected "Optional[T]") Found 1 error in 1 file (checked 1 source file)
Why does mypy behave differently on these two examples? Can I fix both of the cases somehow?
Solution
The first example works correctly after setting up django-stubs for my project. Good instructions can be found from this SO answer.
The last example gives correctly an error from mypy, since it's not allowed by Python. To quote from a GitHub issue I opened:
The objects attribute can contain an instance of an arbitrary
Model
subclass, but the return type ofget_obj_or_none
contains a specific subtypeT
ofModel
.Type[T]
has no effect in this example, since the type of theobjects
attribute is the same in subclasses (it doesn't use a "self" type). I don't think that there's a way to use a self type for a class variable.
Answered By - ruohola
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.