mirror of
https://github.com/davidhalter/django-stubs.git
synced 2025-12-15 08:17:08 +08:00
Fix CI (#1108)
* Fix CI
* Fix CI
* Fix CI
* Fix CI
* APply black
* APply black
* Fix mypy
* Fix mypy errors in django-stubs
* Fix format
* Fix plugin
* Do not patch builtins by default
* Fix mypy
* Only run mypy on 3.10 for now
* Only run mypy on 3.10 for now
* WHAT THE HELL
* Enable strict mode in mypy
* Enable strict mode in mypy
* Fix tests
* Fix tests
* Debug
* Debug
* Fix tests
* Fix tests
* Add TYPE_CHECKING debug
* Caching maybe?
* Caching maybe?
* Try explicit `${{ matrix.python-version }}`
* Remove debug
* Fix typing
* Finally
This commit is contained in:
@@ -1,9 +1,8 @@
|
||||
import builtins
|
||||
import os
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from contextlib import contextmanager
|
||||
from typing import TYPE_CHECKING, Dict, Iterable, Iterator, Optional, Set, Tuple, Type, Union
|
||||
from typing import TYPE_CHECKING, Any, Dict, Iterable, Iterator, Optional, Set, Tuple, Type, Union
|
||||
|
||||
from django.core.exceptions import FieldError
|
||||
from django.db import models
|
||||
@@ -39,7 +38,7 @@ if TYPE_CHECKING:
|
||||
|
||||
|
||||
@contextmanager
|
||||
def temp_environ():
|
||||
def temp_environ() -> Iterator[None]:
|
||||
"""Allow the ability to set os.environ temporarily"""
|
||||
environ = dict(os.environ)
|
||||
try:
|
||||
@@ -56,19 +55,6 @@ def initialize_django(settings_module: str) -> Tuple["Apps", "LazySettings"]:
|
||||
# add current directory to sys.path
|
||||
sys.path.append(os.getcwd())
|
||||
|
||||
def noop_class_getitem(cls, key):
|
||||
return cls
|
||||
|
||||
from django.db import models
|
||||
|
||||
models.QuerySet.__class_getitem__ = classmethod(noop_class_getitem) # type: ignore
|
||||
models.Manager.__class_getitem__ = classmethod(noop_class_getitem) # type: ignore
|
||||
|
||||
# Define mypy builtins, to not cause NameError during setting up Django.
|
||||
# TODO: temporary/unpatch
|
||||
builtins.reveal_type = lambda _: None
|
||||
builtins.reveal_locals = lambda: None
|
||||
|
||||
from django.apps import apps
|
||||
from django.conf import settings
|
||||
|
||||
@@ -80,8 +66,8 @@ def initialize_django(settings_module: str) -> Tuple["Apps", "LazySettings"]:
|
||||
|
||||
apps.populate(settings.INSTALLED_APPS)
|
||||
|
||||
assert apps.apps_ready
|
||||
assert settings.configured
|
||||
assert apps.apps_ready, "Apps are not ready"
|
||||
assert settings.configured, "Settings are not configured"
|
||||
|
||||
return apps, settings
|
||||
|
||||
@@ -127,7 +113,7 @@ class DjangoContext:
|
||||
return model_cls
|
||||
return None
|
||||
|
||||
def get_model_fields(self, model_cls: Type[Model]) -> Iterator[Field]:
|
||||
def get_model_fields(self, model_cls: Type[Model]) -> Iterator["Field[Any, Any]"]:
|
||||
for field in model_cls._meta.get_fields():
|
||||
if isinstance(field, Field):
|
||||
yield field
|
||||
@@ -137,7 +123,9 @@ class DjangoContext:
|
||||
if isinstance(field, ForeignObjectRel):
|
||||
yield field
|
||||
|
||||
def get_field_lookup_exact_type(self, api: TypeChecker, field: Union[Field, ForeignObjectRel]) -> MypyType:
|
||||
def get_field_lookup_exact_type(
|
||||
self, api: TypeChecker, field: Union["Field[Any, Any]", ForeignObjectRel]
|
||||
) -> MypyType:
|
||||
if isinstance(field, (RelatedField, ForeignObjectRel)):
|
||||
related_model_cls = field.related_model
|
||||
primary_key_field = self.get_primary_key_field(related_model_cls)
|
||||
@@ -155,7 +143,7 @@ class DjangoContext:
|
||||
return AnyType(TypeOfAny.explicit)
|
||||
return helpers.get_private_descriptor_type(field_info, "_pyi_lookup_exact_type", is_nullable=field.null)
|
||||
|
||||
def get_primary_key_field(self, model_cls: Type[Model]) -> Field:
|
||||
def get_primary_key_field(self, model_cls: Type[Model]) -> "Field[Any, Any]":
|
||||
for field in model_cls._meta.get_fields():
|
||||
if isinstance(field, Field):
|
||||
if field.primary_key:
|
||||
@@ -258,11 +246,11 @@ class DjangoContext:
|
||||
def all_registered_model_class_fullnames(self) -> Set[str]:
|
||||
return {helpers.get_class_fullname(cls) for cls in self.all_registered_model_classes}
|
||||
|
||||
def get_attname(self, field: Field) -> str:
|
||||
def get_attname(self, field: "Field[Any, Any]") -> str:
|
||||
attname = field.attname
|
||||
return attname
|
||||
|
||||
def get_field_nullability(self, field: Union[Field, ForeignObjectRel], method: Optional[str]) -> bool:
|
||||
def get_field_nullability(self, field: Union["Field[Any, Any]", ForeignObjectRel], method: Optional[str]) -> bool:
|
||||
if method in ("values", "values_list"):
|
||||
return field.null
|
||||
|
||||
@@ -279,7 +267,9 @@ class DjangoContext:
|
||||
return True
|
||||
return nullable
|
||||
|
||||
def get_field_set_type(self, api: TypeChecker, field: Union[Field, ForeignObjectRel], *, method: str) -> MypyType:
|
||||
def get_field_set_type(
|
||||
self, api: TypeChecker, field: Union["Field[Any, Any]", ForeignObjectRel], *, method: str
|
||||
) -> MypyType:
|
||||
"""Get a type of __set__ for this specific Django field."""
|
||||
target_field = field
|
||||
if isinstance(field, ForeignKey):
|
||||
@@ -297,7 +287,9 @@ class DjangoContext:
|
||||
field_set_type = helpers.convert_any_to_type(field_set_type, argument_field_type)
|
||||
return field_set_type
|
||||
|
||||
def get_field_get_type(self, api: TypeChecker, field: Union[Field, ForeignObjectRel], *, method: str) -> MypyType:
|
||||
def get_field_get_type(
|
||||
self, api: TypeChecker, field: Union["Field[Any, Any]", ForeignObjectRel], *, method: str
|
||||
) -> MypyType:
|
||||
"""Get a type of __get__ for this specific Django field."""
|
||||
field_info = helpers.lookup_class_typeinfo(api, field.__class__)
|
||||
if field_info is None:
|
||||
@@ -321,14 +313,16 @@ class DjangoContext:
|
||||
else:
|
||||
return helpers.get_private_descriptor_type(field_info, "_pyi_private_get_type", is_nullable=is_nullable)
|
||||
|
||||
def get_field_related_model_cls(self, field: Union[RelatedField, ForeignObjectRel]) -> Optional[Type[Model]]:
|
||||
def get_field_related_model_cls(
|
||||
self, field: Union["RelatedField[Any, Any]", ForeignObjectRel]
|
||||
) -> Optional[Type[Model]]:
|
||||
if isinstance(field, RelatedField):
|
||||
related_model_cls = field.remote_field.model
|
||||
else:
|
||||
related_model_cls = field.field.model
|
||||
|
||||
if isinstance(related_model_cls, str):
|
||||
if related_model_cls == "self": # type: ignore[unreachable]
|
||||
if related_model_cls == "self": # type: ignore
|
||||
# same model
|
||||
related_model_cls = field.model
|
||||
elif "." not in related_model_cls:
|
||||
@@ -342,9 +336,9 @@ class DjangoContext:
|
||||
|
||||
def _resolve_field_from_parts(
|
||||
self, field_parts: Iterable[str], model_cls: Type[Model]
|
||||
) -> Union[Field, ForeignObjectRel]:
|
||||
) -> Union["Field[Any, Any]", ForeignObjectRel]:
|
||||
currently_observed_model = model_cls
|
||||
field: Union[Field, ForeignObjectRel, GenericForeignKey, None] = None
|
||||
field: Union["Field[Any, Any]", ForeignObjectRel, GenericForeignKey, None] = None
|
||||
for field_part in field_parts:
|
||||
if field_part == "pk":
|
||||
field = self.get_primary_key_field(currently_observed_model)
|
||||
@@ -364,7 +358,9 @@ class DjangoContext:
|
||||
assert isinstance(field, (Field, ForeignObjectRel))
|
||||
return field
|
||||
|
||||
def resolve_lookup_into_field(self, model_cls: Type[Model], lookup: str) -> Union[Field, ForeignObjectRel]:
|
||||
def resolve_lookup_into_field(
|
||||
self, model_cls: Type[Model], lookup: str
|
||||
) -> Union["Field[Any, Any]", ForeignObjectRel]:
|
||||
query = Query(model_cls)
|
||||
lookup_parts, field_parts, is_expression = query.solve_lookup_type(lookup)
|
||||
|
||||
|
||||
@@ -95,7 +95,10 @@ def lookup_fully_qualified_typeinfo(api: Union[TypeChecker, SemanticAnalyzer], f
|
||||
return node
|
||||
|
||||
|
||||
def lookup_class_typeinfo(api: TypeChecker, klass: type) -> Optional[TypeInfo]:
|
||||
def lookup_class_typeinfo(api: TypeChecker, klass: Optional[type]) -> Optional[TypeInfo]:
|
||||
if klass is None:
|
||||
return None
|
||||
|
||||
fullname = get_class_fullname(klass)
|
||||
field_info = lookup_fully_qualified_typeinfo(api, fullname)
|
||||
return field_info
|
||||
@@ -183,7 +186,7 @@ def get_private_descriptor_type(type_info: TypeInfo, private_field_name: str, is
|
||||
return AnyType(TypeOfAny.explicit)
|
||||
|
||||
|
||||
def get_field_lookup_exact_type(api: TypeChecker, field: Field) -> MypyType:
|
||||
def get_field_lookup_exact_type(api: TypeChecker, field: "Field[Any, Any]") -> MypyType:
|
||||
if isinstance(field, (RelatedField, ForeignObjectRel)):
|
||||
lookup_type_class = field.related_model
|
||||
rel_model_info = lookup_class_typeinfo(api, lookup_type_class)
|
||||
@@ -318,7 +321,7 @@ def resolve_string_attribute_value(attr_expr: Expression, django_context: "Djang
|
||||
member_name = attr_expr.name
|
||||
if isinstance(attr_expr.expr, NameExpr) and attr_expr.expr.fullname == "django.conf.settings":
|
||||
if hasattr(django_context.settings, member_name):
|
||||
return getattr(django_context.settings, member_name)
|
||||
return getattr(django_context.settings, member_name) # type: ignore
|
||||
return None
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import sys
|
||||
from functools import partial
|
||||
from typing import Callable, Dict, List, Optional, Tuple
|
||||
from typing import Callable, Dict, List, Optional, Tuple, Type
|
||||
|
||||
from django.db.models.fields.related import RelatedField
|
||||
from mypy.modulefinder import mypy_path
|
||||
@@ -72,7 +72,7 @@ class NewSemanalDjangoPlugin(Plugin):
|
||||
def _get_current_queryset_bases(self) -> Dict[str, int]:
|
||||
model_sym = self.lookup_fully_qualified(fullnames.QUERYSET_CLASS_FULLNAME)
|
||||
if model_sym is not None and isinstance(model_sym.node, TypeInfo):
|
||||
return helpers.get_django_metadata(model_sym.node).setdefault(
|
||||
return helpers.get_django_metadata(model_sym.node).setdefault( # type: ignore[no-any-return]
|
||||
"queryset_bases", {fullnames.QUERYSET_CLASS_FULLNAME: 1}
|
||||
)
|
||||
else:
|
||||
@@ -81,7 +81,7 @@ class NewSemanalDjangoPlugin(Plugin):
|
||||
def _get_current_manager_bases(self) -> Dict[str, int]:
|
||||
model_sym = self.lookup_fully_qualified(fullnames.MANAGER_CLASS_FULLNAME)
|
||||
if model_sym is not None and isinstance(model_sym.node, TypeInfo):
|
||||
return helpers.get_django_metadata(model_sym.node).setdefault(
|
||||
return helpers.get_django_metadata(model_sym.node).setdefault( # type: ignore[no-any-return]
|
||||
"manager_bases", {fullnames.MANAGER_CLASS_FULLNAME: 1}
|
||||
)
|
||||
else:
|
||||
@@ -90,7 +90,7 @@ class NewSemanalDjangoPlugin(Plugin):
|
||||
def _get_current_model_bases(self) -> Dict[str, int]:
|
||||
model_sym = self.lookup_fully_qualified(fullnames.MODEL_CLASS_FULLNAME)
|
||||
if model_sym is not None and isinstance(model_sym.node, TypeInfo):
|
||||
return helpers.get_django_metadata(model_sym.node).setdefault(
|
||||
return helpers.get_django_metadata(model_sym.node).setdefault( # type: ignore[no-any-return]
|
||||
"model_bases", {fullnames.MODEL_CLASS_FULLNAME: 1}
|
||||
)
|
||||
else:
|
||||
@@ -99,7 +99,7 @@ class NewSemanalDjangoPlugin(Plugin):
|
||||
def _get_current_form_bases(self) -> Dict[str, int]:
|
||||
model_sym = self.lookup_fully_qualified(fullnames.BASEFORM_CLASS_FULLNAME)
|
||||
if model_sym is not None and isinstance(model_sym.node, TypeInfo):
|
||||
return helpers.get_django_metadata(model_sym.node).setdefault(
|
||||
return helpers.get_django_metadata(model_sym.node).setdefault( # type: ignore[no-any-return]
|
||||
"baseform_bases",
|
||||
{
|
||||
fullnames.BASEFORM_CLASS_FULLNAME: 1,
|
||||
@@ -305,5 +305,5 @@ class NewSemanalDjangoPlugin(Plugin):
|
||||
return None
|
||||
|
||||
|
||||
def plugin(version):
|
||||
def plugin(version: str) -> Type[NewSemanalDjangoPlugin]:
|
||||
return NewSemanalDjangoPlugin
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import TYPE_CHECKING, Optional, Tuple, Union, cast
|
||||
from typing import TYPE_CHECKING, Any, Optional, Tuple, Union, cast
|
||||
|
||||
from django.db.models.fields import AutoField, Field
|
||||
from django.db.models.fields.related import RelatedField
|
||||
@@ -18,7 +18,7 @@ if TYPE_CHECKING:
|
||||
|
||||
def _get_current_field_from_assignment(
|
||||
ctx: FunctionContext, django_context: DjangoContext
|
||||
) -> Optional[Union[Field, ForeignObjectRel, "GenericForeignKey"]]:
|
||||
) -> Optional[Union["Field[Any, Any]", ForeignObjectRel, "GenericForeignKey"]]:
|
||||
outer_model_info = helpers.get_typechecker_api(ctx).scope.active_class()
|
||||
if outer_model_info is None or not helpers.is_model_subclass_info(outer_model_info, django_context):
|
||||
return None
|
||||
@@ -42,7 +42,7 @@ def _get_current_field_from_assignment(
|
||||
return current_field
|
||||
|
||||
|
||||
def reparametrize_related_field_type(related_field_type: Instance, set_type, get_type) -> Instance:
|
||||
def reparametrize_related_field_type(related_field_type: Instance, set_type: MypyType, get_type: MypyType) -> Instance:
|
||||
args = [
|
||||
helpers.convert_any_to_type(related_field_type.args[0], set_type),
|
||||
helpers.convert_any_to_type(related_field_type.args[1], get_type),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from typing import Optional, Union
|
||||
|
||||
from mypy.checker import TypeChecker, fill_typevars
|
||||
from mypy.checker import TypeChecker
|
||||
from mypy.nodes import (
|
||||
GDEF,
|
||||
CallExpr,
|
||||
@@ -19,6 +19,7 @@ from mypy.plugin import AttributeContext, DynamicClassDefContext, SemanticAnalyz
|
||||
from mypy.types import AnyType, CallableType, Instance, ProperType
|
||||
from mypy.types import Type as MypyType
|
||||
from mypy.types import TypeOfAny
|
||||
from mypy.typevars import fill_typevars
|
||||
from typing_extensions import Final
|
||||
|
||||
from mypy_django_plugin.lib import fullnames, helpers
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import Dict, List, Optional, Type, Union, cast
|
||||
from typing import Any, Dict, List, Optional, Type, Union, cast
|
||||
|
||||
from django.db.models import Manager, Model
|
||||
from django.db.models.fields import DateField, DateTimeField, Field
|
||||
@@ -166,7 +166,7 @@ class ModelClassInitializer:
|
||||
|
||||
return queryset_info
|
||||
|
||||
def run_with_model_cls(self, model_cls):
|
||||
def run_with_model_cls(self, model_cls: Type[Model]) -> None:
|
||||
raise NotImplementedError("Implement this in subclasses")
|
||||
|
||||
|
||||
@@ -202,7 +202,7 @@ class AddDefaultPrimaryKey(ModelClassInitializer):
|
||||
|
||||
def create_autofield(
|
||||
self,
|
||||
auto_field: Field,
|
||||
auto_field: "Field[Any, Any]",
|
||||
dest_name: str,
|
||||
existing_field: bool,
|
||||
) -> None:
|
||||
@@ -394,7 +394,7 @@ class AddManagers(ModelClassInitializer):
|
||||
|
||||
return None
|
||||
|
||||
def get_dynamic_manager(self, fullname: str, manager: Manager) -> Optional[TypeInfo]:
|
||||
def get_dynamic_manager(self, fullname: str, manager: "Manager[Any]") -> Optional[TypeInfo]:
|
||||
"""
|
||||
Try to get a dynamically defined manager
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user