Adding __init__ breaks type inference from __new__
Dieses Issue hat noch niemand übernommen.
- Vorherrschende Sprache
- Python
- Sterne
- 20.6k
- Forks
- 3.3k
- PR-Merge-Kennzahlen
- PR-Kennzahlen ausstehend
Beschreibung
Hello, i'm trying to fix a bug in django type stubs, and i believe i've stubled upon bug in mypy.
Django has following syntax for defining foreign keys:
class Book(models.Model):
author = models.ForeignKey(Author, null=True)
This means author should be Author | None.
Right now django-types are using @override for __init__ with differently typed self parameter:
@overload
def __init__(
self: ForeignKey[_M],
...
null: Literal[False],
...
@overload
def __init__(
self: ForeignKey[Optional[_M]],
...
null: Literal[True],
...
And it works for mypy, but doesn't work for pyright. In mypy it works becouse it uses undocumented feature of typing self, as i discovered here:
Proper, and most concise way of doing this, would be overloading __new__ with args/kwargs and bool: Literal[...], and leaving full annotation with all arguments typed in __init__ (which i attempted here https://github.com/sbdchd/django-types/pull/86).
But, when we try to do this: mypy would ignore overrides in __new__ and will always use non-optional case.
To Reproduce
To reproduce this without much clutter, we can use following code:
# related_field_minimal.pyi
from typing import Generic, Literal, Optional, Type, TypeVar, overload
class Model: ...
_M = TypeVar("_M", bound=Optional[Model])
class ForeignKey(Generic[_M]):
@overload
def __new__(
cls,
to: Type[_M],
null: Literal[False] = ...,
) -> ForeignKey[_M]: ...
@overload
def __new__(
cls,
to: Type[_M],
null: Literal[True],
) -> ForeignKey[Optional[_M]]: ...
def __init__(self, to: Type[_M], null: bool) -> None: ...
# models.py
from typing import Optional
from related_field_minimal import ForeignKey, Model
class Test(Model):
pass
class BookModel(Model):
required_field = ForeignKey(to=Test, null=False)
optional_f1 = ForeignKey(to=Test, null=True)
optional_f2 = ForeignKey[Test](to=Test, null=True)
optional_f3 = ForeignKey[Optional[Test]](to=Test, null=True)
reveal_type(BookModel.required_field)
reveal_type(BookModel.optional_f1)
reveal_type(BookModel.optional_f2)
reveal_type(BookModel.optional_f3)
When there is no __init__ in ForeignKey, inferred types are correct, both in mypy and pyright.
models.py:15: note: Revealed type is "related_field_minimal.ForeignKey[models.Test]"
models.py:16: note: Revealed type is "related_field_minimal.ForeignKey[Union[models.Test, None]]"
models.py:17: note: Revealed type is "related_field_minimal.ForeignKey[Union[models.Test, None]]"
models.py:18: note: Revealed type is "related_field_minimal.ForeignKey[Union[models.Test, None]]"
As soon as we add __init__ to our type definition in related_field_minimal.pyi, it becomes incorrect in mypy:
models.py:15: note: Revealed type is "related_field_minimal.ForeignKey[models.Test]"
models.py:16: note: Revealed type is "related_field_minimal.ForeignKey[models.Test]"
models.py:17: note: Revealed type is "related_field_minimal.ForeignKey[models.Test]"
models.py:18: note: Revealed type is "related_field_minimal.ForeignKey[Union[models.Test, None]]"
(pyright still handles this case correctly).
Your Environment
- Mypy version used: 0.931
- Mypy command-line flags: None
- Mypy configuration options from
mypy.ini(and other config files): None - Python version used: 3.10
Beitragsleitfaden
Erste Schritte
- Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
- Forke das Repository und arbeite in einem Branch.
- Öffne einen Pull Request, der die Issue-Nummer nennt.
Rechercherichtung
Beginne mit der Ausführung der mypy-0.931-Reproduktion unter Verwendung von related_field_minimal.pyi und models.py und vergleiche die Ergebnisse mit und ohne init. Vergleiche die aufgedeckten Typen mit den erwarteten Ausgaben ForeignKey[Test] und ForeignKey[Optional[Test]]; das Issue ist abgeschlossen, wenn die Overloads nach dem Hinzufügen des vollständig typisierten init weiterhin korrekt sind.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Bewertung
- Tech-Stack
- python
- Bereich
- compilers, devtools
- Issue-Typ
- Bug
- Schwierigkeit
- 4/5
- Geschätzter Aufwand
- 3-5 Tage
- Aktivitätsstatus
- Veraltet
- Klarheit
- Klar beschrieben
- Anfängerfreundlichkeit
- 35/100