30 Commits

Author SHA1 Message Date
Maxim Kurnikov
9ea25f3e56 bump version 2019-02-22 00:13:59 +03:00
Maxim Kurnikov
dacf88c692 optimize hooks a bit 2019-02-22 00:12:23 +03:00
Maxim Kurnikov
3d14d07e4e incremental = True for plugin tests should be fixed now 2019-02-21 17:35:46 +03:00
Maxim Kurnikov
6e6d1645d3 enable incremental mode for tests, disable it for one so that it would pass 2019-02-21 00:06:09 +03:00
Maxim Kurnikov
2bd018951b forms, generic views fixes 2019-02-20 22:24:26 +03:00
Maxim Kurnikov
14ea848dd7 add nested Meta inheritance support for forms 2019-02-20 21:52:28 +03:00
Maxim Kurnikov
2d3b5492f0 fix form errors in CI 2019-02-20 21:24:49 +03:00
Maxim Kurnikov
194258ab8e Merge pull request #23 from syastrov/better-types-for-transaction-atomic
Add better typings plus test for transaction.atomic.
2019-02-20 21:12:14 +03:00
Maxim Kurnikov
116aa2c539 clean up forms 2019-02-20 15:22:46 +03:00
Seth Yastrov
67c99434e5 Add better typings plus test for transaction.atomic.
- All cases are handled, including bare decorator (@transaction.atomic).
- Decorated function's signature is preserved when type-checking.
2019-02-20 06:40:22 +01:00
Maxim Kurnikov
5d8cdbcf29 fix integer set type 2019-02-20 02:38:45 +03:00
Maxim Kurnikov
78810f55b6 Merge pull request #26 from roderik333/supertype-processformview
*args and **kwargs changed from 'object' to 'str' and 'any' in post()…
2019-02-19 15:23:50 +03:00
Rune Steinnes
36662896bc *args and **kwargs changed from 'object' to 'str' and 'any' in post(), put() and get() 2019-02-19 12:48:07 +01:00
Maxim Kurnikov
e54dbb79c9 Merge pull request #24 from roderik333/replace-wsgirequest-in-loginrequiredmixin
Replaced WSGIRequest with http.HttpRequest in mixin:LoginRequiredMixin
2019-02-19 13:54:43 +03:00
Rune Steinnes
41f283552a Replaced WSGIRequest with http.HttpRequest in mixin:LoginRequiredMixin 2019-02-19 11:16:27 +01:00
Maxim Kurnikov
ab73d53ae5 add support for models defined in the same module be specified as name of class in related fields 2019-02-19 00:43:27 +03:00
Maxim Kurnikov
d24be4b35f add supported versions to README 2019-02-19 00:42:12 +03:00
Maxim Kurnikov
9d60b472df fix *args, **kwargs for views.generic.base 2019-02-18 15:45:01 +03:00
Maxim Kurnikov
632e063e22 back to incremental = True for tests 2019-02-18 02:16:13 +03:00
Maxim Kurnikov
66224416b5 bump version 2019-02-18 01:47:45 +03:00
Maxim Kurnikov
e5b2496eb5 update django tests sources to latest commit 2019-02-18 01:05:57 +03:00
Maxim Kurnikov
f980311be0 finish strict_optional support, enable it for typechecking of django tests 2019-02-18 00:52:56 +03:00
Maxim Kurnikov
400a0f0486 silence some false positives 2019-02-17 20:20:33 +03:00
Maxim Kurnikov
882ec71d23 remove redundant test 2019-02-17 18:08:58 +03:00
Maxim Kurnikov
e9f9202ed1 preliminary support for strict_optional 2019-02-17 18:07:53 +03:00
Maxim Kurnikov
6763217a80 some strict optional fixes 2019-02-16 21:28:37 +03:00
Maxim Kurnikov
6da5ead6f0 move to pypi version of pytest plugin 2019-02-15 22:01:35 +03:00
Maxim Kurnikov
c382d6aa2f fix redefining field with name id with different than int type 2019-02-15 21:54:40 +03:00
Maxim Kurnikov
63a14f7107 chmod +x 2019-02-15 20:06:13 +03:00
Maxim Kurnikov
dc33dd9493 fix setup.py definition 2019-02-15 20:03:55 +03:00
58 changed files with 1432 additions and 886 deletions

View File

@@ -7,6 +7,10 @@
This package contains type stubs and mypy plugin to provide more precise static types and type inference for Django framework. Django uses some Python "magic" that makes having precise types for some code patterns problematic. This is why we need to accompany the stubs with mypy plugins. The final goal is to be able to get precise types for most common patterns. This package contains type stubs and mypy plugin to provide more precise static types and type inference for Django framework. Django uses some Python "magic" that makes having precise types for some code patterns problematic. This is why we need to accompany the stubs with mypy plugins. The final goal is to be able to get precise types for most common patterns.
Supports Python 3.6/3.7, and Django 2.1.x series.
Could be run on earlier versions of Django, but expect some missing imports warnings.
## Installation ## Installation
``` ```

View File

@@ -1,3 +1,3 @@
black black
-e git+https://github.com/mkurnikov/pytest-mypy-plugins.git#egg=pytest-mypy-plugins pytest-mypy-plugins
-e . -e .

View File

@@ -12,7 +12,7 @@ class AppConfig:
verbose_name: str = ... verbose_name: str = ...
path: str = ... path: str = ...
models_module: None = ... models_module: None = ...
models: Optional[Dict[str, Type[Model]]] = ... models: Dict[str, Type[Model]] = ...
def __init__(self, app_name: str, app_module: Optional[Any]) -> None: ... def __init__(self, app_name: str, app_module: Optional[Any]) -> None: ...
@classmethod @classmethod
def create(cls, entry: str) -> AppConfig: ... def create(cls, entry: str) -> AppConfig: ...

View File

@@ -5,7 +5,7 @@ by the DJANGO_SETTINGS_MODULE environment variable.
# This is defined here as a do-nothing function because we can't import # This is defined here as a do-nothing function because we can't import
# django.utils.translation -- that module depends on the settings. # django.utils.translation -- that module depends on the settings.
from typing import Any, Dict, List, Optional, Pattern, Tuple, Protocol, Union, Callable, TYPE_CHECKING from typing import Any, Dict, List, Optional, Pattern, Tuple, Protocol, Union, Callable, TYPE_CHECKING, Sequence
#################### ####################
# CORE # # CORE #
@@ -377,7 +377,7 @@ CACHE_MIDDLEWARE_ALIAS = "default"
AUTH_USER_MODEL: str = ... AUTH_USER_MODEL: str = ...
AUTHENTICATION_BACKENDS: List[str] = ... AUTHENTICATION_BACKENDS: Sequence[str] = ...
LOGIN_URL = "/accounts/login/" LOGIN_URL = "/accounts/login/"

View File

@@ -25,7 +25,7 @@ class LogEntryManager(models.Manager["LogEntry"]):
class LogEntry(models.Model): class LogEntry(models.Model):
action_time: models.DateTimeField = ... action_time: models.DateTimeField = ...
user: models.ForeignKey = ... user: models.ForeignKey = ...
content_type: models.ForeignKey[ContentType] = ... content_type: models.ForeignKey = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id: models.TextField = ... object_id: models.TextField = ...
object_repr: models.CharField = ... object_repr: models.CharField = ...
action_flag: models.PositiveSmallIntegerField = ... action_flag: models.PositiveSmallIntegerField = ...

View File

@@ -57,14 +57,20 @@ class BaseModelAdmin:
checks_class: Any = ... checks_class: Any = ...
def check(self, **kwargs: Any) -> List[Union[str, Error]]: ... def check(self, **kwargs: Any) -> List[Union[str, Error]]: ...
def __init__(self) -> None: ... def __init__(self) -> None: ...
def formfield_for_dbfield(self, db_field: Field, request: WSGIRequest, **kwargs: Any) -> Optional[Field]: ... def formfield_for_dbfield(
def formfield_for_choice_field(self, db_field: Field, request: WSGIRequest, **kwargs: Any) -> TypedChoiceField: ... self, db_field: Field, request: Optional[WSGIRequest], **kwargs: Any
def get_field_queryset(self, db: None, db_field: RelatedField, request: WSGIRequest) -> Optional[QuerySet]: ... ) -> Optional[Field]: ...
def formfield_for_choice_field(
self, db_field: Field, request: Optional[WSGIRequest], **kwargs: Any
) -> TypedChoiceField: ...
def get_field_queryset(
self, db: None, db_field: RelatedField, request: Optional[WSGIRequest]
) -> Optional[QuerySet]: ...
def formfield_for_foreignkey( def formfield_for_foreignkey(
self, db_field: ForeignKey, request: WSGIRequest, **kwargs: Any self, db_field: ForeignKey, request: Optional[WSGIRequest], **kwargs: Any
) -> Optional[ModelChoiceField]: ... ) -> Optional[ModelChoiceField]: ...
def formfield_for_manytomany( def formfield_for_manytomany(
self, db_field: ManyToManyField, request: WSGIRequest, **kwargs: Any self, db_field: ManyToManyField, request: Optional[WSGIRequest], **kwargs: Any
) -> ModelMultipleChoiceField: ... ) -> ModelMultipleChoiceField: ...
def get_autocomplete_fields(self, request: WSGIRequest) -> Tuple: ... def get_autocomplete_fields(self, request: WSGIRequest) -> Tuple: ...
def get_view_on_site_url(self, obj: Optional[Model] = ...) -> Optional[str]: ... def get_view_on_site_url(self, obj: Optional[Model] = ...) -> Optional[str]: ...
@@ -90,7 +96,7 @@ class BaseModelAdmin:
class ModelAdmin(BaseModelAdmin): class ModelAdmin(BaseModelAdmin):
formfield_overrides: Any formfield_overrides: Any
list_display: Sequence[Union[str, Callable]] = ... list_display: Sequence[Union[str, Callable]] = ...
list_display_links: Sequence[Union[str, Callable]] = ... list_display_links: Optional[Sequence[Union[str, Callable]]] = ...
list_filter: Sequence[Union[str, Type[ListFilter], Tuple[str, Type[ListFilter]]]] = ... list_filter: Sequence[Union[str, Type[ListFilter], Tuple[str, Type[ListFilter]]]] = ...
list_select_related: Union[bool, Sequence[str]] = ... list_select_related: Union[bool, Sequence[str]] = ...
list_per_page: int = ... list_per_page: int = ...

View File

@@ -43,7 +43,7 @@ class NestedObjects(Collector):
def add_edge(self, source: Optional[Model], target: Model) -> None: ... def add_edge(self, source: Optional[Model], target: Model) -> None: ...
def collect( def collect(
self, self,
objs: Union[Sequence[Model], QuerySet], objs: Union[Sequence[Optional[Model]], QuerySet],
source: Optional[Type[Model]] = ..., source: Optional[Type[Model]] = ...,
source_attr: Optional[str] = ..., source_attr: Optional[str] = ...,
**kwargs: Any **kwargs: Any

View File

@@ -1,9 +1,13 @@
from typing import Any, Dict, List, Optional, Tuple, Type from typing import Any, Dict, List, Optional, Tuple, Type, Union
from django.contrib.auth.models import User, Group from django.contrib.auth.models import User, Group
from django.core.handlers.wsgi import WSGIRequest from django.core.handlers.wsgi import WSGIRequest
from django.db.models.fields import Field
from django.db.models.fields.related import ManyToManyField from django.db.models.fields.related import ManyToManyField
from django.db.models.options import Options
from django.forms.models import ModelMultipleChoiceField from django.forms.models import ModelMultipleChoiceField
from django.forms.fields import Field as FormField
from django.forms.widgets import Widget
from django.http.response import HttpResponse from django.http.response import HttpResponse
from django.urls.resolvers import URLPattern from django.urls.resolvers import URLPattern
@@ -27,10 +31,7 @@ class GroupAdmin(admin.ModelAdmin):
class UserAdmin(admin.ModelAdmin): class UserAdmin(admin.ModelAdmin):
admin_site: AdminSite admin_site: AdminSite
formfield_overrides: Dict[ formfield_overrides: Dict[Type[Field], Dict[str, Type[Union[FormField, Widget]]]]
Type[Union[django.db.models.fields.DateTimeCheckMixin, Field]],
Dict[str, Type[Union[django.forms.fields.SplitDateTimeField, Widget]]],
]
model: Type[User] model: Type[User]
opts: Options opts: Options
add_form_template: str = ... add_form_template: str = ...

View File

@@ -1,6 +1,6 @@
from typing import Any, Callable, List, Optional from typing import Any, Callable, List, Optional
from django.core.handlers.wsgi import WSGIRequest from django import http
from django.http.response import HttpResponse, HttpResponseRedirect from django.http.response import HttpResponse, HttpResponseRedirect
class AccessMixin: class AccessMixin:
@@ -14,15 +14,15 @@ class AccessMixin:
def handle_no_permission(self) -> HttpResponseRedirect: ... def handle_no_permission(self) -> HttpResponseRedirect: ...
class LoginRequiredMixin(AccessMixin): class LoginRequiredMixin(AccessMixin):
def dispatch(self, request: WSGIRequest, *args: Any, **kwargs: Any) -> HttpResponse: ... def dispatch(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse: ...
class PermissionRequiredMixin(AccessMixin): class PermissionRequiredMixin(AccessMixin):
permission_required: Any = ... permission_required: Any = ...
def get_permission_required(self) -> List[str]: ... def get_permission_required(self) -> List[str]: ...
def has_permission(self) -> bool: ... def has_permission(self) -> bool: ...
def dispatch(self, request: WSGIRequest, *args: Any, **kwargs: Any) -> HttpResponse: ... def dispatch(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse: ...
class UserPassesTestMixin(AccessMixin): class UserPassesTestMixin(AccessMixin):
def test_func(self) -> None: ... def test_func(self) -> None: ...
def get_test_func(self) -> Callable: ... def get_test_func(self) -> Callable: ...
def dispatch(self, request: WSGIRequest, *args: Any, **kwargs: Any) -> HttpResponse: ... def dispatch(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse: ...

View File

@@ -15,7 +15,7 @@ class PermissionManager(models.Manager):
class Permission(models.Model): class Permission(models.Model):
content_type_id: int content_type_id: int
name: models.CharField = ... name: models.CharField = ...
content_type: models.ForeignKey[ContentType] = ... content_type: models.ForeignKey = models.ForeignKey(ContentType, on_delete=models.CASCADE)
codename: models.CharField = ... codename: models.CharField = ...
def natural_key(self) -> Tuple[str, str, str]: ... def natural_key(self) -> Tuple[str, str, str]: ...
@@ -24,7 +24,7 @@ class GroupManager(models.Manager):
class Group(models.Model): class Group(models.Model):
name: models.CharField = ... name: models.CharField = ...
permissions: models.ManyToManyField[Permission] = ... permissions: models.ManyToManyField = models.ManyToManyField(Permission)
def natural_key(self): ... def natural_key(self): ...
class UserManager(BaseUserManager): class UserManager(BaseUserManager):
@@ -37,8 +37,8 @@ class UserManager(BaseUserManager):
class PermissionsMixin(models.Model): class PermissionsMixin(models.Model):
is_superuser: models.BooleanField = ... is_superuser: models.BooleanField = ...
groups: models.ManyToManyField[Group] = ... groups: models.ManyToManyField = models.ManyToManyField(Group)
user_permissions: models.ManyToManyField[Permission] = ... user_permissions: models.ManyToManyField = models.ManyToManyField(Permission)
def get_group_permissions(self, obj: None = ...) -> Set[str]: ... def get_group_permissions(self, obj: None = ...) -> Set[str]: ...
def get_all_permissions(self, obj: Optional[str] = ...) -> Set[str]: ... def get_all_permissions(self, obj: Optional[str] = ...) -> Set[str]: ...
def has_perm(self, perm: Union[Tuple[str, Any], str], obj: Optional[str] = ...) -> bool: ... def has_perm(self, perm: Union[Tuple[str, Any], str], obj: Optional[str] = ...) -> bool: ...

View File

@@ -1,4 +1,4 @@
from typing import Any, Callable, Dict, List, Optional, Tuple, Type, Union from typing import Any, Callable, Dict, List, Optional, Tuple, Type, Union, Generic
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.checks.messages import Error from django.core.checks.messages import Error

View File

@@ -1,20 +1,51 @@
from typing import Any, Generic, List, Optional, Sequence, TypeVar from typing import Any, Iterable, List, Optional, Sequence, TypeVar, Union
from django.db.models.expressions import Combinable
from django.db.models.fields import Field, _ErrorMessagesToOverride, _FieldChoices, _ValidatorCallable
from django.db.models.fields import Field
from .mixins import CheckFieldDefaultMixin from .mixins import CheckFieldDefaultMixin
_T = TypeVar("_T", bound=Field) # __set__ value type
_ST = TypeVar("_ST")
# __get__ return type
_GT = TypeVar("_GT")
class ArrayField(CheckFieldDefaultMixin, Field[_ST, _GT]):
_pyi_private_set_type: Union[Sequence[Any], Combinable]
_pyi_private_get_type: List[Any]
class ArrayField(CheckFieldDefaultMixin, Field, Generic[_T]):
empty_strings_allowed: bool = ... empty_strings_allowed: bool = ...
default_error_messages: Any = ... default_error_messages: Any = ...
base_field: Any = ... base_field: Any = ...
size: Any = ... size: Any = ...
default_validators: Any = ... default_validators: Any = ...
from_db_value: Any = ... from_db_value: Any = ...
def __init__(self, base_field: _T, size: Optional[int] = ..., **kwargs: Any) -> None: ... def __init__(
self,
base_field: Field,
size: Optional[int] = ...,
verbose_name: Optional[Union[str, bytes]] = ...,
name: Optional[str] = ...,
primary_key: bool = ...,
max_length: Optional[int] = ...,
unique: bool = ...,
blank: bool = ...,
null: bool = ...,
db_index: bool = ...,
default: Any = ...,
editable: bool = ...,
auto_created: bool = ...,
serialize: bool = ...,
unique_for_date: Optional[str] = ...,
unique_for_month: Optional[str] = ...,
unique_for_year: Optional[str] = ...,
choices: Optional[_FieldChoices] = ...,
help_text: str = ...,
db_column: Optional[str] = ...,
db_tablespace: Optional[str] = ...,
validators: Iterable[_ValidatorCallable] = ...,
error_messages: Optional[_ErrorMessagesToOverride] = ...,
) -> None: ...
@property @property
def description(self): ... def description(self): ...
def get_transform(self, name: Any): ... def get_transform(self, name: Any): ...
def __set__(self, instance, value: Sequence[_T]) -> None: ...
def __get__(self, instance, owner) -> List[_T]: ...

View File

@@ -1,6 +1,5 @@
from typing import Any, Dict, Iterator, List, Optional, Tuple, Type, Union from typing import Any, Dict, Iterator, List, Mapping, Optional, Tuple, Union
from django.db.models.base import Model
from django.forms.utils import ErrorDict from django.forms.utils import ErrorDict
class FieldDoesNotExist(Exception): ... class FieldDoesNotExist(Exception): ...
@@ -31,20 +30,13 @@ class ValidationError(Exception):
message: Any = ... message: Any = ...
code: Any = ... code: Any = ...
params: Any = ... params: Any = ...
def __init__( def __init__(self, message: Any, code: Optional[str] = ..., params: Optional[Mapping[str, Any]] = ...) -> None: ...
self,
message: Any,
code: Optional[str] = ...,
params: Optional[
Union[Dict[str, Union[Tuple[str], Type[Model], Model, str]], Dict[str, Union[int, str]]]
] = ...,
) -> None: ...
@property @property
def message_dict(self) -> Dict[str, List[str]]: ... def message_dict(self) -> Dict[str, List[str]]: ...
@property @property
def messages(self) -> List[str]: ... def messages(self) -> List[str]: ...
def update_error_dict( def update_error_dict(
self, error_dict: Union[Dict[str, List[ValidationError]], ErrorDict] self, error_dict: Mapping[str, Any]
) -> Union[Dict[str, List[ValidationError]], ErrorDict]: ... ) -> Union[Dict[str, List[ValidationError]], ErrorDict]: ...
def __iter__(self) -> Iterator[Union[Tuple[str, List[str]], str]]: ... def __iter__(self) -> Iterator[Union[Tuple[str, List[str]], str]]: ...

View File

@@ -39,10 +39,10 @@ class InMemoryUploadedFile(UploadedFile):
charset: Optional[str], charset: Optional[str],
content_type_extra: Dict[str, str] = ..., content_type_extra: Dict[str, str] = ...,
) -> None: ... ) -> None: ...
def chunks(self, chunk_size: int = None) -> Iterator[bytes]: ... def chunks(self, chunk_size: Optional[int] = ...) -> Iterator[bytes]: ...
def multiple_chunks(self, chunk_size: int = None) -> bool: ... def multiple_chunks(self, chunk_size: Optional[int] = ...) -> bool: ...
class SimpleUploadedFile(InMemoryUploadedFile): class SimpleUploadedFile(InMemoryUploadedFile):
def __init__(self, name: str, content: bytes, content_type: str = "") -> None: ... def __init__(self, name: str, content: Optional[Union[bytes, str]], content_type: str = ...) -> None: ...
@classmethod @classmethod
def from_dict(cls: Any, file_dict: Dict[str, Union[str, bytes]]) -> None: ... def from_dict(cls: Any, file_dict: Dict[str, Union[str, bytes]]) -> None: ...

View File

@@ -46,12 +46,12 @@ class FieldRelatedOptionOperation(ModelOptionOperation): ...
class AlterUniqueTogether(FieldRelatedOptionOperation): class AlterUniqueTogether(FieldRelatedOptionOperation):
option_name: str = ... option_name: str = ...
unique_together: Collection[Sequence[str]] = ... unique_together: Collection[Sequence[str]] = ...
def __init__(self, name: str, unique_together: Collection[Sequence[str]]) -> None: ... def __init__(self, name: str, unique_together: Optional[Collection[Sequence[str]]]) -> None: ...
class AlterIndexTogether(FieldRelatedOptionOperation): class AlterIndexTogether(FieldRelatedOptionOperation):
option_name: str = ... option_name: str = ...
index_together: Collection[Sequence[str]] = ... index_together: Collection[Sequence[str]] = ...
def __init__(self, name: str, index_together: Collection[Sequence[str]]) -> None: ... def __init__(self, name: str, index_together: Optional[Collection[Sequence[str]]]) -> None: ...
class AlterOrderWithRespectTo(FieldRelatedOptionOperation): class AlterOrderWithRespectTo(FieldRelatedOptionOperation):
order_with_respect_to: str = ... order_with_respect_to: str = ...

View File

@@ -22,9 +22,9 @@ class ModelState:
name: str name: str
app_label: str app_label: str
fields: List[Tuple[str, Field]] fields: List[Tuple[str, Field]]
options: Optional[Dict[str, Any]] = ... options: Dict[str, Any] = ...
bases: Optional[Tuple[Type[Model]]] = ... bases: Tuple[Type[Model]] = ...
managers: Optional[List[Tuple[str, Manager]]] = ... managers: List[Tuple[str, Manager]] = ...
def __init__( def __init__(
self, self,
app_label: str, app_label: str,

View File

@@ -1,16 +1,14 @@
import uuid
from datetime import date, time, datetime, timedelta
from typing import Any, Optional, Tuple, Iterable, Callable, Dict, Union, Type, TypeVar, Generic
import decimal import decimal
import uuid
from typing_extensions import Literal from datetime import date, datetime, time, timedelta
from typing import Any, Callable, Dict, Generic, Iterable, Optional, Tuple, Type, TypeVar, Union
from django.db.models import Model from django.db.models import Model
from django.db.models.query_utils import RegisterLookupMixin
from django.db.models.expressions import F, Combinable
from django.core.exceptions import FieldDoesNotExist as FieldDoesNotExist from django.core.exceptions import FieldDoesNotExist as FieldDoesNotExist
from django.forms import Widget, Field as FormField from django.db.models.expressions import Combinable
from django.db.models.query_utils import RegisterLookupMixin
from django.forms import Field as FormField, Widget
from .mixins import NOT_PROVIDED as NOT_PROVIDED from .mixins import NOT_PROVIDED as NOT_PROVIDED
_Choice = Tuple[Any, Any] _Choice = Tuple[Any, Any]
@@ -20,7 +18,15 @@ _FieldChoices = Iterable[Union[_Choice, _ChoiceNamedGroup]]
_ValidatorCallable = Callable[..., None] _ValidatorCallable = Callable[..., None]
_ErrorMessagesToOverride = Dict[str, Any] _ErrorMessagesToOverride = Dict[str, Any]
class Field(RegisterLookupMixin): # __set__ value type
_ST = TypeVar("_ST")
# __get__ return type
_GT = TypeVar("_GT")
class Field(RegisterLookupMixin, Generic[_ST, _GT]):
_pyi_private_set_type: Any
_pyi_private_get_type: Any
widget: Widget widget: Widget
help_text: str help_text: str
db_table: str db_table: str
@@ -52,7 +58,8 @@ class Field(RegisterLookupMixin):
validators: Iterable[_ValidatorCallable] = ..., validators: Iterable[_ValidatorCallable] = ...,
error_messages: Optional[_ErrorMessagesToOverride] = ..., error_messages: Optional[_ErrorMessagesToOverride] = ...,
): ... ): ...
def __get__(self, instance, owner) -> Any: ... def __set__(self, instance, value: _ST) -> None: ...
def __get__(self, instance, owner) -> _GT: ...
def deconstruct(self) -> Any: ... def deconstruct(self) -> Any: ...
def set_attributes_from_name(self, name: str) -> None: ... def set_attributes_from_name(self, name: str) -> None: ...
def db_type(self, connection: Any) -> str: ... def db_type(self, connection: Any) -> str: ...
@@ -63,23 +70,25 @@ class Field(RegisterLookupMixin):
def contribute_to_class(self, cls: Type[Model], name: str, private_only: bool = ...) -> None: ... def contribute_to_class(self, cls: Type[Model], name: str, private_only: bool = ...) -> None: ...
def to_python(self, value: Any) -> Any: ... def to_python(self, value: Any) -> Any: ...
class IntegerField(Field): class IntegerField(Field[_ST, _GT]):
def __set__(self, instance, value: Union[int, Combinable, Literal[""]]) -> None: ... _pyi_private_set_type: Union[float, int, str, Combinable]
def __get__(self, instance, owner) -> int: ... _pyi_private_get_type: int
class PositiveIntegerRelDbTypeMixin: class PositiveIntegerRelDbTypeMixin:
def rel_db_type(self, connection: Any): ... def rel_db_type(self, connection: Any): ...
class PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField): ... class PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField[_ST, _GT]): ...
class PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField): ... class PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField[_ST, _GT]): ...
class SmallIntegerField(IntegerField): ... class SmallIntegerField(IntegerField[_ST, _GT]): ...
class BigIntegerField(IntegerField): ... class BigIntegerField(IntegerField[_ST, _GT]): ...
class FloatField(Field): class FloatField(Field[_ST, _GT]):
def __set__(self, instance, value: Union[float, int, str, Combinable]) -> float: ... _pyi_private_set_type: Union[float, int, str, Combinable]
def __get__(self, instance, owner) -> float: ... _pyi_private_get_type: float
class DecimalField(Field): class DecimalField(Field[_ST, _GT]):
_pyi_private_set_type: Union[str, float, decimal.Decimal, Combinable]
_pyi_private_get_type: decimal.Decimal
def __init__( def __init__(
self, self,
verbose_name: Optional[Union[str, bytes]] = ..., verbose_name: Optional[Union[str, bytes]] = ...,
@@ -102,13 +111,14 @@ class DecimalField(Field):
validators: Iterable[_ValidatorCallable] = ..., validators: Iterable[_ValidatorCallable] = ...,
error_messages: Optional[_ErrorMessagesToOverride] = ..., error_messages: Optional[_ErrorMessagesToOverride] = ...,
): ... ): ...
def __set__(self, instance, value: Union[str, float, decimal.Decimal, Combinable]) -> decimal.Decimal: ...
def __get__(self, instance, owner) -> decimal.Decimal: ...
class AutoField(Field): class AutoField(Field[_ST, _GT]):
def __get__(self, instance, owner) -> int: ... _pyi_private_set_type: Union[Combinable, int, str]
_pyi_private_get_type: int
class CharField(Field): class CharField(Field[_ST, _GT]):
_pyi_private_set_type: Union[str, int, Combinable]
_pyi_private_get_type: str
def __init__( def __init__(
self, self,
verbose_name: Optional[Union[str, bytes]] = ..., verbose_name: Optional[Union[str, bytes]] = ...,
@@ -133,10 +143,8 @@ class CharField(Field):
validators: Iterable[_ValidatorCallable] = ..., validators: Iterable[_ValidatorCallable] = ...,
error_messages: Optional[_ErrorMessagesToOverride] = ..., error_messages: Optional[_ErrorMessagesToOverride] = ...,
): ... ): ...
def __set__(self, instance, value: Union[str, int, Combinable]) -> None: ...
def __get__(self, instance, owner) -> str: ...
class SlugField(CharField): class SlugField(CharField[_ST, _GT]):
def __init__( def __init__(
self, self,
verbose_name: Optional[Union[str, bytes]] = ..., verbose_name: Optional[Union[str, bytes]] = ...,
@@ -163,25 +171,29 @@ class SlugField(CharField):
error_messages: Optional[_ErrorMessagesToOverride] = ..., error_messages: Optional[_ErrorMessagesToOverride] = ...,
): ... ): ...
class EmailField(CharField): ... class EmailField(CharField[_ST, _GT]): ...
class URLField(CharField): ... class URLField(CharField[_ST, _GT]): ...
class TextField(Field): class TextField(Field[_ST, _GT]):
def __set__(self, instance, value: Union[str, Combinable]) -> None: ... _pyi_private_set_type: Union[str, Combinable]
def __get__(self, instance, owner) -> str: ... _pyi_private_get_type: str
class BooleanField(Field): class BooleanField(Field[_ST, _GT]):
def __set__(self, instance, value: Union[bool, Combinable]) -> None: ... _pyi_private_set_type: Union[bool, Combinable]
def __get__(self, instance, owner) -> bool: ... _pyi_private_get_type: bool
class NullBooleanField(Field): class NullBooleanField(Field[_ST, _GT]):
def __set__(self, instance, value: Optional[Union[bool, Combinable]]) -> None: ... _pyi_private_set_type: Optional[Union[bool, Combinable]]
def __get__(self, instance, owner) -> Optional[bool]: ... _pyi_private_get_type: Optional[bool]
class IPAddressField(Field): class IPAddressField(Field[_ST, _GT]):
def __get__(self, instance, owner) -> str: ... _pyi_private_set_type: Union[str, Combinable]
_pyi_private_get_type: str
class GenericIPAddressField(Field[_ST, _GT]):
_pyi_private_set_type: Union[str, int, Callable[..., Any], Combinable]
_pyi_private_get_type: str
class GenericIPAddressField(Field):
default_error_messages: Any = ... default_error_messages: Any = ...
unpack_ipv4: Any = ... unpack_ipv4: Any = ...
protocol: Any = ... protocol: Any = ...
@@ -207,12 +219,12 @@ class GenericIPAddressField(Field):
validators: Iterable[_ValidatorCallable] = ..., validators: Iterable[_ValidatorCallable] = ...,
error_messages: Optional[_ErrorMessagesToOverride] = ..., error_messages: Optional[_ErrorMessagesToOverride] = ...,
) -> None: ... ) -> None: ...
def __set__(self, instance, value: Union[str, int, Callable[..., Any], Combinable]): ...
def __get__(self, instance, owner) -> str: ...
class DateTimeCheckMixin: ... class DateTimeCheckMixin: ...
class DateField(DateTimeCheckMixin, Field): class DateField(DateTimeCheckMixin, Field[_ST, _GT]):
_pyi_private_set_type: Union[str, date, Combinable]
_pyi_private_get_type: date
def __init__( def __init__(
self, self,
verbose_name: Optional[Union[str, bytes]] = ..., verbose_name: Optional[Union[str, bytes]] = ...,
@@ -236,10 +248,10 @@ class DateField(DateTimeCheckMixin, Field):
validators: Iterable[_ValidatorCallable] = ..., validators: Iterable[_ValidatorCallable] = ...,
error_messages: Optional[_ErrorMessagesToOverride] = ..., error_messages: Optional[_ErrorMessagesToOverride] = ...,
): ... ): ...
def __set__(self, instance, value: Union[str, date, Combinable]) -> None: ...
def __get__(self, instance, owner) -> date: ...
class TimeField(DateTimeCheckMixin, Field): class TimeField(DateTimeCheckMixin, Field[_ST, _GT]):
_pyi_private_set_type: Union[str, time, datetime, Combinable]
_pyi_private_get_type: time
def __init__( def __init__(
self, self,
verbose_name: Optional[Union[str, bytes]] = ..., verbose_name: Optional[Union[str, bytes]] = ...,
@@ -262,18 +274,15 @@ class TimeField(DateTimeCheckMixin, Field):
validators: Iterable[_ValidatorCallable] = ..., validators: Iterable[_ValidatorCallable] = ...,
error_messages: Optional[_ErrorMessagesToOverride] = ..., error_messages: Optional[_ErrorMessagesToOverride] = ...,
): ... ): ...
def __set__(self, instance, value: Union[str, time, datetime, Combinable]) -> None: ...
def __get__(self, instance, owner) -> time: ...
class DateTimeField(DateField): class DateTimeField(DateField[_ST, _GT]):
def __set__(self, instance, value: Union[str, date, datetime, Combinable]) -> None: ... _pyi_private_get_type: datetime
def __get__(self, instance, owner) -> datetime: ...
class UUIDField(Field): class UUIDField(Field[_ST, _GT]):
def __set__(self, instance, value: Union[str, uuid.UUID]) -> None: ... _pyi_private_set_type: Union[str, uuid.UUID]
def __get__(self, instance, owner) -> uuid.UUID: ... _pyi_private_get_type: uuid.UUID
class FilePathField(Field): class FilePathField(Field[_ST, _GT]):
path: str = ... path: str = ...
match: Optional[Any] = ... match: Optional[Any] = ...
recursive: bool = ... recursive: bool = ...
@@ -306,10 +315,10 @@ class FilePathField(Field):
error_messages: Optional[_ErrorMessagesToOverride] = ..., error_messages: Optional[_ErrorMessagesToOverride] = ...,
): ... ): ...
class BinaryField(Field): ... class BinaryField(Field[_ST, _GT]): ...
class DurationField(Field): class DurationField(Field[_ST, _GT]):
def __get__(self, instance, owner) -> timedelta: ... _pyi_private_get_type: timedelta
class BigAutoField(AutoField): ... class BigAutoField(AutoField[_ST, _GT]): ...
class CommaSeparatedIntegerField(CharField): ... class CommaSeparatedIntegerField(CharField[_ST, _GT]): ...

View File

@@ -49,7 +49,12 @@ _ErrorMessagesToOverride = Dict[str, Any]
RECURSIVE_RELATIONSHIP_CONSTANT: str = ... RECURSIVE_RELATIONSHIP_CONSTANT: str = ...
class RelatedField(FieldCacheMixin, Field): # __set__ value type
_ST = TypeVar("_ST")
# __get__ return type
_GT = TypeVar("_GT")
class RelatedField(FieldCacheMixin, Field[_ST, _GT]):
one_to_many: bool = ... one_to_many: bool = ...
one_to_one: bool = ... one_to_one: bool = ...
many_to_many: bool = ... many_to_many: bool = ...
@@ -83,6 +88,7 @@ class ForeignObject(RelatedField):
related_query_name: None = ..., related_query_name: None = ...,
limit_choices_to: Optional[Union[Dict[str, Any], Callable[[], Any]]] = ..., limit_choices_to: Optional[Union[Dict[str, Any], Callable[[], Any]]] = ...,
parent_link: bool = ..., parent_link: bool = ...,
db_constraint: bool = ...,
swappable: bool = ..., swappable: bool = ...,
verbose_name: Optional[str] = ..., verbose_name: Optional[str] = ...,
name: Optional[str] = ..., name: Optional[str] = ...,
@@ -103,17 +109,82 @@ class ForeignObject(RelatedField):
error_messages: Optional[_ErrorMessagesToOverride] = ..., error_messages: Optional[_ErrorMessagesToOverride] = ...,
): ... ): ...
class ForeignKey(RelatedField, Generic[_T]): class ForeignKey(RelatedField[_ST, _GT]):
def __init__(self, to: Union[Type[_T], str], on_delete: Any, related_name: str = ..., **kwargs): ... _pyi_private_set_type: Union[Any, Combinable]
def __set__(self, instance, value: Union[Model, Combinable]) -> None: ... _pyi_private_get_type: Any
def __get__(self, instance, owner) -> _T: ... def __init__(
self,
to: Union[Type[Model], str],
on_delete: Callable[..., None],
to_field: Optional[str] = ...,
related_name: str = ...,
related_query_name: Optional[str] = ...,
limit_choices_to: Optional[Union[Dict[str, Any], Callable[[], Any], Q]] = ...,
parent_link: bool = ...,
db_constraint: bool = ...,
verbose_name: Optional[Union[str, bytes]] = ...,
name: Optional[str] = ...,
primary_key: bool = ...,
max_length: Optional[int] = ...,
unique: bool = ...,
blank: bool = ...,
null: bool = ...,
db_index: bool = ...,
default: Any = ...,
editable: bool = ...,
auto_created: bool = ...,
serialize: bool = ...,
unique_for_date: Optional[str] = ...,
unique_for_month: Optional[str] = ...,
unique_for_year: Optional[str] = ...,
choices: Optional[_FieldChoices] = ...,
help_text: str = ...,
db_column: Optional[str] = ...,
db_tablespace: Optional[str] = ...,
validators: Iterable[_ValidatorCallable] = ...,
error_messages: Optional[_ErrorMessagesToOverride] = ...,
): ...
class OneToOneField(RelatedField, Generic[_T]): class OneToOneField(RelatedField[_ST, _GT]):
def __init__(self, to: Union[Type[_T], str], on_delete: Any, related_name: str = ..., **kwargs): ... _pyi_private_set_type: Union[Any, Combinable]
def __set__(self, instance, value: Union[Model, Combinable]) -> None: ... _pyi_private_get_type: Any
def __get__(self, instance, owner) -> _T: ... def __init__(
self,
to: Union[Type[Model], str],
on_delete: Any,
to_field: Optional[str] = ...,
related_name: str = ...,
related_query_name: Optional[str] = ...,
limit_choices_to: Optional[Union[Dict[str, Any], Callable[[], Any], Q]] = ...,
parent_link: bool = ...,
db_constraint: bool = ...,
verbose_name: Optional[Union[str, bytes]] = ...,
name: Optional[str] = ...,
primary_key: bool = ...,
max_length: Optional[int] = ...,
unique: bool = ...,
blank: bool = ...,
null: bool = ...,
db_index: bool = ...,
default: Any = ...,
editable: bool = ...,
auto_created: bool = ...,
serialize: bool = ...,
unique_for_date: Optional[str] = ...,
unique_for_month: Optional[str] = ...,
unique_for_year: Optional[str] = ...,
choices: Optional[_FieldChoices] = ...,
help_text: str = ...,
db_column: Optional[str] = ...,
db_tablespace: Optional[str] = ...,
validators: Iterable[_ValidatorCallable] = ...,
error_messages: Optional[_ErrorMessagesToOverride] = ...,
): ...
class ManyToManyField(RelatedField[_ST, _GT]):
_pyi_private_set_type: Sequence[Any]
_pyi_private_get_type: RelatedManager[Any]
class ManyToManyField(RelatedField, Generic[_T]):
many_to_many: bool = ... many_to_many: bool = ...
many_to_one: bool = ... many_to_one: bool = ...
one_to_many: bool = ... one_to_many: bool = ...
@@ -127,17 +198,35 @@ class ManyToManyField(RelatedField, Generic[_T]):
to: Union[Type[_T], str], to: Union[Type[_T], str],
related_name: Optional[str] = ..., related_name: Optional[str] = ...,
related_query_name: Optional[str] = ..., related_query_name: Optional[str] = ...,
limit_choices_to: Optional[Union[Dict[str, Any], Callable[[], Any]]] = ..., limit_choices_to: Optional[Union[Dict[str, Any], Callable[[], Any], Q]] = ...,
symmetrical: Optional[bool] = ..., symmetrical: Optional[bool] = ...,
through: Optional[Union[str, Type[Model]]] = ..., through: Optional[Union[str, Type[Model]]] = ...,
through_fields: Optional[Tuple[str, str]] = ..., through_fields: Optional[Tuple[str, str]] = ...,
db_constraint: bool = ..., db_constraint: bool = ...,
db_table: Optional[str] = ..., db_table: Optional[str] = ...,
swappable: bool = ..., swappable: bool = ...,
**kwargs: Any verbose_name: Optional[Union[str, bytes]] = ...,
name: Optional[str] = ...,
primary_key: bool = ...,
max_length: Optional[int] = ...,
unique: bool = ...,
blank: bool = ...,
null: bool = ...,
db_index: bool = ...,
default: Any = ...,
editable: bool = ...,
auto_created: bool = ...,
serialize: bool = ...,
unique_for_date: Optional[str] = ...,
unique_for_month: Optional[str] = ...,
unique_for_year: Optional[str] = ...,
choices: Optional[_FieldChoices] = ...,
help_text: str = ...,
db_column: Optional[str] = ...,
db_tablespace: Optional[str] = ...,
validators: Iterable[_ValidatorCallable] = ...,
error_messages: Optional[_ErrorMessagesToOverride] = ...,
) -> None: ... ) -> None: ...
def __set__(self, instance, value: Sequence[_T]) -> None: ...
def __get__(self, instance, owner) -> RelatedManager[_T]: ...
def check(self, **kwargs: Any) -> List[Any]: ... def check(self, **kwargs: Any) -> List[Any]: ...
def deconstruct(self) -> Tuple[Optional[str], str, List[Any], Dict[str, str]]: ... def deconstruct(self) -> Tuple[Optional[str], str, List[Any], Dict[str, str]]: ...
def get_path_info(self, filtered_relation: None = ...) -> List[PathInfo]: ... def get_path_info(self, filtered_relation: None = ...) -> List[PathInfo]: ...

View File

@@ -4,7 +4,6 @@ from django.db.models.base import Model
from django.db.models.query import QuerySet from django.db.models.query import QuerySet
_T = TypeVar("_T", bound=Model, covariant=True) _T = TypeVar("_T", bound=Model, covariant=True)
_Self = TypeVar("_Self", bound="BaseManager")
class BaseManager(QuerySet[_T]): class BaseManager(QuerySet[_T]):
creation_counter: int = ... creation_counter: int = ...
@@ -17,9 +16,7 @@ class BaseManager(QuerySet[_T]):
def deconstruct(self) -> Tuple[bool, str, None, Tuple, Dict[str, int]]: ... def deconstruct(self) -> Tuple[bool, str, None, Tuple, Dict[str, int]]: ...
def check(self, **kwargs: Any) -> List[Any]: ... def check(self, **kwargs: Any) -> List[Any]: ...
@classmethod @classmethod
def from_queryset( def from_queryset(cls, queryset_class: Type[QuerySet], class_name: Optional[str] = ...) -> Any: ...
cls: Type[_Self], queryset_class: Type[QuerySet], class_name: Optional[str] = ...
) -> Type[_Self]: ...
@classmethod @classmethod
def _get_queryset_methods(cls, queryset_class: type) -> Dict[str, Any]: ... def _get_queryset_methods(cls, queryset_class: type) -> Dict[str, Any]: ...
def contribute_to_class(self, model: Type[Model], name: str) -> None: ... def contribute_to_class(self, model: Type[Model], name: str) -> None: ...

View File

@@ -1,5 +1,4 @@
from contextlib import ContextDecorator from typing import Any, Callable, Optional, overload, TypeVar
from typing import Any, Callable, Optional, Union, Iterator, overload, ContextManager
from django.db import ProgrammingError from django.db import ProgrammingError
@@ -18,19 +17,23 @@ def get_rollback(using: None = ...) -> bool: ...
def set_rollback(rollback: bool, using: Optional[str] = ...) -> None: ... def set_rollback(rollback: bool, using: Optional[str] = ...) -> None: ...
def on_commit(func: Callable, using: None = ...) -> None: ... def on_commit(func: Callable, using: None = ...) -> None: ...
class Atomic(ContextDecorator): _C = TypeVar("_C", bound=Callable) # Any callable
# Don't inherit from ContextDecorator, so we can provide a more specific signature for __call__
class Atomic:
using: Optional[str] = ... using: Optional[str] = ...
savepoint: bool = ... savepoint: bool = ...
def __init__(self, using: Optional[str], savepoint: bool) -> None: ... def __init__(self, using: Optional[str], savepoint: bool) -> None: ...
# When decorating, return the decorated function as-is, rather than clobbering it as ContextDecorator does.
def __call__(self, func: _C) -> _C: ...
def __enter__(self) -> None: ... def __enter__(self) -> None: ...
def __exit__(self, exc_type: None, exc_value: None, traceback: None) -> None: ... def __exit__(self, exc_type: None, exc_value: None, traceback: None) -> None: ...
# Bare decorator
@overload @overload
def atomic() -> Atomic: ... def atomic(using: _C) -> _C: ...
# Decorator or context-manager with parameters
@overload @overload
def atomic(using: Optional[str] = ...,) -> ContextManager[Atomic]: ... def atomic(using: Optional[str] = None, savepoint: bool = True) -> Atomic: ...
@overload
def atomic(using: Callable = ...) -> Callable: ...
@overload
def atomic(using: Optional[str] = ..., savepoint: bool = ...) -> ContextManager[Atomic]: ...
def non_atomic_requests(using: Callable = ...) -> Callable: ... def non_atomic_requests(using: Callable = ...) -> Callable: ...

View File

@@ -1,12 +1,8 @@
import decimal from datetime import datetime, timedelta
from datetime import date, datetime, time, timedelta
from decimal import Decimal from decimal import Decimal
from typing import Any, Callable, Dict, List, Optional, Tuple, Union, Type from typing import Any, Callable, List, Optional, Pattern, Sequence, Type, Union
from uuid import UUID
from django.core.files.base import File
from django.core.validators import BaseValidator from django.core.validators import BaseValidator
from django.db.models.fields.files import FieldFile
from django.forms.boundfield import BoundField from django.forms.boundfield import BoundField
from django.forms.forms import BaseForm from django.forms.forms import BaseForm
from django.forms.widgets import Widget from django.forms.widgets import Widget
@@ -31,16 +27,16 @@ class Field:
self, self,
*, *,
required: bool = ..., required: bool = ...,
widget: Optional[Any] = ..., widget: Optional[Union[Widget, Type[Widget]]] = ...,
label: Optional[Any] = ..., label: Optional[Any] = ...,
initial: Optional[Any] = ..., initial: Optional[Any] = ...,
help_text: str = ..., help_text: str = ...,
error_messages: Optional[Any] = ..., error_messages: Optional[Any] = ...,
show_hidden_initial: bool = ..., show_hidden_initial: bool = ...,
validators: Any = ..., validators: Sequence[Any] = ...,
localize: bool = ..., localize: bool = ...,
disabled: bool = ..., disabled: bool = ...,
label_suffix: Optional[Any] = ... label_suffix: Optional[Any] = ...,
) -> None: ... ) -> None: ...
def prepare_value(self, value: Any) -> Any: ... def prepare_value(self, value: Any) -> Any: ...
def to_python(self, value: Optional[Any]) -> Optional[Any]: ... def to_python(self, value: Optional[Any]) -> Optional[Any]: ...
@@ -49,60 +45,60 @@ class Field:
def clean(self, value: Any) -> Any: ... def clean(self, value: Any) -> Any: ...
def bound_data(self, data: Any, initial: Any) -> Any: ... def bound_data(self, data: Any, initial: Any) -> Any: ...
def widget_attrs(self, widget: Widget) -> Any: ... def widget_attrs(self, widget: Widget) -> Any: ...
def has_changed(self, initial: Any, data: Optional[str]) -> bool: ... def has_changed(self, initial: Any, data: Any) -> bool: ...
def get_bound_field(self, form: BaseForm, field_name: str) -> BoundField: ... def get_bound_field(self, form: BaseForm, field_name: str) -> BoundField: ...
def __deepcopy__(self, memo: Dict[Any, Any]) -> Field: ...
class CharField(Field): class CharField(Field):
disabled: bool
error_messages: Dict[str, str]
required: bool
show_hidden_initial: bool
max_length: Optional[Union[int, str]] = ... max_length: Optional[Union[int, str]] = ...
min_length: Optional[Union[int, str]] = ... min_length: Optional[Union[int, str]] = ...
strip: bool = ... strip: bool = ...
empty_value: Optional[str] = ... empty_value: Optional[str] = ...
def __init__( def __init__(
self, self,
*,
max_length: Optional[Any] = ..., max_length: Optional[Any] = ...,
min_length: Optional[Any] = ..., min_length: Optional[Any] = ...,
strip: bool = ..., strip: bool = ...,
empty_value: str = ..., empty_value: Optional[str] = ...,
**kwargs: Any required: bool = ...,
widget: Optional[Union[Widget, Type[Widget]]] = ...,
label: Optional[Any] = ...,
initial: Optional[Any] = ...,
help_text: str = ...,
error_messages: Optional[Any] = ...,
show_hidden_initial: bool = ...,
validators: Sequence[Any] = ...,
localize: bool = ...,
disabled: bool = ...,
label_suffix: Optional[Any] = ...,
) -> None: ... ) -> None: ...
class IntegerField(Field): class IntegerField(Field):
disabled: bool
error_messages: Dict[str, str]
max_value: Optional[Any] max_value: Optional[Any]
min_value: Optional[Any] min_value: Optional[Any]
required: bool
show_hidden_initial: bool
default_error_messages: Any = ...
re_decimal: Any = ... re_decimal: Any = ...
def __init__(self, *, max_value: Optional[Any] = ..., min_value: Optional[Any] = ..., **kwargs: Any) -> None: ... def __init__(
self,
max_value: Optional[Any] = ...,
min_value: Optional[Any] = ...,
required: bool = ...,
widget: Optional[Union[Widget, Type[Widget]]] = ...,
label: Optional[Any] = ...,
initial: Optional[Any] = ...,
help_text: str = ...,
error_messages: Optional[Any] = ...,
show_hidden_initial: bool = ...,
validators: Sequence[Any] = ...,
localize: bool = ...,
disabled: bool = ...,
label_suffix: Optional[Any] = ...,
) -> None: ...
class FloatField(IntegerField): class FloatField(IntegerField):
disabled: bool
error_messages: Dict[str, str]
max_value: Optional[float]
min_value: Optional[float]
required: bool
show_hidden_initial: bool
default_error_messages: Any = ...
def validate(self, value: Optional[float]) -> None: ... def validate(self, value: Optional[float]) -> None: ...
class DecimalField(IntegerField): class DecimalField(IntegerField):
decimal_places: Optional[int] decimal_places: Optional[int]
disabled: bool
error_messages: Dict[str, str]
max_digits: Optional[int] max_digits: Optional[int]
max_value: Optional[Union[decimal.Decimal, int]]
min_value: Optional[Union[decimal.Decimal, int]]
required: bool
show_hidden_initial: bool
default_error_messages: Any = ...
def __init__( def __init__(
self, self,
*, *,
@@ -110,118 +106,95 @@ class DecimalField(IntegerField):
min_value: Optional[Any] = ..., min_value: Optional[Any] = ...,
max_digits: Optional[Any] = ..., max_digits: Optional[Any] = ...,
decimal_places: Optional[Any] = ..., decimal_places: Optional[Any] = ...,
**kwargs: Any required: bool = ...,
widget: Optional[Union[Widget, Type[Widget]]] = ...,
label: Optional[Any] = ...,
initial: Optional[Any] = ...,
help_text: str = ...,
error_messages: Optional[Any] = ...,
show_hidden_initial: bool = ...,
validators: Sequence[Any] = ...,
localize: bool = ...,
disabled: bool = ...,
label_suffix: Optional[Any] = ...,
) -> None: ... ) -> None: ...
def validate(self, value: Optional[Decimal]) -> None: ... def validate(self, value: Optional[Decimal]) -> None: ...
class BaseTemporalField(Field): class BaseTemporalField(Field):
input_formats: Any = ... input_formats: Any = ...
def __init__(self, *, input_formats: Optional[Any] = ..., **kwargs: Any) -> None: ... def __init__(
def strptime(self, value: Any, format: Any) -> Any: ... self,
input_formats: Optional[Any] = ...,
required: bool = ...,
widget: Optional[Union[Widget, Type[Widget]]] = ...,
label: Optional[Any] = ...,
initial: Optional[Any] = ...,
help_text: str = ...,
error_messages: Optional[Any] = ...,
show_hidden_initial: bool = ...,
validators: Sequence[Any] = ...,
localize: bool = ...,
disabled: bool = ...,
label_suffix: Optional[Any] = ...,
) -> None: ...
def strptime(self, value: Any, format: str) -> Any: ...
class DateField(BaseTemporalField): class DateField(BaseTemporalField): ...
disabled: bool class TimeField(BaseTemporalField): ...
error_messages: Dict[str, str] class DateTimeField(BaseTemporalField): ...
required: bool
show_hidden_initial: bool
input_formats: Any = ...
default_error_messages: Any = ...
def strptime(self, value: str, format: str) -> date: ...
class TimeField(BaseTemporalField):
disabled: bool
error_messages: Dict[str, str]
required: bool
show_hidden_initial: bool
input_formats: Any = ...
default_error_messages: Any = ...
def strptime(self, value: str, format: str) -> time: ...
class DateTimeField(BaseTemporalField):
disabled: bool
error_messages: Dict[str, str]
required: bool
show_hidden_initial: bool
input_formats: Any = ...
default_error_messages: Any = ...
def prepare_value(self, value: Optional[datetime]) -> Optional[datetime]: ...
def strptime(self, value: str, format: str) -> datetime: ...
class DurationField(Field): class DurationField(Field):
disabled: bool
required: bool
show_hidden_initial: bool
default_error_messages: Any = ...
def prepare_value(self, value: Optional[Union[timedelta, str]]) -> Optional[str]: ... def prepare_value(self, value: Optional[Union[timedelta, str]]) -> Optional[str]: ...
class RegexField(CharField): class RegexField(CharField):
disabled: bool regex: str = ...
empty_value: str def __init__(
error_messages: Dict[str, str] self,
max_length: Optional[int] regex: Union[str, Pattern],
min_length: Optional[int] max_length: Optional[Any] = ...,
required: bool min_length: Optional[Any] = ...,
show_hidden_initial: bool strip: bool = ...,
strip: bool empty_value: Optional[str] = ...,
def __init__(self, regex: str, **kwargs: Any) -> None: ... required: bool = ...,
regex: Any = ... widget: Optional[Union[Widget, Type[Widget]]] = ...,
label: Optional[Any] = ...,
initial: Optional[Any] = ...,
help_text: str = ...,
error_messages: Optional[Any] = ...,
show_hidden_initial: bool = ...,
validators: Sequence[Any] = ...,
localize: bool = ...,
disabled: bool = ...,
label_suffix: Optional[Any] = ...,
) -> None: ...
class EmailField(CharField): class EmailField(CharField): ...
disabled: bool
empty_value: Optional[str]
error_messages: Dict[str, str]
max_length: Optional[int]
min_length: Optional[int]
required: bool
show_hidden_initial: bool
strip: bool
default_validators: Any = ...
def __init__(self, **kwargs: Any) -> None: ...
class FileField(Field): class FileField(Field):
disabled: bool
required: bool
show_hidden_initial: bool
default_error_messages: Any = ...
max_length: Optional[int] = ... max_length: Optional[int] = ...
allow_empty_file: bool = ... allow_empty_file: bool = ...
def __init__(self, *, max_length: Optional[Any] = ..., allow_empty_file: bool = ..., **kwargs: Any) -> None: ... def __init__(
def bound_data(self, data: Any, initial: Optional[FieldFile]) -> Optional[Union[File, str]]: ... self,
max_length: Optional[Any] = ...,
allow_empty_file: bool = ...,
required: bool = ...,
widget: Optional[Union[Widget, Type[Widget]]] = ...,
label: Optional[Any] = ...,
initial: Optional[Any] = ...,
help_text: str = ...,
error_messages: Optional[Any] = ...,
show_hidden_initial: bool = ...,
validators: Sequence[Any] = ...,
localize: bool = ...,
disabled: bool = ...,
label_suffix: Optional[Any] = ...,
) -> None: ...
def clean(self, data: Any, initial: Optional[Any] = ...): ...
class ImageField(FileField): class ImageField(FileField): ...
allow_empty_file: bool class URLField(CharField): ...
disabled: bool class BooleanField(Field): ...
max_length: Optional[int] class NullBooleanField(BooleanField): ...
required: bool
show_hidden_initial: bool
default_validators: Any = ...
default_error_messages: Any = ...
class URLField(CharField):
disabled: bool
empty_value: Optional[str]
error_messages: Dict[str, str]
max_length: Optional[int]
min_length: Optional[int]
required: bool
show_hidden_initial: bool
strip: bool
default_error_messages: Any = ...
default_validators: Any = ...
def __init__(self, **kwargs: Any) -> None: ...
class BooleanField(Field):
disabled: bool
error_messages: Dict[str, str]
required: bool
show_hidden_initial: bool
def validate(self, value: bool) -> None: ...
class NullBooleanField(BooleanField):
disabled: bool
required: bool
show_hidden_initial: bool
def validate(self, value: Optional[bool]) -> None: ...
class CallableChoiceIterator: class CallableChoiceIterator:
choices_func: Callable = ... choices_func: Callable = ...
@@ -229,125 +202,191 @@ class CallableChoiceIterator:
def __iter__(self) -> None: ... def __iter__(self) -> None: ...
class ChoiceField(Field): class ChoiceField(Field):
disabled: bool
error_messages: Dict[str, str]
required: bool
show_hidden_initial: bool
default_error_messages: Any = ...
choices: Any = ... choices: Any = ...
def __init__(self, *, choices: Any = ..., **kwargs: Any) -> None: ... def __init__(
def validate(self, value: Any) -> None: ... self,
choices: Any = ...,
required: bool = ...,
widget: Optional[Union[Widget, Type[Widget]]] = ...,
label: Optional[Any] = ...,
initial: Optional[Any] = ...,
help_text: str = ...,
error_messages: Optional[Any] = ...,
show_hidden_initial: bool = ...,
validators: Sequence[Any] = ...,
localize: bool = ...,
disabled: bool = ...,
label_suffix: Optional[Any] = ...,
) -> None: ...
def valid_value(self, value: str) -> bool: ... def valid_value(self, value: str) -> bool: ...
class TypedChoiceField(ChoiceField): class TypedChoiceField(ChoiceField):
disabled: bool coerce: Union[Callable, Type[Any]] = ...
required: bool
show_hidden_initial: bool
coerce: Union[Callable, Type[Union[bool, float, str]]] = ...
empty_value: Optional[str] = ... empty_value: Optional[str] = ...
def __init__(self, *, coerce: Any = ..., empty_value: str = ..., **kwargs: Any) -> None: ... def __init__(
self,
coerce: Any = ...,
empty_value: Optional[str] = ...,
choices: Any = ...,
required: bool = ...,
widget: Optional[Union[Widget, Type[Widget]]] = ...,
label: Optional[Any] = ...,
initial: Optional[Any] = ...,
help_text: str = ...,
error_messages: Optional[Any] = ...,
show_hidden_initial: bool = ...,
validators: Sequence[Any] = ...,
localize: bool = ...,
disabled: bool = ...,
label_suffix: Optional[Any] = ...,
) -> None: ...
class MultipleChoiceField(ChoiceField): class MultipleChoiceField(ChoiceField): ...
disabled: bool
error_messages: Dict[str, str]
required: bool
show_hidden_initial: bool
hidden_widget: Any = ...
default_error_messages: Any = ...
def validate(self, value: List[str]) -> None: ...
class TypedMultipleChoiceField(MultipleChoiceField): class TypedMultipleChoiceField(MultipleChoiceField):
disabled: bool
required: bool
show_hidden_initial: bool
coerce: Union[Callable, Type[float]] = ... coerce: Union[Callable, Type[float]] = ...
empty_value: Optional[List[Any]] = ... empty_value: Optional[List[Any]] = ...
def __init__(self, *, coerce: Any = ..., **kwargs: Any) -> None: ... def __init__(
def validate(self, value: List[str]) -> None: ... self,
coerce: Any = ...,
empty_value: Optional[str] = ...,
choices: Any = ...,
required: bool = ...,
widget: Optional[Union[Widget, Type[Widget]]] = ...,
label: Optional[Any] = ...,
initial: Optional[Any] = ...,
help_text: str = ...,
error_messages: Optional[Any] = ...,
show_hidden_initial: bool = ...,
validators: Sequence[Any] = ...,
localize: bool = ...,
disabled: bool = ...,
label_suffix: Optional[Any] = ...,
) -> None: ...
class ComboField(Field): class ComboField(Field):
disabled: bool
required: bool
show_hidden_initial: bool
fields: Any = ... fields: Any = ...
def __init__(self, fields: List[CharField], **kwargs: Any) -> None: ... def __init__(
self,
fields: Sequence[Field],
required: bool = ...,
widget: Optional[Union[Widget, Type[Widget]]] = ...,
label: Optional[Any] = ...,
initial: Optional[Any] = ...,
help_text: str = ...,
error_messages: Optional[Any] = ...,
show_hidden_initial: bool = ...,
validators: Sequence[Any] = ...,
localize: bool = ...,
disabled: bool = ...,
label_suffix: Optional[Any] = ...,
) -> None: ...
class MultiValueField(Field): class MultiValueField(Field):
disabled: bool
required: bool
show_hidden_initial: bool
default_error_messages: Any = ...
require_all_fields: bool = ... require_all_fields: bool = ...
fields: Any = ... fields: Any = ...
def __init__(self, fields: Tuple[Field, Field], *, require_all_fields: bool = ..., **kwargs: Any) -> None: ... def __init__(
def validate(self, value: Union[datetime, str]) -> None: ... self,
fields: Sequence[Field],
require_all_fields: bool = ...,
required: bool = ...,
widget: Optional[Union[Widget, Type[Widget]]] = ...,
label: Optional[Any] = ...,
initial: Optional[Any] = ...,
help_text: str = ...,
error_messages: Optional[Any] = ...,
show_hidden_initial: bool = ...,
validators: Sequence[Any] = ...,
localize: bool = ...,
disabled: bool = ...,
label_suffix: Optional[Any] = ...,
) -> None: ...
def compress(self, data_list: Any) -> Any: ... def compress(self, data_list: Any) -> Any: ...
class FilePathField(ChoiceField): class FilePathField(ChoiceField):
allow_files: bool allow_files: bool
allow_folders: bool allow_folders: bool
disabled: bool
match: Optional[str] match: Optional[str]
path: str path: str
recursive: bool recursive: bool
required: bool
show_hidden_initial: bool
choices: Any = ...
match_re: Any = ... match_re: Any = ...
def __init__( def __init__(
self, self,
path: str, path: str,
*,
match: Optional[Any] = ..., match: Optional[Any] = ...,
recursive: bool = ..., recursive: bool = ...,
allow_files: bool = ..., allow_files: bool = ...,
allow_folders: bool = ..., allow_folders: bool = ...,
**kwargs: Any choices: Any = ...,
required: bool = ...,
widget: Optional[Union[Widget, Type[Widget]]] = ...,
label: Optional[Any] = ...,
initial: Optional[Any] = ...,
help_text: str = ...,
error_messages: Optional[Any] = ...,
show_hidden_initial: bool = ...,
validators: Sequence[Any] = ...,
localize: bool = ...,
disabled: bool = ...,
label_suffix: Optional[Any] = ...,
) -> None: ... ) -> None: ...
class SplitDateTimeField(MultiValueField): class SplitDateTimeField(MultiValueField):
disabled: bool
require_all_fields: bool
required: bool
show_hidden_initial: bool
hidden_widget: Any = ...
default_error_messages: Any = ...
def __init__( def __init__(
self, *, input_date_formats: Optional[Any] = ..., input_time_formats: Optional[Any] = ..., **kwargs: Any self,
input_date_formats: Optional[Any] = ...,
input_time_formats: Optional[Any] = ...,
fields: Sequence[Field] = ...,
require_all_fields: bool = ...,
required: bool = ...,
widget: Optional[Union[Widget, Type[Widget]]] = ...,
label: Optional[Any] = ...,
initial: Optional[Any] = ...,
help_text: str = ...,
error_messages: Optional[Any] = ...,
show_hidden_initial: bool = ...,
validators: Sequence[Any] = ...,
localize: bool = ...,
disabled: bool = ...,
label_suffix: Optional[Any] = ...,
) -> None: ... ) -> None: ...
def compress(self, data_list: List[Optional[datetime]]) -> Optional[datetime]: ... def compress(self, data_list: List[Optional[datetime]]) -> Optional[datetime]: ...
class GenericIPAddressField(CharField): class GenericIPAddressField(CharField):
disabled: bool
empty_value: str
error_messages: Dict[str, str]
max_length: None
min_length: None
required: bool
show_hidden_initial: bool
strip: bool
unpack_ipv4: bool = ... unpack_ipv4: bool = ...
default_validators: List[Callable] = ... def __init__(
def __init__(self, *, protocol: str = ..., unpack_ipv4: bool = ..., **kwargs: Any) -> None: ... self,
protocol: str = ...,
unpack_ipv4: bool = ...,
required: bool = ...,
widget: Optional[Union[Widget, Type[Widget]]] = ...,
label: Optional[Any] = ...,
initial: Optional[Any] = ...,
help_text: str = ...,
error_messages: Optional[Any] = ...,
show_hidden_initial: bool = ...,
validators: Sequence[Any] = ...,
localize: bool = ...,
disabled: bool = ...,
label_suffix: Optional[Any] = ...,
) -> None: ...
class SlugField(CharField): class SlugField(CharField):
disabled: bool
empty_value: str
max_length: Optional[int]
min_length: None
required: bool
show_hidden_initial: bool
strip: bool
allow_unicode: bool = ... allow_unicode: bool = ...
def __init__(self, *, allow_unicode: bool = ..., **kwargs: Any) -> None: ... def __init__(
self,
allow_unicode: bool = ...,
required: bool = ...,
widget: Optional[Union[Widget, Type[Widget]]] = ...,
label: Optional[Any] = ...,
initial: Optional[Any] = ...,
help_text: str = ...,
error_messages: Optional[Any] = ...,
show_hidden_initial: bool = ...,
validators: Sequence[Any] = ...,
localize: bool = ...,
disabled: bool = ...,
label_suffix: Optional[Any] = ...,
) -> None: ...
class UUIDField(CharField): class UUIDField(CharField): ...
disabled: bool
empty_value: str
max_length: None
min_length: None
required: bool
show_hidden_initial: bool
strip: bool
default_error_messages: Any = ...
def prepare_value(self, value: UUID) -> str: ...

View File

@@ -1,48 +1,45 @@
from collections import OrderedDict
from datetime import datetime from datetime import datetime
from typing import Any, Dict, Iterator, List, Mapping, Optional, Tuple, Type, Union from typing import Any, Dict, Iterator, List, Mapping, Optional, Sequence, Type, Union
from django.core.exceptions import ValidationError as ValidationError from django.core.exceptions import ValidationError as ValidationError
from django.core.files.base import File
from django.core.files.uploadedfile import SimpleUploadedFile from django.core.files.uploadedfile import SimpleUploadedFile
from django.db.models.query import QuerySet from django.db.models.query import QuerySet
from django.forms.boundfield import BoundField from django.forms.boundfield import BoundField
from django.forms.fields import Field from django.forms.fields import Field
from django.forms.renderers import BaseRenderer
from django.forms.utils import ErrorDict, ErrorList from django.forms.utils import ErrorDict, ErrorList
from django.forms.widgets import Media, MediaDefiningClass from django.forms.widgets import Media, MediaDefiningClass
from django.utils.safestring import SafeText from django.utils.safestring import SafeText
class DeclarativeFieldsMetaclass(MediaDefiningClass): class DeclarativeFieldsMetaclass(MediaDefiningClass):
def __new__( def __new__(mcs, name: str, bases: Sequence[Type[BaseForm]], attrs: Dict[str, Any]) -> Type[BaseForm]: ...
mcs: Type[DeclarativeFieldsMetaclass], name: str, bases: Tuple[Type[BaseForm]], attrs: OrderedDict
) -> Type[BaseForm]: ...
class BaseForm: class BaseForm:
default_renderer: Any = ... default_renderer: Any = ...
field_order: Any = ... field_order: Any = ...
prefix: Any = ...
use_required_attribute: bool = ... use_required_attribute: bool = ...
is_bound: Any = ... is_bound: bool = ...
data: Any = ... data: Dict[str, Any] = ...
files: Any = ... files: Optional[Dict[str, Any]] = ...
auto_id: Any = ... auto_id: Any = ...
initial: Any = ... initial: Dict[str, Any] = ...
error_class: Any = ... error_class: Type[ErrorList] = ...
label_suffix: Any = ... prefix: str = ...
empty_permitted: Any = ... label_suffix: str = ...
fields: Any = ... empty_permitted: bool = ...
renderer: Any = ... fields: Dict[str, Any] = ...
renderer: BaseRenderer = ...
def __init__( def __init__(
self, self,
data: Optional[Mapping[str, Any]] = ..., data: Optional[Mapping[str, Any]] = ...,
files: Optional[Mapping[str, File]] = ..., files: Optional[Mapping[str, Any]] = ...,
auto_id: Optional[Union[bool, str]] = ..., auto_id: Optional[Union[bool, str]] = ...,
prefix: Optional[str] = ..., prefix: Optional[str] = ...,
initial: Optional[Mapping[str, Any]] = ..., initial: Optional[Mapping[str, Any]] = ...,
error_class: Type[ErrorList] = ..., error_class: Type[ErrorList] = ...,
label_suffix: None = ..., label_suffix: Optional[str] = ...,
empty_permitted: bool = ..., empty_permitted: bool = ...,
field_order: None = ..., field_order: Optional[Any] = ...,
use_required_attribute: Optional[bool] = ..., use_required_attribute: Optional[bool] = ...,
renderer: Any = ..., renderer: Any = ...,
) -> None: ... ) -> None: ...

View File

@@ -1,8 +1,4 @@
import collections from typing import Any, Dict, Mapping, Optional, Sequence, Sized
from typing import Any, List, Optional, Union, Dict, Type
from django.forms.renderers import BaseRenderer
from django.forms.utils import ErrorList
from django.forms import Form from django.forms import Form
@@ -17,21 +13,9 @@ DEFAULT_MIN_NUM: int = ...
DEFAULT_MAX_NUM: int = ... DEFAULT_MAX_NUM: int = ...
class ManagementForm(Form): class ManagementForm(Form):
auto_id: Union[bool, str]
cleaned_data: Dict[str, Optional[int]] cleaned_data: Dict[str, Optional[int]]
data: Dict[str, Union[List[int], int, str]]
empty_permitted: bool
error_class: Type[ErrorList]
fields: collections.OrderedDict
files: Dict[Any, Any]
initial: Dict[str, int]
is_bound: bool
label_suffix: str
prefix: str
renderer: BaseRenderer
def __init__(self, *args: Any, **kwargs: Any) -> None: ...
class BaseFormSet: class BaseFormSet(Sized, Mapping[str, Any]):
is_bound: Any = ... is_bound: Any = ...
prefix: Any = ... prefix: Any = ...
auto_id: Any = ... auto_id: Any = ...
@@ -57,6 +41,7 @@ class BaseFormSet:
def management_form(self): ... def management_form(self): ...
def total_form_count(self): ... def total_form_count(self): ...
def initial_form_count(self): ... def initial_form_count(self): ...
@property
def forms(self): ... def forms(self): ...
def get_form_kwargs(self, index: Any): ... def get_form_kwargs(self, index: Any): ...
@property @property
@@ -101,4 +86,4 @@ def formset_factory(
min_num: Optional[Any] = ..., min_num: Optional[Any] = ...,
validate_min: bool = ..., validate_min: bool = ...,
): ... ): ...
def all_valid(formsets: List[Any]) -> bool: ... def all_valid(formsets: Sequence[Any]) -> bool: ...

View File

@@ -1,10 +1,11 @@
from collections import OrderedDict from collections import OrderedDict
from datetime import date, datetime from datetime import date, datetime
from typing import Any, Callable, Dict, Iterator, List, Optional, Tuple, Type, Union, Sequence from typing import Any, Callable, Dict, Iterator, List, MutableMapping, Optional, Sequence, Tuple, Type, Union
from unittest.mock import MagicMock from unittest.mock import MagicMock
from uuid import UUID from uuid import UUID
from django.core.files.uploadedfile import SimpleUploadedFile from django.core.files.base import File
from django.db import models
from django.db.models import ForeignKey from django.db.models import ForeignKey
from django.db.models.base import Model from django.db.models.base import Model
from django.db.models.manager import Manager from django.db.models.manager import Manager
@@ -14,9 +15,8 @@ from django.forms.fields import CharField, ChoiceField, Field
from django.forms.forms import BaseForm, DeclarativeFieldsMetaclass from django.forms.forms import BaseForm, DeclarativeFieldsMetaclass
from django.forms.formsets import BaseFormSet from django.forms.formsets import BaseFormSet
from django.forms.utils import ErrorList from django.forms.utils import ErrorList
from django.forms.widgets import Input, Widget, Select from django.forms.widgets import Input, Widget
from django.http.request import QueryDict from typing_extensions import Literal
from django.utils.datastructures import MultiValueDict
ALL_FIELDS: str ALL_FIELDS: str
@@ -57,21 +57,19 @@ class ModelFormOptions:
def __init__(self, options: Optional[type] = ...) -> None: ... def __init__(self, options: Optional[type] = ...) -> None: ...
class ModelFormMetaclass(DeclarativeFieldsMetaclass): class ModelFormMetaclass(DeclarativeFieldsMetaclass):
def __new__( def __new__(mcs, name: str, bases: Sequence[Type[ModelForm]], attrs: Dict[str, Any]) -> Type[ModelForm]: ...
mcs: Type[ModelFormMetaclass], name: str, bases: Tuple[Type[ModelForm]], attrs: OrderedDict
) -> Type[ModelForm]: ...
class BaseModelForm(BaseForm): class BaseModelForm(BaseForm):
instance: Any = ... instance: Any = ...
def __init__( def __init__(
self, self,
data: Optional[Union[Dict[str, Any], QueryDict]] = ..., data: Optional[Dict[str, Any]] = ...,
files: Optional[Union[Dict[str, SimpleUploadedFile], MultiValueDict]] = ..., files: Optional[Dict[str, File]] = ...,
auto_id: Union[bool, str] = ..., auto_id: Union[bool, str] = ...,
prefix: None = ..., prefix: Optional[str] = ...,
initial: Optional[Union[Dict[str, List[int]], Dict[str, int]]] = ..., initial: Optional[Dict[str, Any]] = ...,
error_class: Type[ErrorList] = ..., error_class: Type[ErrorList] = ...,
label_suffix: None = ..., label_suffix: Optional[str] = ...,
empty_permitted: bool = ..., empty_permitted: bool = ...,
instance: Optional[Model] = ..., instance: Optional[Model] = ...,
use_required_attribute: None = ..., use_required_attribute: None = ...,
@@ -87,16 +85,16 @@ class ModelForm(BaseModelForm): ...
def modelform_factory( def modelform_factory(
model: Type[Model], model: Type[Model],
form: Type[ModelForm] = ..., form: Type[ModelForm] = ...,
fields: Optional[Union[List[str], str]] = ..., fields: Optional[Union[Sequence[str], Literal["__all__"]]] = ...,
exclude: None = ..., exclude: Optional[Sequence[str]] = ...,
formfield_callback: Optional[str] = ..., formfield_callback: Optional[Union[str, Callable[[models.Field], Field]]] = ...,
widgets: None = ..., widgets: Optional[MutableMapping[str, Widget]] = ...,
localized_fields: None = ..., localized_fields: Optional[Sequence[str]] = ...,
labels: None = ..., labels: Optional[MutableMapping[str, str]] = ...,
help_texts: None = ..., help_texts: Optional[MutableMapping[str, str]] = ...,
error_messages: None = ..., error_messages: Optional[MutableMapping[str, Dict[str, Any]]] = ...,
field_classes: None = ..., field_classes: Optional[MutableMapping[str, Type[Field]]] = ...,
) -> Any: ... ) -> Type[ModelForm]: ...
class BaseModelFormSet(BaseFormSet): class BaseModelFormSet(BaseFormSet):
model: Any = ... model: Any = ...
@@ -148,13 +146,13 @@ def modelformset_factory(
exclude: Optional[Sequence[str]] = ..., exclude: Optional[Sequence[str]] = ...,
widgets: Optional[Dict[str, Any]] = ..., widgets: Optional[Dict[str, Any]] = ...,
validate_max: bool = ..., validate_max: bool = ...,
localized_fields: None = ..., localized_fields: Optional[Sequence[str]] = ...,
labels: Optional[Dict[str, str]] = ..., labels: Optional[Dict[str, str]] = ...,
help_texts: Optional[Dict[str, str]] = ..., help_texts: Optional[Dict[str, str]] = ...,
error_messages: Optional[Dict[str, Dict[str, str]]] = ..., error_messages: Optional[Dict[str, Dict[str, str]]] = ...,
validate_min: bool = ..., validate_min: bool = ...,
field_classes: Optional[Dict[str, Any]] = ..., field_classes: Optional[Dict[str, Type[Field]]] = ...,
) -> Any: ... ) -> Type[BaseModelFormSet]: ...
class BaseInlineFormSet(BaseModelFormSet): class BaseInlineFormSet(BaseModelFormSet):
instance: Any = ... instance: Any = ...
@@ -192,14 +190,14 @@ def inlineformset_factory(
formfield_callback: Optional[Callable] = ..., formfield_callback: Optional[Callable] = ...,
widgets: Optional[Dict[str, Any]] = ..., widgets: Optional[Dict[str, Any]] = ...,
validate_max: bool = ..., validate_max: bool = ...,
localized_fields: None = ..., localized_fields: Optional[Sequence[str]] = ...,
labels: Optional[Dict[str, str]] = ..., labels: Optional[Dict[str, str]] = ...,
help_texts: Optional[Dict[str, str]] = ..., help_texts: Optional[Dict[str, str]] = ...,
error_messages: Optional[Dict[str, Dict[str, str]]] = ..., error_messages: Optional[Dict[str, Dict[str, str]]] = ...,
min_num: Optional[int] = ..., min_num: Optional[int] = ...,
validate_min: bool = ..., validate_min: bool = ...,
field_classes: Optional[Dict[str, Any]] = ..., field_classes: Optional[Dict[str, Any]] = ...,
) -> Any: ... ) -> Type[BaseInlineFormSet]: ...
class InlineForeignKeyField(Field): class InlineForeignKeyField(Field):
disabled: bool disabled: bool

View File

@@ -6,11 +6,11 @@ from django.core.exceptions import ValidationError
from django.utils.safestring import SafeText from django.utils.safestring import SafeText
def pretty_name(name: str) -> str: ... def pretty_name(name: str) -> str: ...
def flatatt(attrs: Dict[str, Optional[str]]) -> SafeText: ... def flatatt(attrs: Dict[str, Any]) -> SafeText: ...
class ErrorDict(dict): class ErrorDict(dict):
def as_data(self) -> Dict[str, List[ValidationError]]: ... def as_data(self) -> Dict[str, List[ValidationError]]: ...
def get_json_data(self, escape_html: bool = ...) -> Dict[str, List[Dict[str, str]]]: ... def get_json_data(self, escape_html: bool = ...) -> Dict[str, Any]: ...
def as_json(self, escape_html: bool = ...) -> str: ... def as_json(self, escape_html: bool = ...) -> str: ...
def as_ul(self) -> str: ... def as_ul(self) -> str: ...
def as_text(self) -> str: ... def as_text(self) -> str: ...
@@ -19,7 +19,9 @@ class ErrorList(UserList):
data: List[Union[ValidationError, str]] data: List[Union[ValidationError, str]]
error_class: str = ... error_class: str = ...
def __init__( def __init__(
self, initlist: Optional[Union[ErrorList, Sequence[str]]] = ..., error_class: Optional[str] = ... self,
initlist: Optional[Union[ErrorList, Sequence[Union[str, Exception]]]] = ...,
error_class: Optional[str] = ...,
) -> None: ... ) -> None: ...
def as_data(self) -> List[ValidationError]: ... def as_data(self) -> List[ValidationError]: ...
def get_json_data(self, escape_html: bool = ...) -> List[Dict[str, str]]: ... def get_json_data(self, escape_html: bool = ...) -> List[Dict[str, str]]: ...

View File

@@ -1,17 +1,16 @@
from datetime import time from datetime import time
from decimal import Decimal from decimal import Decimal
from itertools import chain from itertools import chain
from typing import Any, Callable, Dict, Iterator, List, Optional, Set, Tuple, Type, Union, Iterable, Sequence from typing import Any, Callable, Dict, Iterable, Iterator, List, Optional, Sequence, Set, Tuple, Type, Union
from django.contrib.admin.options import BaseModelAdmin
from django.core.files.base import File from django.core.files.base import File
from django.core.files.uploadedfile import SimpleUploadedFile
from django.db.models.fields.files import FieldFile from django.db.models.fields.files import FieldFile
from django.forms.forms import BaseForm
from django.forms.renderers import EngineMixin from django.forms.renderers import EngineMixin
from django.utils.datastructures import MultiValueDict from django.utils.datastructures import MultiValueDict
from django.utils.safestring import SafeText from django.utils.safestring import SafeText
_OptAttrs = Dict[str, str]
class MediaOrderConflictWarning(RuntimeWarning): ... class MediaOrderConflictWarning(RuntimeWarning): ...
class Media: class Media:
@@ -32,9 +31,7 @@ class Media:
def __add__(self, other: Media) -> Media: ... def __add__(self, other: Media) -> Media: ...
class MediaDefiningClass(type): class MediaDefiningClass(type):
def __new__( def __new__(mcs, name: str, bases: Sequence[Any], attrs: Dict[str, Any]) -> type: ...
mcs: Type[MediaDefiningClass], name: str, bases: Tuple, attrs: Any
) -> Type[Union[BaseModelAdmin, BaseForm, Widget]]: ...
class Widget: class Widget:
needs_multipart_form: bool = ... needs_multipart_form: bool = ...
@@ -61,10 +58,10 @@ class Widget:
self, base_attrs: Dict[str, Union[float, str]], extra_attrs: Optional[Dict[str, Union[bool, str]]] = ... self, base_attrs: Dict[str, Union[float, str]], extra_attrs: Optional[Dict[str, Union[bool, str]]] = ...
) -> Dict[str, Union[Decimal, float, str]]: ... ) -> Dict[str, Union[Decimal, float, str]]: ...
def value_from_datadict( def value_from_datadict(
self, data: dict, files: Union[Dict[str, SimpleUploadedFile], MultiValueDict], name: str self, data: dict, files: Union[Dict[str, Iterable[Any]], MultiValueDict], name: str
) -> Any: ... ) -> Any: ...
def value_omitted_from_data( def value_omitted_from_data(
self, data: Dict[str, Any], files: Union[Dict[str, SimpleUploadedFile], MultiValueDict], name: str self, data: Dict[str, Any], files: Union[Dict[str, Iterable[Any]], MultiValueDict], name: str
) -> bool: ... ) -> bool: ...
def id_for_label(self, id_: str) -> str: ... def id_for_label(self, id_: str) -> str: ...
def use_required_attribute(self, initial: Any) -> bool: ... def use_required_attribute(self, initial: Any) -> bool: ...
@@ -80,6 +77,7 @@ class URLInput(Input): ...
class PasswordInput(Input): class PasswordInput(Input):
render_value: bool = ... render_value: bool = ...
def __init__(self, attrs: Optional[_OptAttrs] = ..., render_value: bool = ...): ...
class HiddenInput(Input): class HiddenInput(Input):
choices: Iterable[Tuple[str, str]] choices: Iterable[Tuple[str, str]]
@@ -105,6 +103,7 @@ class DateTimeBaseInput(TextInput):
format_key: str = ... format_key: str = ...
supports_microseconds: bool = ... supports_microseconds: bool = ...
format: Optional[str] = ... format: Optional[str] = ...
def __init__(self, attrs: Optional[_OptAttrs] = ..., format: Optional[str] = ...): ...
class DateInput(DateTimeBaseInput): ... class DateInput(DateTimeBaseInput): ...
class DateTimeInput(DateTimeBaseInput): ... class DateTimeInput(DateTimeBaseInput): ...
@@ -112,9 +111,7 @@ class TimeInput(DateTimeBaseInput): ...
class CheckboxInput(Input): class CheckboxInput(Input):
check_test: Callable = ... check_test: Callable = ...
def __init__(self, attrs: Optional[Dict[str, str]] = ..., check_test: Optional[Callable] = ...) -> None: ... def __init__(self, attrs: Optional[_OptAttrs] = ..., check_test: Optional[Callable] = ...) -> None: ...
_OptAttrs = Dict[str, Any]
class ChoiceWidget(Widget): class ChoiceWidget(Widget):
allow_multiple_selected: bool = ... allow_multiple_selected: bool = ...
@@ -165,7 +162,7 @@ class CheckboxSelectMultiple(ChoiceWidget):
class MultiWidget(Widget): class MultiWidget(Widget):
template_name: str = ... template_name: str = ...
widgets: List[Widget] = ... widgets: List[Widget] = ...
def __init__(self, widgets: Sequence[Widget], attrs: Optional[_OptAttrs] = ...) -> None: ... def __init__(self, widgets: Sequence[Union[Widget, Type[Widget]]], attrs: Optional[_OptAttrs] = ...) -> None: ...
@property @property
def is_hidden(self) -> bool: ... def is_hidden(self) -> bool: ...
def decompress(self, value: Any) -> Optional[Any]: ... def decompress(self, value: Any) -> Optional[Any]: ...
@@ -209,7 +206,7 @@ class SelectDateWidget(Widget):
def __init__( def __init__(
self, self,
attrs: Optional[_OptAttrs] = ..., attrs: Optional[_OptAttrs] = ...,
years: Optional[Union[Tuple[Union[int, str]], range]] = ..., years: Optional[Iterable[Union[int, str]]] = ...,
months: None = ..., months: Optional[Dict[int, str]] = ...,
empty_label: Optional[Union[Tuple[str, str], str]] = ..., empty_label: Optional[Union[str, Sequence[str]]] = ...,
) -> None: ... ) -> None: ...

View File

@@ -33,7 +33,7 @@ class HttpResponseBase(Iterable[AnyStr]):
def has_header(self, header: str) -> bool: ... def has_header(self, header: str) -> bool: ...
def items(self) -> Iterable[Tuple[str, str]]: ... def items(self) -> Iterable[Tuple[str, str]]: ...
@overload @overload
def get(self, header: Union[str, bytes], alternate: str) -> str: ... def get(self, header: Union[str, bytes], alternate: Optional[str]) -> str: ...
@overload @overload
def get(self, header: Union[str, bytes]) -> Optional[str]: ... def get(self, header: Union[str, bytes]) -> Optional[str]: ...
def set_cookie( def set_cookie(

View File

@@ -1,4 +1,4 @@
from typing import Any, Callable, Dict, Iterator, List, Optional, Type, Union from typing import Any, Callable, Dict, Iterator, List, Optional, Type, Union, Iterable
from django.http.request import HttpRequest from django.http.request import HttpRequest
from django.template.base import Node, Origin, Template from django.template.base import Node, Origin, Template
@@ -15,10 +15,10 @@ class ContextDict(dict):
def __enter__(self) -> ContextDict: ... def __enter__(self) -> ContextDict: ...
def __exit__(self, *args: Any, **kwargs: Any) -> None: ... def __exit__(self, *args: Any, **kwargs: Any) -> None: ...
class BaseContext: class BaseContext(Iterable[Any]):
def __init__(self, dict_: Any = ...) -> None: ... def __init__(self, dict_: Any = ...) -> None: ...
def __copy__(self) -> BaseContext: ... def __copy__(self) -> BaseContext: ...
def __iter__(self) -> None: ... def __iter__(self) -> Iterator[Any]: ...
def push(self, *args: Any, **kwargs: Any) -> ContextDict: ... def push(self, *args: Any, **kwargs: Any) -> ContextDict: ...
def pop(self) -> ContextDict: ... def pop(self) -> ContextDict: ...
def __setitem__(self, key: Union[Node, str], value: Any) -> None: ... def __setitem__(self, key: Union[Node, str], value: Any) -> None: ...
@@ -50,7 +50,6 @@ class Context(BaseContext):
class RenderContext(BaseContext): class RenderContext(BaseContext):
dicts: List[Dict[Union[IncludeNode, str], str]] dicts: List[Dict[Union[IncludeNode, str], str]]
template: Optional[Template] = ... template: Optional[Template] = ...
def __iter__(self) -> None: ...
def push_state(self, template: Template, isolated_context: bool = ...) -> Iterator[None]: ... def push_state(self, template: Template, isolated_context: bool = ...) -> Iterator[None]: ...
class RequestContext(Context): class RequestContext(Context):

View File

@@ -1,48 +1,44 @@
from typing import Any, Callable, Dict, List, Optional, Tuple, Type from typing import Any, Callable, Dict, List, Optional, Type
from django import http from django import http
logger = ... # type: Any
class ContextMixin: class ContextMixin:
def get_context_data(self, **kwargs: object) -> Dict[str, Any]: ... def get_context_data(self, **kwargs: Any) -> Dict[str, Any]: ...
class View: class View:
http_method_names = ... # type: List[str] http_method_names: List[str] = ...
request = ... # type: http.HttpRequest request: http.HttpRequest = ...
args = ... # type: Tuple[object, ...] args: Any = ...
kwargs = ... # type: Dict[str, object] kwargs: Any = ...
def __init__(self, **kwargs: object) -> None: ... def __init__(self, **kwargs: Any) -> None: ...
@classmethod @classmethod
def as_view(cls: Any, **initkwargs: object) -> Callable[..., http.HttpResponse]: ... def as_view(cls: Any, **initkwargs: Any) -> Callable[..., http.HttpResponse]: ...
def dispatch(self, request: http.HttpRequest, *args: object, **kwargs: object) -> http.HttpResponse: ... def dispatch(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> http.HttpResponse: ...
def http_method_not_allowed( def http_method_not_allowed(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> http.HttpResponse: ...
self, request: http.HttpRequest, *args: object, **kwargs: object def options(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> http.HttpResponse: ...
) -> http.HttpResponse: ...
def options(self, request: http.HttpRequest, *args: object, **kwargs: object) -> http.HttpResponse: ...
class TemplateResponseMixin: class TemplateResponseMixin:
template_name = ... # type: str template_name: str = ...
template_engine = ... # type: Optional[str] template_engine: Optional[str] = ...
response_class = ... # type: Type[http.HttpResponse] response_class: Type[http.HttpResponse] = ...
content_type = ... # type: Optional[str] content_type: Optional[str] = ...
request = ... # type: http.HttpRequest request: http.HttpRequest = ...
def render_to_response(self, context: Dict[str, object], **response_kwargs: object) -> http.HttpResponse: ... def render_to_response(self, context: Dict[str, Any], **response_kwargs: Any) -> http.HttpResponse: ...
def get_template_names(self) -> List[str]: ... def get_template_names(self) -> List[str]: ...
class TemplateView(TemplateResponseMixin, ContextMixin, View): class TemplateView(TemplateResponseMixin, ContextMixin, View):
def get(self, request: http.HttpRequest, *args: object, **kwargs: object) -> http.HttpResponse: ... def get(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> http.HttpResponse: ...
class RedirectView(View): class RedirectView(View):
permanent = ... # type: bool permanent: bool = ...
url = ... # type: Optional[str] url: Optional[str] = ...
pattern_name = ... # type: Optional[str] pattern_name: Optional[str] = ...
query_string = ... # type: bool query_string: bool = ...
def get_redirect_url(self, *args: object, **kwargs: object) -> Optional[str]: ... def get_redirect_url(self, *args: Any, **kwargs: Any) -> Optional[str]: ...
def get(self, request: http.HttpRequest, *args: object, **kwargs: object) -> http.HttpResponse: ... def get(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> http.HttpResponse: ...
def head(self, request: http.HttpRequest, *args: object, **kwargs: object) -> http.HttpResponse: ... def head(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> http.HttpResponse: ...
def post(self, request: http.HttpRequest, *args: object, **kwargs: object) -> http.HttpResponse: ... def post(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> http.HttpResponse: ...
def options(self, request: http.HttpRequest, *args: object, **kwargs: object) -> http.HttpResponse: ... def options(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> http.HttpResponse: ...
def delete(self, request: http.HttpRequest, *args: object, **kwargs: object) -> http.HttpResponse: ... def delete(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> http.HttpResponse: ...
def put(self, request: http.HttpRequest, *args: object, **kwargs: object) -> http.HttpResponse: ... def put(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> http.HttpResponse: ...
def patch(self, request: http.HttpRequest, *args: object, **kwargs: object) -> http.HttpResponse: ... def patch(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> http.HttpResponse: ...

View File

@@ -5,31 +5,30 @@ from django.http import HttpResponse, HttpRequest
from django.views.generic.base import ContextMixin, TemplateResponseMixin, View from django.views.generic.base import ContextMixin, TemplateResponseMixin, View
class SingleObjectMixin(ContextMixin): class SingleObjectMixin(ContextMixin):
model = ... # type: Optional[Type[models.Model]] model: Optional[Type[models.Model]] = ...
queryset = ... # type: Optional[models.query.QuerySet] queryset: Optional[models.query.QuerySet] = ...
slug_field = ... # type: str slug_field: str = ...
context_object_name = ... # type: Optional[str] context_object_name: Optional[str] = ...
slug_url_kwarg = ... # type: str slug_url_kwarg: str = ...
pk_url_kwarg = ... # type: str pk_url_kwarg: str = ...
query_pk_and_slug = ... # type: bool query_pk_and_slug: bool = ...
object = ... # type: models.Model object: models.Model = ...
kwargs = ... # type: Dict[str, object] kwargs: Dict[str, Any] = ...
def get_object(self, queryset: models.query.QuerySet = None) -> models.Model: ... def get_object(self, queryset: Optional[models.query.QuerySet] = None) -> models.Model: ...
def get_queryset(self) -> models.query.QuerySet: ... def get_queryset(self) -> models.query.QuerySet: ...
def get_slug_field(self) -> str: ... def get_slug_field(self) -> str: ...
def get_context_object_name(self, obj: Any) -> Optional[str]: ... def get_context_object_name(self, obj: Any) -> Optional[str]: ...
def get_context_data(self, **kwargs: object) -> Dict[str, Any]: ... def get_context_data(self, **kwargs: Any) -> Dict[str, Any]: ...
class BaseDetailView(SingleObjectMixin, View): class BaseDetailView(SingleObjectMixin, View):
def render_to_response(self, context: Dict[str, object], **response_kwargs: object) -> HttpResponse: ... def render_to_response(self, context: Dict[str, Any], **response_kwargs: Any) -> HttpResponse: ...
object = ... # type: models.Model def get(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse: ...
def get(self, request: HttpRequest, *args: object, **kwargs: object) -> HttpResponse: ...
class SingleObjectTemplateResponseMixin(TemplateResponseMixin): class SingleObjectTemplateResponseMixin(TemplateResponseMixin):
template_name_field = ... # type: Optional[str] template_name_field: Optional[str] = ...
template_name_suffix = ... # type: str template_name_suffix: str = ...
model = ... # type: Optional[Type[models.Model]] model: Optional[Type[models.Model]] = ...
object = ... # type: models.Model object: models.Model = ...
def get_template_names(self) -> List[str]: ... def get_template_names(self) -> List[str]: ...
class DetailView(SingleObjectTemplateResponseMixin, BaseDetailView): ... class DetailView(SingleObjectTemplateResponseMixin, BaseDetailView): ...

View File

@@ -1,77 +1,53 @@
from typing import Any, Callable, Dict, Optional, Sequence, Type, Union from typing import Any, Callable, Dict, Optional, Sequence, Type, Union
from django.db import models
from django.forms.forms import BaseForm
from django.http import HttpRequest, HttpResponse
from django.views.generic.base import ContextMixin, TemplateResponseMixin, View from django.views.generic.base import ContextMixin, TemplateResponseMixin, View
from django.views.generic.detail import BaseDetailView, SingleObjectMixin, SingleObjectTemplateResponseMixin from django.views.generic.detail import BaseDetailView, SingleObjectMixin, SingleObjectTemplateResponseMixin
from typing_extensions import Literal from typing_extensions import Literal
from django.db import models
from django.forms import Form
from django.http import HttpRequest, HttpResponse
class FormMixin(ContextMixin): class FormMixin(ContextMixin):
initial: Dict[str, Any] = ... initial: Dict[str, Any] = ...
form_class: Optional[Type[Form]] = ... form_class: Optional[Type[BaseForm]] = ...
success_url: Optional[Union[str, Callable[..., Any]]] = ... success_url: Optional[Union[str, Callable[..., Any]]] = ...
prefix: Optional[str] = ... prefix: Optional[str] = ...
request: HttpRequest = ... request: HttpRequest = ...
def render_to_response(self, context: Dict[str, Any], **response_kwargs: object) -> HttpResponse: ... def render_to_response(self, context: Dict[str, Any], **response_kwargs: object) -> HttpResponse: ...
def get_initial(self) -> Dict[str, Any]: ... def get_initial(self) -> Dict[str, Any]: ...
def get_prefix(self) -> Optional[str]: ... def get_prefix(self) -> Optional[str]: ...
def get_form_class(self) -> Type[Form]: ... def get_form_class(self) -> Type[BaseForm]: ...
def get_form(self, form_class: Type[Form] = None) -> Form: ... def get_form(self, form_class: Optional[Type[BaseForm]] = None) -> BaseForm: ...
def get_form_kwargs(self) -> Dict[str, Any]: ... def get_form_kwargs(self) -> Dict[str, Any]: ...
def get_success_url(self) -> str: ... def get_success_url(self) -> str: ...
def form_valid(self, form: Form) -> HttpResponse: ... def form_valid(self, form: BaseForm) -> HttpResponse: ...
def form_invalid(self, form: Form) -> HttpResponse: ... def form_invalid(self, form: BaseForm) -> HttpResponse: ...
def get_context_data(self, **kwargs: object) -> Dict[str, Any]: ...
class ModelFormMixin(FormMixin, SingleObjectMixin): class ModelFormMixin(FormMixin, SingleObjectMixin):
fields: Optional[Union[Sequence[str], Literal["__all__"]]] = ... fields: Optional[Union[Sequence[str], Literal["__all__"]]] = ...
object: models.Model = ...
def get_form_class(self) -> Type[Form]: ...
def get_form_kwargs(self) -> Dict[str, object]: ...
def get_success_url(self) -> str: ...
def form_valid(self, form: Form) -> HttpResponse: ...
class ProcessFormView(View): class ProcessFormView(View):
def render_to_response(self, context: Dict[str, object], **response_kwargs: object) -> HttpResponse: ... def render_to_response(self, context: Dict[str, Any], **response_kwargs: Any) -> HttpResponse: ...
def get_context_data(self, **kwargs: object) -> Dict[str, Any]: ... def get_context_data(self, **kwargs: Any) -> Dict[str, Any]: ...
def get_form(self, form_class: Type[Form] = None) -> Form: ... def get(self, request: HttpRequest, *args: str, **kwargs: Any) -> HttpResponse: ...
def form_valid(self, form: Form) -> HttpResponse: ... def post(self, request: HttpRequest, *args: str, **kwargs: Any) -> HttpResponse: ...
def form_invalid(self, form: Form) -> HttpResponse: ... def put(self, *args: str, **kwargs: Any) -> HttpResponse: ...
def get(self, request: HttpRequest, *args: object, **kwargs: object) -> HttpResponse: ...
def post(self, request: HttpRequest, *args: object, **kwargs: object) -> HttpResponse: ...
def put(self, *args: Any, **kwargs: Any) -> HttpResponse: ...
class BaseFormView(FormMixin, ProcessFormView): ... class BaseFormView(FormMixin, ProcessFormView): ...
class FormView(TemplateResponseMixin, BaseFormView): ... class FormView(TemplateResponseMixin, BaseFormView): ...
class BaseCreateView(ModelFormMixin, ProcessFormView): ...
class BaseCreateView(ModelFormMixin, ProcessFormView): class CreateView(SingleObjectTemplateResponseMixin, BaseCreateView): ...
object = ... # type: models.Model class BaseUpdateView(ModelFormMixin, ProcessFormView): ...
def get(self, request: HttpRequest, *args: object, **kwargs: object) -> HttpResponse: ... class UpdateView(SingleObjectTemplateResponseMixin, BaseUpdateView): ...
def post(self, request: HttpRequest, *args: object, **kwargs: object) -> HttpResponse: ...
class CreateView(SingleObjectTemplateResponseMixin, BaseCreateView):
template_name_suffix = ... # type: str
class BaseUpdateView(ModelFormMixin, ProcessFormView):
object = ... # type: models.Model
def get(self, request: HttpRequest, *args: object, **kwargs: object) -> HttpResponse: ...
def post(self, request: HttpRequest, *args: object, **kwargs: object) -> HttpResponse: ...
class UpdateView(SingleObjectTemplateResponseMixin, BaseUpdateView):
template_name_suffix = ... # type: str
_object = object _object = object
class DeletionMixin: class DeletionMixin:
success_url = ... # type: Optional[str] success_url: Optional[str] = ...
object = ... # type: models.Model object: models.Model = ...
def delete(self, request: HttpRequest, *args: _object, **kwargs: _object) -> HttpResponse: ... def post(self, request: HttpRequest, *args: str, **kwargs: Any) -> HttpResponse: ...
def post(self, request: HttpRequest, *args: _object, **kwargs: _object) -> HttpResponse: ... def delete(self, request: HttpRequest, *args: str, **kwargs: Any) -> HttpResponse: ...
def get_success_url(self) -> str: ... def get_success_url(self) -> str: ...
class BaseDeleteView(DeletionMixin, BaseDetailView): ... class BaseDeleteView(DeletionMixin, BaseDetailView): ...
class DeleteView(SingleObjectTemplateResponseMixin, BaseDeleteView): ...
class DeleteView(SingleObjectTemplateResponseMixin, BaseDeleteView):
template_name_suffix = ... # type: str

View File

@@ -2,13 +2,16 @@ import typing
from typing import Dict, Optional from typing import Dict, Optional
from mypy.checker import TypeChecker from mypy.checker import TypeChecker
from mypy.nodes import AssignmentStmt, ClassDef, Expression, FuncDef, ImportedName, Lvalue, MypyFile, NameExpr, SymbolNode, \ from mypy.nodes import AssignmentStmt, ClassDef, Expression, ImportedName, Lvalue, MypyFile, NameExpr, SymbolNode, \
TypeInfo TypeInfo
from mypy.plugin import FunctionContext from mypy.plugin import FunctionContext
from mypy.types import AnyType, CallableType, Instance, Type, TypeOfAny, TypeVarType, UnionType from mypy.types import AnyType, Instance, NoneTyp, Type, TypeOfAny, TypeVarType, UnionType
MODEL_CLASS_FULLNAME = 'django.db.models.base.Model' MODEL_CLASS_FULLNAME = 'django.db.models.base.Model'
FIELD_FULLNAME = 'django.db.models.fields.Field' FIELD_FULLNAME = 'django.db.models.fields.Field'
CHAR_FIELD_FULLNAME = 'django.db.models.fields.CharField'
ARRAY_FIELD_FULLNAME = 'django.contrib.postgres.fields.array.ArrayField'
AUTO_FIELD_FULLNAME = 'django.db.models.fields.AutoField'
GENERIC_FOREIGN_KEY_FULLNAME = 'django.contrib.contenttypes.fields.GenericForeignKey' GENERIC_FOREIGN_KEY_FULLNAME = 'django.contrib.contenttypes.fields.GenericForeignKey'
FOREIGN_KEY_FULLNAME = 'django.db.models.fields.related.ForeignKey' FOREIGN_KEY_FULLNAME = 'django.db.models.fields.related.ForeignKey'
ONETOONE_FIELD_FULLNAME = 'django.db.models.fields.related.OneToOneField' ONETOONE_FIELD_FULLNAME = 'django.db.models.fields.related.OneToOneField'
@@ -19,6 +22,7 @@ QUERYSET_CLASS_FULLNAME = 'django.db.models.query.QuerySet'
BASE_MANAGER_CLASS_FULLNAME = 'django.db.models.manager.BaseManager' BASE_MANAGER_CLASS_FULLNAME = 'django.db.models.manager.BaseManager'
MANAGER_CLASS_FULLNAME = 'django.db.models.manager.Manager' MANAGER_CLASS_FULLNAME = 'django.db.models.manager.Manager'
RELATED_MANAGER_CLASS_FULLNAME = 'django.db.models.manager.RelatedManager' RELATED_MANAGER_CLASS_FULLNAME = 'django.db.models.manager.RelatedManager'
MODELFORM_CLASS_FULLNAME = 'django.forms.models.ModelForm'
MANAGER_CLASSES = { MANAGER_CLASSES = {
MANAGER_CLASS_FULLNAME, MANAGER_CLASS_FULLNAME,
@@ -51,9 +55,9 @@ def get_model_fullname(app_name: str, model_name: str,
return None return None
class InvalidModelString(ValueError): class SameFileModel(Exception):
def __init__(self, model_string: str): def __init__(self, model_cls_name: str):
self.model_string = model_string self.model_cls_name = model_cls_name
class SelfReference(ValueError): class SelfReference(ValueError):
@@ -66,7 +70,7 @@ def get_model_fullname_from_string(model_string: str,
raise SelfReference() raise SelfReference()
if '.' not in model_string: if '.' not in model_string:
raise InvalidModelString(model_string) raise SameFileModel(model_string)
app_name, model_name = model_string.split('.') app_name, model_name = model_string.split('.')
return get_model_fullname(app_name, model_name, all_modules) return get_model_fullname(app_name, model_name, all_modules)
@@ -95,12 +99,13 @@ def parse_bool(expr: Expression) -> Optional[bool]:
return None return None
def reparametrize_with(instance: Instance, new_typevars: typing.List[Type]): def reparametrize_instance(instance: Instance, new_args: typing.List[Type]) -> Instance:
return Instance(instance.type, args=new_typevars) return Instance(instance.type, args=new_args,
line=instance.line, column=instance.column)
def fill_typevars_with_any(instance: Instance) -> Type: def fill_typevars_with_any(instance: Instance) -> Instance:
return reparametrize_with(instance, [AnyType(TypeOfAny.unannotated)]) return reparametrize_instance(instance, [AnyType(TypeOfAny.unannotated)])
def extract_typevar_value(tp: Instance, typevar_name: str) -> Type: def extract_typevar_value(tp: Instance, typevar_name: str) -> Type:
@@ -117,7 +122,7 @@ def fill_typevars(tp: Instance, type_to_fill: Instance) -> Instance:
for typevar_arg in type_to_fill.args: for typevar_arg in type_to_fill.args:
if isinstance(typevar_arg, TypeVarType): if isinstance(typevar_arg, TypeVarType):
typevar_values.append(extract_typevar_value(tp, typevar_arg.name)) typevar_values.append(extract_typevar_value(tp, typevar_arg.name))
return reparametrize_with(type_to_fill, typevar_values) return Instance(type_to_fill.type, typevar_values)
def get_argument_by_name(ctx: FunctionContext, name: str) -> Optional[Expression]: def get_argument_by_name(ctx: FunctionContext, name: str) -> Optional[Expression]:
@@ -189,28 +194,12 @@ def iter_over_assignments(
def extract_field_setter_type(tp: Instance) -> Optional[Type]: def extract_field_setter_type(tp: Instance) -> Optional[Type]:
if not isinstance(tp, Instance): """ Extract __set__ value of a field. """
return None
if tp.type.has_base(FIELD_FULLNAME): if tp.type.has_base(FIELD_FULLNAME):
set_method = tp.type.get_method('__set__') return tp.args[0]
if isinstance(set_method, FuncDef) and isinstance(set_method.type, CallableType): # GenericForeignKey
if 'value' in set_method.type.arg_names: if tp.type.has_base(GENERIC_FOREIGN_KEY_FULLNAME):
set_value_type = set_method.type.arg_types[set_method.type.arg_names.index('value')] return AnyType(TypeOfAny.special_form)
if isinstance(set_value_type, Instance):
set_value_type = fill_typevars(tp, set_value_type)
return set_value_type
elif isinstance(set_value_type, UnionType):
items_no_typevars = []
for item in set_value_type.items:
if isinstance(item, Instance):
item = fill_typevars(tp, item)
items_no_typevars.append(item)
return UnionType(items_no_typevars)
field_getter_type = extract_field_getter_type(tp)
if field_getter_type:
return field_getter_type
return None return None
@@ -218,9 +207,7 @@ def extract_field_getter_type(tp: Instance) -> Optional[Type]:
if not isinstance(tp, Instance): if not isinstance(tp, Instance):
return None return None
if tp.type.has_base(FIELD_FULLNAME): if tp.type.has_base(FIELD_FULLNAME):
get_method = tp.type.get_method('__get__') return tp.args[1]
if isinstance(get_method, FuncDef) and isinstance(get_method.type, CallableType):
return get_method.type.ret_type
# GenericForeignKey # GenericForeignKey
if tp.type.has_base(GENERIC_FOREIGN_KEY_FULLNAME): if tp.type.has_base(GENERIC_FOREIGN_KEY_FULLNAME):
return AnyType(TypeOfAny.special_form) return AnyType(TypeOfAny.special_form)
@@ -240,7 +227,10 @@ def get_fields_metadata(model: TypeInfo) -> Dict[str, typing.Any]:
return get_django_metadata(model).setdefault('fields', {}) return get_django_metadata(model).setdefault('fields', {})
def extract_primary_key_type_for_set(model: TypeInfo) -> Optional[Type]: def extract_explicit_set_type_of_model_primary_key(model: TypeInfo) -> Optional[Type]:
"""
If field with primary_key=True is set on the model, extract its __set__ type.
"""
for field_name, props in get_fields_metadata(model).items(): for field_name, props in get_fields_metadata(model).items():
is_primary_key = props.get('primary_key', False) is_primary_key = props.get('primary_key', False)
if is_primary_key: if is_primary_key:
@@ -254,3 +244,40 @@ def extract_primary_key_type_for_get(model: TypeInfo) -> Optional[Type]:
if is_primary_key: if is_primary_key:
return extract_field_getter_type(model.names[field_name].type) return extract_field_getter_type(model.names[field_name].type)
return None return None
def make_optional(typ: Type):
return UnionType.make_union([typ, NoneTyp()])
def make_required(typ: Type) -> Type:
if not isinstance(typ, UnionType):
return typ
items = [item for item in typ.items if not isinstance(item, NoneTyp)]
# will reduce to Instance, if only one item
return UnionType.make_union(items)
def is_optional(typ: Type) -> bool:
if not isinstance(typ, UnionType):
return False
return any([isinstance(item, NoneTyp) for item in typ.items])
def has_any_of_bases(info: TypeInfo, bases: typing.Sequence[str]) -> bool:
for base_fullname in bases:
if info.has_base(base_fullname):
return True
return False
def is_none_expr(expr: Expression) -> bool:
return isinstance(expr, NameExpr) and expr.fullname == 'builtins.None'
def get_nested_meta_node_for_current_class(info: TypeInfo) -> Optional[TypeInfo]:
metaclass_sym = info.names.get('Meta')
if metaclass_sym is not None and isinstance(metaclass_sym.node, TypeInfo):
return metaclass_sym.node
return None

View File

@@ -1,19 +1,20 @@
import os import os
from typing import Callable, Dict, Optional, cast from typing import Callable, Dict, Optional, Union, cast
from mypy.checker import TypeChecker from mypy.checker import TypeChecker
from mypy.nodes import MemberExpr, TypeInfo from mypy.nodes import MemberExpr, TypeInfo
from mypy.options import Options from mypy.options import Options
from mypy.plugin import AttributeContext, ClassDefContext, FunctionContext, MethodContext, Plugin from mypy.plugin import AttributeContext, ClassDefContext, FunctionContext, MethodContext, Plugin
from mypy.types import AnyType, Instance, Type, TypeOfAny, TypeType from mypy.types import AnyType, Instance, Type, TypeOfAny, TypeType, UnionType
from mypy_django_plugin import helpers, monkeypatch from mypy_django_plugin import helpers, monkeypatch
from mypy_django_plugin.config import Config from mypy_django_plugin.config import Config
from mypy_django_plugin.plugins import init_create from mypy_django_plugin.transformers import fields, init_create
from mypy_django_plugin.plugins.fields import determine_type_of_array_field, record_field_properties_into_outer_model_class from mypy_django_plugin.transformers.forms import make_meta_nested_class_inherit_from_any
from mypy_django_plugin.plugins.migrations import determine_model_cls_from_string_for_migrations, get_string_value_from_expr from mypy_django_plugin.transformers.migrations import determine_model_cls_from_string_for_migrations, \
from mypy_django_plugin.plugins.models import process_model_class get_string_value_from_expr
from mypy_django_plugin.plugins.related_fields import extract_to_parameter_as_get_ret_type_for_related_field, reparametrize_with from mypy_django_plugin.transformers.models import process_model_class
from mypy_django_plugin.plugins.settings import AddSettingValuesToDjangoConfObject, get_settings_metadata from mypy_django_plugin.transformers.settings import AddSettingValuesToDjangoConfObject, get_settings_metadata
def transform_model_class(ctx: ClassDefContext) -> None: def transform_model_class(ctx: ClassDefContext) -> None:
@@ -34,6 +35,14 @@ def transform_manager_class(ctx: ClassDefContext) -> None:
sym.node.metadata['django']['manager_bases'][ctx.cls.fullname] = 1 sym.node.metadata['django']['manager_bases'][ctx.cls.fullname] = 1
def transform_modelform_class(ctx: ClassDefContext) -> None:
sym = ctx.api.lookup_fully_qualified_or_none(helpers.MODELFORM_CLASS_FULLNAME)
if sym is not None and isinstance(sym.node, TypeInfo):
sym.node.metadata['django']['modelform_bases'][ctx.cls.fullname] = 1
make_meta_nested_class_inherit_from_any(ctx)
def determine_proper_manager_type(ctx: FunctionContext) -> Type: def determine_proper_manager_type(ctx: FunctionContext) -> Type:
api = cast(TypeChecker, ctx.api) api = cast(TypeChecker, ctx.api)
ret = ctx.default_return_type ret = ctx.default_return_type
@@ -50,7 +59,7 @@ def determine_proper_manager_type(ctx: FunctionContext) -> Type:
if base.type.fullname() in {helpers.MANAGER_CLASS_FULLNAME, if base.type.fullname() in {helpers.MANAGER_CLASS_FULLNAME,
helpers.RELATED_MANAGER_CLASS_FULLNAME, helpers.RELATED_MANAGER_CLASS_FULLNAME,
helpers.BASE_MANAGER_CLASS_FULLNAME}: helpers.BASE_MANAGER_CLASS_FULLNAME}:
ret.type.bases[i] = reparametrize_with(base, [Instance(outer_model_info, [])]) ret.type.bases[i] = Instance(base.type, [Instance(outer_model_info, [])])
return ret return ret
return ret return ret
@@ -84,6 +93,17 @@ def return_user_model_hook(ctx: FunctionContext) -> Type:
return TypeType(Instance(model_info, [])) return TypeType(Instance(model_info, []))
def _extract_referred_to_type_info(typ: Union[UnionType, Instance]) -> Optional[TypeInfo]:
if isinstance(typ, Instance):
return typ.type
else:
# should be Union[TYPE, None]
typ = helpers.make_required(typ)
if isinstance(typ, Instance):
return typ.type
return None
def extract_and_return_primary_key_of_bound_related_field_parameter(ctx: AttributeContext) -> Type: def extract_and_return_primary_key_of_bound_related_field_parameter(ctx: AttributeContext) -> Type:
if not isinstance(ctx.default_attr_type, Instance) or not (ctx.default_attr_type.type.fullname() == 'builtins.int'): if not isinstance(ctx.default_attr_type, Instance) or not (ctx.default_attr_type.type.fullname() == 'builtins.int'):
return ctx.default_attr_type return ctx.default_attr_type
@@ -94,14 +114,28 @@ def extract_and_return_primary_key_of_bound_related_field_parameter(ctx: Attribu
field_name = ctx.context.name.split('_')[0] field_name = ctx.context.name.split('_')[0]
sym = ctx.type.type.get(field_name) sym = ctx.type.type.get(field_name)
if sym and isinstance(sym.type, Instance) and len(sym.type.args) > 0: if sym and isinstance(sym.type, Instance) and len(sym.type.args) > 0:
to_arg = sym.type.args[0] referred_to = sym.type.args[1]
if isinstance(to_arg, AnyType): if isinstance(referred_to, AnyType):
return AnyType(TypeOfAny.special_form) return AnyType(TypeOfAny.implementation_artifact)
model_type = _extract_referred_to_type_info(referred_to)
if model_type is None:
return AnyType(TypeOfAny.implementation_artifact)
model_type: TypeInfo = to_arg.type
primary_key_type = helpers.extract_primary_key_type_for_get(model_type) primary_key_type = helpers.extract_primary_key_type_for_get(model_type)
if primary_key_type: if primary_key_type:
return primary_key_type return primary_key_type
is_nullable = helpers.get_fields_metadata(ctx.type.type).get(field_name, {}).get('null', False)
if is_nullable:
return helpers.make_optional(ctx.default_attr_type)
return ctx.default_attr_type
def return_integer_type_for_id_for_non_defined_primary_key_in_models(ctx: AttributeContext) -> Type:
if isinstance(ctx.type, Instance) and ctx.type.type.has_base(helpers.MODEL_CLASS_FULLNAME):
return ctx.api.named_generic_type('builtins.int', [])
return ctx.default_attr_type return ctx.default_attr_type
@@ -152,22 +186,27 @@ class DjangoPlugin(Plugin):
def _get_current_model_bases(self) -> Dict[str, int]: def _get_current_model_bases(self) -> Dict[str, int]:
model_sym = self.lookup_fully_qualified(helpers.MODEL_CLASS_FULLNAME) model_sym = self.lookup_fully_qualified(helpers.MODEL_CLASS_FULLNAME)
if model_sym is not None and isinstance(model_sym.node, TypeInfo): if model_sym is not None and isinstance(model_sym.node, TypeInfo):
if 'django' not in model_sym.node.metadata: return (model_sym.node.metadata
model_sym.node.metadata['django'] = { .setdefault('django', {})
'model_bases': {helpers.MODEL_CLASS_FULLNAME: 1} .setdefault('model_bases', {helpers.MODEL_CLASS_FULLNAME: 1}))
}
return model_sym.node.metadata['django']['model_bases']
else: else:
return {} return {}
def _get_current_manager_bases(self) -> Dict[str, int]: def _get_current_manager_bases(self) -> Dict[str, int]:
manager_sym = self.lookup_fully_qualified(helpers.MANAGER_CLASS_FULLNAME) model_sym = self.lookup_fully_qualified(helpers.MANAGER_CLASS_FULLNAME)
if manager_sym is not None and isinstance(manager_sym.node, TypeInfo): if model_sym is not None and isinstance(model_sym.node, TypeInfo):
if 'django' not in manager_sym.node.metadata: return (model_sym.node.metadata
manager_sym.node.metadata['django'] = { .setdefault('django', {})
'manager_bases': {helpers.MANAGER_CLASS_FULLNAME: 1} .setdefault('manager_bases', {helpers.MANAGER_CLASS_FULLNAME: 1}))
} else:
return manager_sym.node.metadata['django']['manager_bases'] return {}
def _get_current_modelform_bases(self) -> Dict[str, int]:
model_sym = self.lookup_fully_qualified(helpers.MODELFORM_CLASS_FULLNAME)
if model_sym is not None and isinstance(model_sym.node, TypeInfo):
return (model_sym.node.metadata
.setdefault('django', {})
.setdefault('modelform_bases', {helpers.MODELFORM_CLASS_FULLNAME: 1}))
else: else:
return {} return {}
@@ -176,43 +215,32 @@ class DjangoPlugin(Plugin):
if fullname == 'django.contrib.auth.get_user_model': if fullname == 'django.contrib.auth.get_user_model':
return return_user_model_hook return return_user_model_hook
if fullname in {helpers.FOREIGN_KEY_FULLNAME,
helpers.ONETOONE_FIELD_FULLNAME,
helpers.MANYTOMANY_FIELD_FULLNAME}:
return extract_to_parameter_as_get_ret_type_for_related_field
if fullname == 'django.contrib.postgres.fields.array.ArrayField':
return determine_type_of_array_field
manager_bases = self._get_current_manager_bases() manager_bases = self._get_current_manager_bases()
if fullname in manager_bases: if fullname in manager_bases:
return determine_proper_manager_type return determine_proper_manager_type
sym = self.lookup_fully_qualified(fullname) sym = self.lookup_fully_qualified(fullname)
if sym and isinstance(sym.node, TypeInfo): if sym is not None and isinstance(sym.node, TypeInfo):
if sym.node.has_base(helpers.FIELD_FULLNAME): if sym.node.has_base(helpers.FIELD_FULLNAME):
return record_field_properties_into_outer_model_class return fields.adjust_return_type_of_field_instantiation
if sym.node.metadata.get('django', {}).get('generated_init'): if sym.node.metadata.get('django', {}).get('generated_init'):
return init_create.redefine_and_typecheck_model_init return init_create.redefine_and_typecheck_model_init
def get_method_hook(self, fullname: str def get_method_hook(self, fullname: str
) -> Optional[Callable[[MethodContext], Type]]: ) -> Optional[Callable[[MethodContext], Type]]:
if fullname in {'django.apps.registry.Apps.get_model',
'django.db.migrations.state.StateApps.get_model'}:
return determine_model_cls_from_string_for_migrations
manager_classes = self._get_current_manager_bases() manager_classes = self._get_current_manager_bases()
class_fullname, _, method_name = fullname.rpartition('.') class_fullname, _, method_name = fullname.rpartition('.')
if class_fullname in manager_classes and method_name == 'create': if class_fullname in manager_classes and method_name == 'create':
return init_create.redefine_and_typecheck_model_create return init_create.redefine_and_typecheck_model_create
if fullname in {'django.apps.registry.Apps.get_model',
'django.db.migrations.state.StateApps.get_model'}:
return determine_model_cls_from_string_for_migrations
return None return None
def get_base_class_hook(self, fullname: str def get_base_class_hook(self, fullname: str
) -> Optional[Callable[[ClassDefContext], None]]: ) -> Optional[Callable[[ClassDefContext], None]]:
if fullname in self._get_current_model_bases():
return transform_model_class
if fullname == helpers.DUMMY_SETTINGS_BASE_CLASS: if fullname == helpers.DUMMY_SETTINGS_BASE_CLASS:
settings_modules = ['django.conf.global_settings'] settings_modules = ['django.conf.global_settings']
if self.django_settings_module: if self.django_settings_module:
@@ -220,13 +248,22 @@ class DjangoPlugin(Plugin):
return AddSettingValuesToDjangoConfObject(settings_modules, return AddSettingValuesToDjangoConfObject(settings_modules,
self.config.ignore_missing_settings) self.config.ignore_missing_settings)
if fullname in self._get_current_model_bases():
return transform_model_class
if fullname in self._get_current_manager_bases(): if fullname in self._get_current_manager_bases():
return transform_manager_class return transform_manager_class
if fullname in self._get_current_modelform_bases():
return transform_modelform_class
return None return None
def get_attribute_hook(self, fullname: str def get_attribute_hook(self, fullname: str
) -> Optional[Callable[[AttributeContext], Type]]: ) -> Optional[Callable[[AttributeContext], Type]]:
if fullname == 'builtins.object.id':
return return_integer_type_for_id_for_non_defined_primary_key_in_models
module, _, name = fullname.rpartition('.') module, _, name = fullname.rpartition('.')
sym = self.lookup_fully_qualified('django.conf.LazySettings') sym = self.lookup_fully_qualified('django.conf.LazySettings')
if sym and isinstance(sym.node, TypeInfo): if sym and isinstance(sym.node, TypeInfo):

View File

@@ -1,74 +0,0 @@
from typing import cast
from mypy.checker import TypeChecker
from mypy.nodes import ListExpr, NameExpr, TupleExpr
from mypy.plugin import FunctionContext
from mypy.types import Instance, TupleType, Type
from mypy_django_plugin import helpers
from mypy_django_plugin.plugins.models import iter_over_assignments
def determine_type_of_array_field(ctx: FunctionContext) -> Type:
base_field_arg_type = helpers.get_argument_type_by_name(ctx, 'base_field')
if not base_field_arg_type or not isinstance(base_field_arg_type, Instance):
return ctx.default_return_type
get_method = base_field_arg_type.type.get_method('__get__')
if not get_method:
# not a method
return ctx.default_return_type
return ctx.api.named_generic_type(ctx.context.callee.fullname,
args=[get_method.type.ret_type])
def record_field_properties_into_outer_model_class(ctx: FunctionContext) -> Type:
api = cast(TypeChecker, ctx.api)
outer_model = api.scope.active_class()
if outer_model is None or not outer_model.has_base(helpers.MODEL_CLASS_FULLNAME):
# outside models.Model class, undetermined
return ctx.default_return_type
field_name = None
for name_expr, stmt in iter_over_assignments(outer_model.defn):
if stmt == ctx.context and isinstance(name_expr, NameExpr):
field_name = name_expr.name
break
if field_name is None:
return ctx.default_return_type
fields_metadata = outer_model.metadata.setdefault('django', {}).setdefault('fields', {})
# primary key
is_primary_key = False
primary_key_arg = helpers.get_argument_by_name(ctx, 'primary_key')
if primary_key_arg:
is_primary_key = helpers.parse_bool(primary_key_arg)
fields_metadata[field_name] = {'primary_key': is_primary_key}
# choices
choices_arg = helpers.get_argument_by_name(ctx, 'choices')
if choices_arg and isinstance(choices_arg, (TupleExpr, ListExpr)):
# iterable of 2 element tuples of two kinds
_, analyzed_choices = api.analyze_iterable_item_type(choices_arg)
if isinstance(analyzed_choices, TupleType):
first_element_type = analyzed_choices.items[0]
if isinstance(first_element_type, Instance):
fields_metadata[field_name]['choices'] = first_element_type.type.fullname()
# nullability
null_arg = helpers.get_argument_by_name(ctx, 'null')
is_nullable = False
if null_arg:
is_nullable = helpers.parse_bool(null_arg)
fields_metadata[field_name]['null'] = is_nullable
# is_blankable
blank_arg = helpers.get_argument_by_name(ctx, 'blank')
is_blankable = False
if blank_arg:
is_blankable = helpers.parse_bool(blank_arg)
fields_metadata[field_name]['blank'] = is_blankable
return ctx.default_return_type

View File

@@ -1,62 +0,0 @@
from typing import Optional, cast
from mypy.checker import TypeChecker
from mypy.nodes import StrExpr, TypeInfo
from mypy.plugin import FunctionContext
from mypy.types import CallableType, Instance, Type
from mypy_django_plugin import helpers
from mypy_django_plugin.helpers import fill_typevars_with_any, reparametrize_with
def get_valid_to_value_or_none(ctx: FunctionContext) -> Optional[Instance]:
api = cast(TypeChecker, ctx.api)
if 'to' not in ctx.callee_arg_names:
# shouldn't happen, invalid code
api.msg.fail(f'to= parameter must be set for {ctx.context.callee.fullname}',
context=ctx.context)
return None
arg_type = ctx.arg_types[ctx.callee_arg_names.index('to')][0]
if not isinstance(arg_type, CallableType):
to_arg_expr = ctx.args[ctx.callee_arg_names.index('to')][0]
if not isinstance(to_arg_expr, StrExpr):
# not string, not supported
return None
try:
model_fullname = helpers.get_model_fullname_from_string(to_arg_expr.value,
all_modules=api.modules)
except helpers.SelfReference:
model_fullname = api.tscope.classes[-1].fullname()
if model_fullname is None:
return None
model_info = helpers.lookup_fully_qualified_generic(model_fullname,
all_modules=api.modules)
if model_info is None or not isinstance(model_info, TypeInfo):
return None
return Instance(model_info, [])
referred_to_type = arg_type.ret_type
if not isinstance(referred_to_type, Instance):
return None
if not referred_to_type.type.has_base(helpers.MODEL_CLASS_FULLNAME):
ctx.api.msg.fail(f'to= parameter value must be '
f'a subclass of {helpers.MODEL_CLASS_FULLNAME}',
context=ctx.context)
return None
return referred_to_type
def extract_to_parameter_as_get_ret_type_for_related_field(ctx: FunctionContext) -> Type:
try:
referred_to_type = get_valid_to_value_or_none(ctx)
except helpers.InvalidModelString as exc:
ctx.api.fail(f'Invalid value for a to= parameter: {exc.model_string!r}', ctx.context)
return fill_typevars_with_any(ctx.default_return_type)
if referred_to_type is None:
# couldn't extract to= value
return fill_typevars_with_any(ctx.default_return_type)
return reparametrize_with(ctx.default_return_type, [referred_to_type])

View File

@@ -0,0 +1,200 @@
from typing import Optional, cast
from mypy.checker import TypeChecker
from mypy.nodes import ListExpr, NameExpr, StrExpr, TupleExpr, TypeInfo, Var
from mypy.plugin import FunctionContext
from mypy.types import AnyType, CallableType, Instance, TupleType, Type, TypeOfAny, UnionType
from mypy_django_plugin import helpers
from mypy_django_plugin.transformers.models import iter_over_assignments
def extract_referred_to_type(ctx: FunctionContext) -> Optional[Instance]:
api = cast(TypeChecker, ctx.api)
if 'to' not in ctx.callee_arg_names:
api.msg.fail(f'to= parameter must be set for {ctx.context.callee.fullname}',
context=ctx.context)
return None
arg_type = ctx.arg_types[ctx.callee_arg_names.index('to')][0]
if not isinstance(arg_type, CallableType):
to_arg_expr = ctx.args[ctx.callee_arg_names.index('to')][0]
if not isinstance(to_arg_expr, StrExpr):
# not string, not supported
return None
try:
model_fullname = helpers.get_model_fullname_from_string(to_arg_expr.value,
all_modules=api.modules)
except helpers.SelfReference:
model_fullname = api.tscope.classes[-1].fullname()
except helpers.SameFileModel as exc:
model_fullname = api.tscope.classes[-1].module_name + '.' + exc.model_cls_name
if model_fullname is None:
return None
model_info = helpers.lookup_fully_qualified_generic(model_fullname,
all_modules=api.modules)
if model_info is None or not isinstance(model_info, TypeInfo):
return None
return Instance(model_info, [])
referred_to_type = arg_type.ret_type
if not isinstance(referred_to_type, Instance):
return None
if not referred_to_type.type.has_base(helpers.MODEL_CLASS_FULLNAME):
ctx.api.msg.fail(f'to= parameter value must be '
f'a subclass of {helpers.MODEL_CLASS_FULLNAME}',
context=ctx.context)
return None
return referred_to_type
def convert_any_to_type(typ: Type, referred_to_type: Type) -> Type:
if isinstance(typ, UnionType):
converted_items = []
for item in typ.items:
converted_items.append(convert_any_to_type(item, referred_to_type))
return UnionType.make_union(converted_items,
line=typ.line, column=typ.column)
if isinstance(typ, Instance):
args = []
for default_arg in typ.args:
if isinstance(default_arg, AnyType):
args.append(referred_to_type)
else:
args.append(default_arg)
return helpers.reparametrize_instance(typ, args)
if isinstance(typ, AnyType):
return referred_to_type
return typ
def fill_descriptor_types_for_related_field(ctx: FunctionContext) -> Type:
default_return_type = set_descriptor_types_for_field(ctx)
referred_to_type = extract_referred_to_type(ctx)
if referred_to_type is None:
return default_return_type
# replace Any with referred_to_type
args = []
for default_arg in default_return_type.args:
args.append(convert_any_to_type(default_arg, referred_to_type))
return helpers.reparametrize_instance(ctx.default_return_type, new_args=args)
def get_private_descriptor_type(type_info: TypeInfo, private_field_name: str, is_nullable: bool) -> Type:
node = type_info.get(private_field_name).node
if isinstance(node, Var):
descriptor_type = node.type
if is_nullable:
descriptor_type = helpers.make_optional(descriptor_type)
return descriptor_type
return AnyType(TypeOfAny.unannotated)
def set_descriptor_types_for_field(ctx: FunctionContext) -> Instance:
default_return_type = cast(Instance, ctx.default_return_type)
is_nullable = helpers.parse_bool(helpers.get_argument_by_name(ctx, 'null'))
if not is_nullable and default_return_type.type.has_base(helpers.CHAR_FIELD_FULLNAME):
# blank=True for CharField can be interpreted as null=True
is_nullable = helpers.parse_bool(helpers.get_argument_by_name(ctx, 'blank'))
set_type = get_private_descriptor_type(default_return_type.type, '_pyi_private_set_type',
is_nullable=is_nullable)
get_type = get_private_descriptor_type(default_return_type.type, '_pyi_private_get_type',
is_nullable=is_nullable)
return helpers.reparametrize_instance(default_return_type, [set_type, get_type])
def determine_type_of_array_field(ctx: FunctionContext) -> Type:
default_return_type = set_descriptor_types_for_field(ctx)
base_field_arg_type = helpers.get_argument_type_by_name(ctx, 'base_field')
if not base_field_arg_type or not isinstance(base_field_arg_type, Instance):
return default_return_type
base_type = base_field_arg_type.args[1] # extract __get__ type
args = []
for default_arg in default_return_type.args:
args.append(convert_any_to_type(default_arg, base_type))
return helpers.reparametrize_instance(default_return_type, args)
def transform_into_proper_return_type(ctx: FunctionContext) -> Type:
default_return_type = ctx.default_return_type
if not isinstance(default_return_type, Instance):
return default_return_type
if helpers.has_any_of_bases(default_return_type.type, (helpers.FOREIGN_KEY_FULLNAME,
helpers.ONETOONE_FIELD_FULLNAME,
helpers.MANYTOMANY_FIELD_FULLNAME)):
return fill_descriptor_types_for_related_field(ctx)
if default_return_type.type.has_base(helpers.ARRAY_FIELD_FULLNAME):
return determine_type_of_array_field(ctx)
return set_descriptor_types_for_field(ctx)
def adjust_return_type_of_field_instantiation(ctx: FunctionContext) -> Type:
record_field_properties_into_outer_model_class(ctx)
return transform_into_proper_return_type(ctx)
def record_field_properties_into_outer_model_class(ctx: FunctionContext) -> None:
api = cast(TypeChecker, ctx.api)
outer_model = api.scope.active_class()
if outer_model is None or not outer_model.has_base(helpers.MODEL_CLASS_FULLNAME):
# outside models.Model class, undetermined
return
field_name = None
for name_expr, stmt in iter_over_assignments(outer_model.defn):
if stmt == ctx.context and isinstance(name_expr, NameExpr):
field_name = name_expr.name
break
if field_name is None:
return
fields_metadata = outer_model.metadata.setdefault('django', {}).setdefault('fields', {})
# primary key
is_primary_key = False
primary_key_arg = helpers.get_argument_by_name(ctx, 'primary_key')
if primary_key_arg:
is_primary_key = helpers.parse_bool(primary_key_arg)
fields_metadata[field_name] = {'primary_key': is_primary_key}
# choices
choices_arg = helpers.get_argument_by_name(ctx, 'choices')
if choices_arg and isinstance(choices_arg, (TupleExpr, ListExpr)):
# iterable of 2 element tuples of two kinds
_, analyzed_choices = api.analyze_iterable_item_type(choices_arg)
if isinstance(analyzed_choices, TupleType):
first_element_type = analyzed_choices.items[0]
if isinstance(first_element_type, Instance):
fields_metadata[field_name]['choices'] = first_element_type.type.fullname()
# nullability
null_arg = helpers.get_argument_by_name(ctx, 'null')
is_nullable = False
if null_arg:
is_nullable = helpers.parse_bool(null_arg)
fields_metadata[field_name]['null'] = is_nullable
# is_blankable
blank_arg = helpers.get_argument_by_name(ctx, 'blank')
is_blankable = False
if blank_arg:
is_blankable = helpers.parse_bool(blank_arg)
fields_metadata[field_name]['blank'] = is_blankable
# default
default_arg = helpers.get_argument_by_name(ctx, 'default')
if default_arg and not helpers.is_none_expr(default_arg):
fields_metadata[field_name]['default_specified'] = True

View File

@@ -0,0 +1,9 @@
from mypy.plugin import ClassDefContext
from mypy_django_plugin import helpers
def make_meta_nested_class_inherit_from_any(ctx: ClassDefContext) -> None:
meta_node = helpers.get_nested_meta_node_for_current_class(ctx.cls.info)
if meta_node is None:
return None
meta_node.fallback_to_any = True

View File

@@ -3,10 +3,10 @@ from typing import Dict, Optional, Set, cast
from mypy.checker import TypeChecker from mypy.checker import TypeChecker
from mypy.nodes import TypeInfo, Var from mypy.nodes import TypeInfo, Var
from mypy.plugin import FunctionContext, MethodContext from mypy.plugin import FunctionContext, MethodContext
from mypy.types import AnyType, Instance, Type, TypeOfAny, UnionType from mypy.types import AnyType, Instance, Type, TypeOfAny
from mypy_django_plugin import helpers from mypy_django_plugin import helpers
from mypy_django_plugin.helpers import extract_field_setter_type, extract_primary_key_type_for_set, get_fields_metadata from mypy_django_plugin.helpers import extract_field_setter_type, extract_explicit_set_type_of_model_primary_key, get_fields_metadata
from mypy_django_plugin.transformers.fields import get_private_descriptor_type
def extract_base_pointer_args(model: TypeInfo) -> Set[str]: def extract_base_pointer_args(model: TypeInfo) -> Set[str]:
@@ -25,12 +25,13 @@ def redefine_and_typecheck_model_init(ctx: FunctionContext) -> Type:
api = cast(TypeChecker, ctx.api) api = cast(TypeChecker, ctx.api)
model: TypeInfo = ctx.default_return_type.type model: TypeInfo = ctx.default_return_type.type
expected_types = extract_expected_types(ctx, model) expected_types = extract_expected_types(ctx, model, is_init=True)
# order is preserved, can use for positionals
# order is preserved, can be used for positionals
positional_names = list(expected_types.keys()) positional_names = list(expected_types.keys())
positional_names.remove('pk') positional_names.remove('pk')
visited_positionals = set()
visited_positionals = set()
# check positionals # check positionals
for i, (_, actual_pos_type) in enumerate(zip(ctx.arg_names[0], ctx.arg_types[0])): for i, (_, actual_pos_type) in enumerate(zip(ctx.arg_names[0], ctx.arg_types[0])):
actual_pos_name = positional_names[i] actual_pos_name = positional_names[i]
@@ -111,42 +112,75 @@ def extract_choices_type(model: TypeInfo, field_name: str) -> Optional[str]:
return None return None
def extract_expected_types(ctx: FunctionContext, model: TypeInfo) -> Dict[str, Type]: def extract_expected_types(ctx: FunctionContext, model: TypeInfo,
expected_types: Dict[str, Type] = {} is_init: bool = False) -> Dict[str, Type]:
api = cast(TypeChecker, ctx.api)
primary_key_type = extract_primary_key_type_for_set(model) expected_types: Dict[str, Type] = {}
primary_key_type = extract_explicit_set_type_of_model_primary_key(model)
if not primary_key_type: if not primary_key_type:
# no explicit primary key, set pk to Any and add id # no explicit primary key, set pk to Any and add id
primary_key_type = AnyType(TypeOfAny.special_form) primary_key_type = AnyType(TypeOfAny.special_form)
expected_types['id'] = ctx.api.named_generic_type('builtins.int', []) if is_init:
expected_types['id'] = helpers.make_optional(ctx.api.named_generic_type('builtins.int', []))
else:
expected_types['id'] = ctx.api.named_generic_type('builtins.int', [])
expected_types['pk'] = primary_key_type expected_types['pk'] = primary_key_type
for base in model.mro: for base in model.mro:
# extract all fields for all models in MRO
for name, sym in base.names.items(): for name, sym in base.names.items():
# do not redefine special attrs # do not redefine special attrs
if name in {'_meta', 'pk'}: if name in {'_meta', 'pk'}:
continue continue
if isinstance(sym.node, Var): if isinstance(sym.node, Var):
if sym.node.type is None or isinstance(sym.node.type, AnyType): typ = sym.node.type
if typ is None or isinstance(typ, AnyType):
# types are not ready, fallback to Any # types are not ready, fallback to Any
expected_types[name] = AnyType(TypeOfAny.from_unimported_type) expected_types[name] = AnyType(TypeOfAny.from_unimported_type)
expected_types[name + '_id'] = AnyType(TypeOfAny.from_unimported_type) expected_types[name + '_id'] = AnyType(TypeOfAny.from_unimported_type)
elif isinstance(sym.node.type, Instance): elif isinstance(typ, Instance):
tp = sym.node.type field_type = extract_field_setter_type(typ)
field_type = extract_field_setter_type(tp)
if field_type is None: if field_type is None:
continue continue
if tp.type.fullname() in {helpers.FOREIGN_KEY_FULLNAME, helpers.ONETOONE_FIELD_FULLNAME}: if helpers.has_any_of_bases(typ.type, (helpers.FOREIGN_KEY_FULLNAME,
ref_to_model = tp.args[0] helpers.ONETOONE_FIELD_FULLNAME)):
primary_key_type = AnyType(TypeOfAny.special_form) related_primary_key_type = AnyType(TypeOfAny.implementation_artifact)
if isinstance(ref_to_model, Instance) and ref_to_model.type.has_base(helpers.MODEL_CLASS_FULLNAME): # in case it's optional, we need Instance type
typ = extract_primary_key_type_for_set(ref_to_model.type) referred_to_model = typ.args[1]
if typ: is_nullable = helpers.is_optional(referred_to_model)
primary_key_type = typ if is_nullable:
expected_types[name + '_id'] = primary_key_type referred_to_model = helpers.make_required(typ.args[1])
if isinstance(referred_to_model, Instance) and referred_to_model.type.has_base(helpers.MODEL_CLASS_FULLNAME):
pk_type = extract_explicit_set_type_of_model_primary_key(referred_to_model.type)
if not pk_type:
# extract set type of AutoField
autofield_info = api.lookup_typeinfo('django.db.models.fields.AutoField')
pk_type = get_private_descriptor_type(autofield_info, '_pyi_private_set_type',
is_nullable=is_nullable)
related_primary_key_type = pk_type
if is_init:
related_primary_key_type = helpers.make_optional(related_primary_key_type)
expected_types[name + '_id'] = related_primary_key_type
field_metadata = get_fields_metadata(model).get(name, {})
if field_type: if field_type:
# related fields could be None in __init__ (but should be specified before save())
if helpers.has_any_of_bases(typ.type, (helpers.FOREIGN_KEY_FULLNAME,
helpers.ONETOONE_FIELD_FULLNAME)) and is_init:
field_type = helpers.make_optional(field_type)
# if primary_key=True and default specified
elif field_metadata.get('primary_key', False) and field_metadata.get('default_specified', False):
field_type = helpers.make_optional(field_type)
expected_types[name] = field_type expected_types[name] = field_type
return expected_types return expected_types

View File

@@ -4,7 +4,6 @@ from mypy.checker import TypeChecker
from mypy.nodes import Expression, StrExpr, TypeInfo from mypy.nodes import Expression, StrExpr, TypeInfo
from mypy.plugin import MethodContext from mypy.plugin import MethodContext
from mypy.types import Instance, Type, TypeType from mypy.types import Instance, Type, TypeType
from mypy_django_plugin import helpers from mypy_django_plugin import helpers

View File

@@ -2,13 +2,12 @@ from abc import ABCMeta, abstractmethod
from typing import Dict, Iterator, List, Optional, Tuple, cast from typing import Dict, Iterator, List, Optional, Tuple, cast
import dataclasses import dataclasses
from mypy.nodes import ARG_STAR, ARG_STAR2, Argument, CallExpr, ClassDef, Context, Expression, IndexExpr, \ from mypy.nodes import ARG_STAR, ARG_STAR2, Argument, CallExpr, ClassDef, Expression, IndexExpr, \
Lvalue, MDEF, MemberExpr, MypyFile, NameExpr, StrExpr, SymbolTableNode, TypeInfo, Var Lvalue, MDEF, MemberExpr, MypyFile, NameExpr, StrExpr, SymbolTableNode, TypeInfo, Var
from mypy.plugin import ClassDefContext from mypy.plugin import ClassDefContext
from mypy.plugins.common import add_method from mypy.plugins.common import add_method
from mypy.semanal import SemanticAnalyzerPass2 from mypy.semanal import SemanticAnalyzerPass2
from mypy.types import AnyType, Instance, NoneTyp, TypeOfAny from mypy.types import AnyType, Instance, NoneTyp, TypeOfAny
from mypy_django_plugin import helpers from mypy_django_plugin import helpers
from mypy_django_plugin.helpers import iter_over_assignments from mypy_django_plugin.helpers import iter_over_assignments
@@ -22,14 +21,8 @@ class ModelClassInitializer(metaclass=ABCMeta):
def from_ctx(cls, ctx: ClassDefContext): def from_ctx(cls, ctx: ClassDefContext):
return cls(api=cast(SemanticAnalyzerPass2, ctx.api), model_classdef=ctx.cls) return cls(api=cast(SemanticAnalyzerPass2, ctx.api), model_classdef=ctx.cls)
def get_nested_meta_node(self) -> Optional[TypeInfo]:
metaclass_sym = self.model_classdef.info.names.get('Meta')
if metaclass_sym is not None and isinstance(metaclass_sym.node, TypeInfo):
return metaclass_sym.node
return None
def get_meta_attribute(self, name: str) -> Optional[Expression]: def get_meta_attribute(self, name: str) -> Optional[Expression]:
meta_node = self.get_nested_meta_node() meta_node = helpers.get_nested_meta_node_for_current_class(self.model_classdef.info)
if meta_node is None: if meta_node is None:
return None return None
@@ -74,8 +67,6 @@ def iter_over_one_to_n_related_fields(klass: ClassDef) -> Iterator[Tuple[NameExp
class SetIdAttrsForRelatedFields(ModelClassInitializer): class SetIdAttrsForRelatedFields(ModelClassInitializer):
def run(self) -> None: def run(self) -> None:
for lvalue, rvalue in iter_over_one_to_n_related_fields(self.model_classdef): for lvalue, rvalue in iter_over_one_to_n_related_fields(self.model_classdef):
# base_model_info = self.api.named_type('builtins.object').type
# helpers.get_related_field_primary_key_names(base_model_info).append(node_name)
node_name = lvalue.name + '_id' node_name = lvalue.name + '_id'
self.add_new_node_to_model_class(name=node_name, self.add_new_node_to_model_class(name=node_name,
typ=self.api.builtin_type('builtins.int')) typ=self.api.builtin_type('builtins.int'))
@@ -83,7 +74,7 @@ class SetIdAttrsForRelatedFields(ModelClassInitializer):
class InjectAnyAsBaseForNestedMeta(ModelClassInitializer): class InjectAnyAsBaseForNestedMeta(ModelClassInitializer):
def run(self) -> None: def run(self) -> None:
meta_node = self.get_nested_meta_node() meta_node = helpers.get_nested_meta_node_for_current_class(self.model_classdef.info)
if meta_node is None: if meta_node is None:
return None return None
meta_node.fallback_to_any = True meta_node.fallback_to_any = True
@@ -161,7 +152,7 @@ class AddIdAttributeIfPrimaryKeyTrueIsNotSet(ModelClassInitializer):
and self.api.parse_bool(rvalue.args[rvalue.arg_names.index('primary_key')])): and self.api.parse_bool(rvalue.args[rvalue.arg_names.index('primary_key')])):
break break
else: else:
self.add_new_node_to_model_class('id', self.api.builtin_type('builtins.int')) self.add_new_node_to_model_class('id', self.api.builtin_type('builtins.object'))
class AddRelatedManagers(ModelClassInitializer): class AddRelatedManagers(ModelClassInitializer):
@@ -176,10 +167,9 @@ class AddRelatedManagers(ModelClassInitializer):
all_modules=self.api.modules) all_modules=self.api.modules)
except helpers.SelfReference: except helpers.SelfReference:
ref_to_fullname = defn.fullname ref_to_fullname = defn.fullname
except helpers.InvalidModelString as exc:
self.api.fail(f'Invalid value for a to= parameter: {exc.model_string!r}', except helpers.SameFileModel as exc:
Context(line=rvalue.line)) ref_to_fullname = module_name + '.' + exc.model_cls_name
return None
if self.model_classdef.fullname == ref_to_fullname: if self.model_classdef.fullname == ref_to_fullname:
related_manager_name = defn.name.lower() + '_set' related_manager_name = defn.name.lower() + '_set'

View File

@@ -4,6 +4,5 @@ testpaths = ./test-data
addopts = addopts =
--tb=native --tb=native
--mypy-ini-file=./test-data/plugins.ini --mypy-ini-file=./test-data/plugins.ini
--mypy-no-cache
-s -s
-v -v

9
release.xsh Normal file → Executable file
View File

@@ -1,4 +1,9 @@
#!/usr/local/bin/xonsh #!/usr/local/bin/xonsh
python setup.py sdist try:
twine upload dist/* pip install wheel
python setup.py sdist bdist_wheel --universal
twine upload dist/*
finally:
rm -rf dist/ build/

View File

@@ -1,11 +1,12 @@
[mypy] [mypy]
strict_optional = False strict_optional = True
ignore_missing_imports = True ignore_missing_imports = True
check_untyped_defs = True check_untyped_defs = True
warn_no_return = False warn_no_return = False
show_traceback = True show_traceback = True
warn_redundant_casts = True warn_redundant_casts = True
allow_redefinition = True allow_redefinition = True
incremental = False
plugins = plugins =
mypy_django_plugin.main mypy_django_plugin.main

View File

@@ -15,7 +15,7 @@ PROJECT_DIRECTORY = Path(__file__).parent.parent
DJANGO_BRANCH = 'stable/2.1.x' DJANGO_BRANCH = 'stable/2.1.x'
# Specific commit in the Django repository to check against # Specific commit in the Django repository to check against
DJANGO_COMMIT_SHA = '03219b5f709dcd5b0bfacd963508625557ec1ef0' DJANGO_COMMIT_SHA = '8fe63dc4cd637c1422a9bf3e3421d64388e14afd'
# Some errors occur for the test suite itself, and cannot be addressed via django-stubs. They should be ignored # Some errors occur for the test suite itself, and cannot be addressed via django-stubs. They should be ignored
# using this constant. # using this constant.
@@ -32,7 +32,7 @@ IGNORED_ERRORS = {
'Cannot assign to a type', 'Cannot assign to a type',
re.compile(r'Cannot assign to class variable "[a-z_]+" via instance'), re.compile(r'Cannot assign to class variable "[a-z_]+" via instance'),
# forms <-> models plugin support # forms <-> models plugin support
'"Model" has no attribute', # '"Model" has no attribute',
re.compile(r'Cannot determine type of \'(objects|stuff)\''), re.compile(r'Cannot determine type of \'(objects|stuff)\''),
# settings # settings
re.compile(r'Module has no attribute "[A-Z_]+"'), re.compile(r'Module has no attribute "[A-Z_]+"'),
@@ -51,7 +51,11 @@ IGNORED_ERRORS = {
# private members # private members
re.compile(r'has no attribute "|\'_[a-z][a-z_]+"|\''), re.compile(r'has no attribute "|\'_[a-z][a-z_]+"|\''),
'Invalid base class', 'Invalid base class',
'ValuesIterable' 'ValuesIterable',
'Value of type "Optional[Dict[str, Any]]" is not indexable',
'Argument 1 to "len" has incompatible type "Optional[List[_Record]]"; expected "Sized"',
'Argument 1 to "loads" has incompatible type "Union[bytes, str, None]"; expected "Union[str, bytes, bytearray]"',
'Incompatible types in assignment (expression has type "None", variable has type Module)'
], ],
'admin_changelist': [ 'admin_changelist': [
'Incompatible types in assignment (expression has type "FilteredChildAdmin", variable has type "ChildAdmin")' 'Incompatible types in assignment (expression has type "FilteredChildAdmin", variable has type "ChildAdmin")'
@@ -62,7 +66,7 @@ IGNORED_ERRORS = {
'admin_widgets': [ 'admin_widgets': [
'Incompatible types in assignment (expression has type "RelatedFieldWidgetWrapper", ' 'Incompatible types in assignment (expression has type "RelatedFieldWidgetWrapper", '
'variable has type "AdminRadioSelect")', 'variable has type "AdminRadioSelect")',
'Incompatible types in assignment (expression has type "Widget", variable has type "AutocompleteSelect")' 'Incompatible types in assignment (expression has type "Union[Widget, Any]", variable has type "AutocompleteSelect")'
], ],
'admin_utils': [ 'admin_utils': [
re.compile(r'Argument [0-9] to "lookup_field" has incompatible type'), re.compile(r'Argument [0-9] to "lookup_field" has incompatible type'),
@@ -78,13 +82,16 @@ IGNORED_ERRORS = {
'"object" not callable', '"object" not callable',
'Incompatible type for "pk" of "Collector" (got "int", expected "str")', 'Incompatible type for "pk" of "Collector" (got "int", expected "str")',
re.compile('Unexpected attribute "[a-z]+" for model "Model"'), re.compile('Unexpected attribute "[a-z]+" for model "Model"'),
'Unexpected attribute "two_id" for model "CyclicOne"' 'Unexpected attribute "two_id" for model "CyclicOne"',
'Argument "is_dst" to "localize" of "BaseTzInfo" has incompatible type "None"; expected "bool"'
], ],
'aggregation': [ 'aggregation': [
'Incompatible types in assignment (expression has type "QuerySet[Any]", variable has type "List[Any]")', 'Incompatible types in assignment (expression has type "QuerySet[Any]", variable has type "List[Any]")',
'"as_sql" undefined in superclass', '"as_sql" undefined in superclass',
'Incompatible types in assignment (expression has type "FlatValuesListIterable", ' 'Incompatible types in assignment (expression has type "FlatValuesListIterable", '
+ 'variable has type "ValuesListIterable")' + 'variable has type "ValuesListIterable")',
'Incompatible type for "contact" of "Book" (got "Optional[Author]", expected "Union[Author, Combinable]")',
'Incompatible type for "publisher" of "Book" (got "Optional[Publisher]", expected "Union[Publisher, Combinable]")'
], ],
'aggregation_regress': [ 'aggregation_regress': [
'Incompatible types in assignment (expression has type "List[str]", variable has type "QuerySet[Author]")', 'Incompatible types in assignment (expression has type "List[str]", variable has type "QuerySet[Author]")',
@@ -95,6 +102,9 @@ IGNORED_ERRORS = {
'Incompatible types in assignment (expression has type "str", target has type "type")', 'Incompatible types in assignment (expression has type "str", target has type "type")',
'"Callable[[bool, bool], List[Type[Model]]]" has no attribute "cache_clear"' '"Callable[[bool, bool], List[Type[Model]]]" has no attribute "cache_clear"'
], ],
'annotations': [
'Incompatible type for "store" of "Employee" (got "Optional[Store]", expected "Union[Store, Combinable]")'
],
'auth_tests': [ 'auth_tests': [
'"PasswordValidator" has no attribute "min_length"', '"PasswordValidator" has no attribute "min_length"',
'"validate_password" does not return a value', '"validate_password" does not return a value',
@@ -157,6 +167,29 @@ IGNORED_ERRORS = {
'fixtures': [ 'fixtures': [
'Incompatible types in assignment (expression has type "int", target has type "Iterable[str]")' 'Incompatible types in assignment (expression has type "int", target has type "Iterable[str]")'
], ],
'forms_tests': [
'List item 0 has incompatible type "Jinja2"; expected "DjangoTemplates"',
'Not enough arguments for format string',
'Argument after ** must be a mapping, not "object"',
'"media" undefined in superclass',
'expression has type "None", base class "TestFormParent"',
'variable has type "SongForm"',
'"full_clean" of "BaseForm" does not return a value',
'No overload variant of "zip" matches argument types "Tuple[str, str, str]", "object"',
'note:',
'Incompatible types in assignment (expression has type "GetDateShowHiddenInitial", variable has type "GetDate")',
re.compile(r'Incompatible types in assignment \(expression has type "[a-zA-Z]+Field", '
r'base class "BaseForm" defined the type as "Dict\[str, Any\]"\)'),
'List or tuple expected as variable arguments',
'Argument 1 to "__init__" of "MultiWidget" has incompatible type "List[object]"; '
+ 'expected "Sequence[Union[Widget, Type[Widget]]]"',
'Argument 1 to "issubclass" has incompatible type "ModelFormMetaclass"; expected "type"',
'Incompatible types in assignment (expression has type "List[str]", target has type "str")',
'Incompatible types in assignment (expression has type "TestForm", variable has type "Person")',
'Incompatible types in assignment (expression has type "Type[Textarea]", '
+ 'base class "Field" defined the type as "Widget")',
'Incompatible types in assignment (expression has type "SimpleUploadedFile", variable has type "BinaryIO")'
],
'get_object_or_404': [ 'get_object_or_404': [
'Argument 1 to "get_object_or_404" has incompatible type "str"; ' 'Argument 1 to "get_object_or_404" has incompatible type "str"; '
+ 'expected "Union[Type[<nothing>], Manager[<nothing>], QuerySet[<nothing>]]"', + 'expected "Union[Type[<nothing>], Manager[<nothing>], QuerySet[<nothing>]]"',
@@ -165,11 +198,15 @@ IGNORED_ERRORS = {
'CustomClass' 'CustomClass'
], ],
'get_or_create': [ 'get_or_create': [
'Argument 1 to "update_or_create" of "QuerySet" has incompatible type "**Dict[str, object]"; expected "MutableMapping[str, Any]"' 'Argument 1 to "update_or_create" of "QuerySet" has incompatible type "**Dict[str, object]"; '
+ 'expected "Optional[MutableMapping[str, Any]]"'
], ],
'httpwrappers': [ 'httpwrappers': [
'Argument 2 to "appendlist" of "QueryDict" has incompatible type "List[str]"; expected "str"' 'Argument 2 to "appendlist" of "QueryDict" has incompatible type "List[str]"; expected "str"'
], ],
'humanize_tests': [
'Argument 1 to "append" of "list" has incompatible type "None"; expected "str"'
],
'invalid_models_tests': [ 'invalid_models_tests': [
'Argument "max_length" to "CharField" has incompatible type "str"; expected "Optional[int]"', 'Argument "max_length" to "CharField" has incompatible type "str"; expected "Optional[int]"',
'Argument "choices" to "CharField" has incompatible type "str"' 'Argument "choices" to "CharField" has incompatible type "str"'
@@ -177,6 +214,9 @@ IGNORED_ERRORS = {
'logging_tests': [ 'logging_tests': [
re.compile('"(setUpClass|tearDownClass)" undefined in superclass') re.compile('"(setUpClass|tearDownClass)" undefined in superclass')
], ],
'many_to_one': [
'Incompatible type for "parent" of "Child" (got "None", expected "Union[Parent, Combinable]")'
],
'model_inheritance_regress': [ 'model_inheritance_regress': [
'Incompatible types in assignment (expression has type "List[Supplier]", variable has type "QuerySet[Supplier]")' 'Incompatible types in assignment (expression has type "List[Supplier]", variable has type "QuerySet[Supplier]")'
], ],
@@ -189,7 +229,14 @@ IGNORED_ERRORS = {
'Unexpected keyword argument "name" for "Person"', 'Unexpected keyword argument "name" for "Person"',
'Cannot assign multiple types to name "PersonTwoImages" without an explicit "Type[...]" annotation', 'Cannot assign multiple types to name "PersonTwoImages" without an explicit "Type[...]" annotation',
'Incompatible types in assignment (expression has type "Type[Person]", ' 'Incompatible types in assignment (expression has type "Type[Person]", '
+ 'base class "ImageFieldTestMixin" defined the type as "Type[PersonWithHeightAndWidth]")' + 'base class "ImageFieldTestMixin" defined the type as "Type[PersonWithHeightAndWidth]")',
'note: "Person" defined here'
],
'model_formsets': [
'Incompatible types in string interpolation (expression has type "object", placeholder has type "Union[int, float]")'
],
'model_formsets_regress': [
'Incompatible types in assignment (expression has type "Model", variable has type "User")'
], ],
'model_regress': [ 'model_regress': [
'Too many arguments for "Worker"', 'Too many arguments for "Worker"',
@@ -203,6 +250,7 @@ IGNORED_ERRORS = {
], ],
'migrate_signals': [ 'migrate_signals': [
'Value of type "None" is not indexable', 'Value of type "None" is not indexable',
'Argument 1 to "set" has incompatible type "None"; expected "Iterable[<nothing>]"'
], ],
'migrations': [ 'migrations': [
'FakeMigration', 'FakeMigration',
@@ -213,7 +261,8 @@ IGNORED_ERRORS = {
+ 'expected "Optional[Sequence[Union[Type[Model], str]]]"', + 'expected "Optional[Sequence[Union[Type[Model], str]]]"',
'Argument 1 to "RunPython" has incompatible type "str"; expected "Callable[..., Any]"', 'Argument 1 to "RunPython" has incompatible type "str"; expected "Callable[..., Any]"',
'FakeLoader', 'FakeLoader',
'Argument 1 to "append" of "list" has incompatible type "AddIndex"; expected "CreateModel"' 'Argument 1 to "append" of "list" has incompatible type "AddIndex"; expected "CreateModel"',
'Unsupported operand types for - ("Set[Any]" and "None")'
], ],
'middleware_exceptions': [ 'middleware_exceptions': [
'Argument 1 to "append" of "list" has incompatible type "Tuple[Any, Any]"; expected "str"' 'Argument 1 to "append" of "list" has incompatible type "Tuple[Any, Any]"; expected "str"'
@@ -227,15 +276,20 @@ IGNORED_ERRORS = {
], ],
'postgres_tests': [ 'postgres_tests': [
'Cannot assign multiple types to name', 'Cannot assign multiple types to name',
'Incompatible types in assignment (expression has type "Type[Field]', 'Incompatible types in assignment (expression has type "Type[Field[Any, Any]]',
'DummyArrayField', 'DummyArrayField',
'DummyJSONField', 'DummyJSONField',
'Argument "encoder" to "JSONField" has incompatible type "DjangoJSONEncoder"; expected "Optional[Type[JSONEncoder]]"', 'Argument "encoder" to "JSONField" has incompatible type "DjangoJSONEncoder"; expected "Optional[Type[JSONEncoder]]"',
'for model "CITestModel"' 'for model "CITestModel"',
'Incompatible type for "field" of "IntegerArrayModel" (got "None", expected "Union[Sequence[int], Combinable]")'
], ],
'properties': [ 'properties': [
re.compile('Unexpected attribute "(full_name|full_name_2)" for model "Person"') re.compile('Unexpected attribute "(full_name|full_name_2)" for model "Person"')
], ],
'queries': [
'Incompatible types in assignment (expression has type "None", variable has type "str")',
'Invalid index type "Optional[str]" for "Dict[str, int]"; expected type "str"'
],
'requests': [ 'requests': [
'Incompatible types in assignment (expression has type "Dict[str, str]", variable has type "QueryDict")' 'Incompatible types in assignment (expression has type "Dict[str, str]", variable has type "QueryDict")'
], ],
@@ -248,7 +302,8 @@ IGNORED_ERRORS = {
'has no attribute "read_by"' 'has no attribute "read_by"'
], ],
'signals': [ 'signals': [
'Argument 1 to "append" of "list" has incompatible type "Tuple[Any, Any, Any, Any]"; expected "Tuple[Any, Any, Any]"' 'Argument 1 to "append" of "list" has incompatible type "Tuple[Any, Any, Optional[Any], Any]"; '
+ 'expected "Tuple[Any, Any, Any]"'
], ],
'syndication_tests': [ 'syndication_tests': [
'List or tuple expected as variable arguments' 'List or tuple expected as variable arguments'
@@ -259,6 +314,14 @@ IGNORED_ERRORS = {
'Argument 1 to "write" of "IO" has incompatible type "bytes"; expected "str"', 'Argument 1 to "write" of "IO" has incompatible type "bytes"; expected "str"',
'Value of type "object" is not indexable' 'Value of type "object" is not indexable'
], ],
'schema': [
'Incompatible type for "info" of "Note" (got "None", expected "Union[str, Combinable]")',
'Incompatible type for "detail_info" of "NoteRename" (got "None", expected "Union[str, Combinable]")',
'Incompatible type for "year" of "UniqueTest" (got "None", expected "Union[float, int, str, Combinable]")'
],
'settings_tests': [
'Argument 1 to "Settings" has incompatible type "Optional[str]"; expected "str"'
],
'transactions': [ 'transactions': [
'Incompatible types in assignment (expression has type "Thread", variable has type "Callable[[], Any]")' 'Incompatible types in assignment (expression has type "Thread", variable has type "Callable[[], Any]")'
], ],
@@ -267,7 +330,9 @@ IGNORED_ERRORS = {
'Incompatible types in assignment (expression has type "HttpResponse", variable has type "StreamingHttpResponse")' 'Incompatible types in assignment (expression has type "HttpResponse", variable has type "StreamingHttpResponse")'
], ],
'test_client_regress': [ 'test_client_regress': [
'Incompatible types in assignment (expression has type "Dict[<nothing>, <nothing>]", variable has type "SessionBase")' 'Incompatible types in assignment (expression has type "Dict[<nothing>, <nothing>]", variable has type "SessionBase")',
'Unsupported left operand type for + ("None")',
'Both left and right operands are unions'
], ],
'timezones': [ 'timezones': [
'Too few arguments for "render" of "Template"' 'Too few arguments for "render" of "Template"'
@@ -289,11 +354,12 @@ IGNORED_ERRORS = {
'template_debug', 'template_debug',
'"yield from" can\'t be applied to', '"yield from" can\'t be applied to',
re.compile(r'List item [0-9] has incompatible type "URLResolver"; expected "URLPattern"'), re.compile(r'List item [0-9] has incompatible type "URLResolver"; expected "URLPattern"'),
'"WSGIRequest" has no attribute "current_app"' '"WSGIRequest" has no attribute "current_app"',
'Value of type variable "AnyStr" of "urljoin" cannot be "Optional[str]"'
], ],
'template_backends': [ 'template_backends': [
'Incompatible import of "Jinja2" (imported name has type "Type[Jinja2]", local name has type "object")', 'Incompatible import of "Jinja2" (imported name has type "Type[Jinja2]", local name has type "object")',
'TemplateStringsTests' 'TemplateStringsTests',
], ],
'urlpatterns': [ 'urlpatterns': [
'"object" has no attribute "__iter__"; maybe "__str__" or "__dir__"? (not iterable)', '"object" has no attribute "__iter__"; maybe "__str__" or "__dir__"? (not iterable)',
@@ -311,7 +377,8 @@ IGNORED_ERRORS = {
"'django.urls.resolvers.ResolverMatch' object is not iterable" "'django.urls.resolvers.ResolverMatch' object is not iterable"
], ],
'sessions_tests': [ 'sessions_tests': [
'base class "SessionTestsMixin" defined the type as "None")' 'base class "SessionTestsMixin" defined the type as "None")',
'Incompatible types in assignment (expression has type "None", variable has type "int")',
], ],
'select_related_onetoone': [ 'select_related_onetoone': [
'"None" has no attribute' '"None" has no attribute'
@@ -402,7 +469,7 @@ TESTS_DIRS = [
'flatpages_tests', 'flatpages_tests',
'force_insert_update', 'force_insert_update',
'foreign_object', 'foreign_object',
# TODO: 'forms_tests', 'forms_tests',
'from_db_value', 'from_db_value',
'generic_inline_admin', 'generic_inline_admin',
'generic_relations', 'generic_relations',

View File

@@ -31,12 +31,12 @@ if sys.version_info[:2] < (3, 7):
setup( setup(
name="django-stubs", name="django-stubs",
version="0.5.0", version="0.7.0",
description='Django mypy stubs', description='Django mypy stubs',
long_description=readme, long_description=readme,
long_description_content_type='text/markdown', long_description_content_type='text/markdown',
license='BSD', license='MIT',
url="https://github.com/mkurnikov/django-stubs.git", url="https://github.com/mkurnikov/django-stubs",
author="Maksim Kurnikov", author="Maksim Kurnikov",
author_email="maxim.kurnikov@gmail.com", author_email="maxim.kurnikov@gmail.com",
py_modules=[], py_modules=[],
@@ -46,6 +46,8 @@ setup(
package_data={'django-stubs': find_stub_files('django-stubs')}, package_data={'django-stubs': find_stub_files('django-stubs')},
classifiers=[ classifiers=[
'Development Status :: 3 - Alpha', 'Development Status :: 3 - Alpha',
'Programming Language :: Python :: 3' 'License :: OSI Approved :: MIT License',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7'
] ]
) )

View File

@@ -1,4 +1,5 @@
[mypy] [mypy]
incremental = False incremental = True
strict_optional = True
plugins = plugins =
mypy_django_plugin.main mypy_django_plugin.main

View File

@@ -1,28 +1,30 @@
[CASE missing_settings_ignored_flag] [CASE missing_settings_ignored_flag]
[env MYPY_DJANGO_CONFIG=${MYPY_CWD}/mypy_django.ini]
[disable_cache]
from django.conf import settings from django.conf import settings
reveal_type(settings.NO_SUCH_SETTING) # E: Revealed type is 'Any' reveal_type(settings.NO_SUCH_SETTING) # E: Revealed type is 'Any'
[env MYPY_DJANGO_CONFIG=${MYPY_CWD}/mypy_django.ini]
[file mypy_django.ini] [file mypy_django.ini]
[[mypy_django_plugin] [[mypy_django_plugin]
ignore_missing_settings = True ignore_missing_settings = True
[out] [/CASE]
[CASE django_settings_via_config_file] [CASE django_settings_via_config_file]
[env MYPY_DJANGO_CONFIG=${MYPY_CWD}/mypy_django.ini]
[disable_cache]
from django.conf import settings from django.conf import settings
reveal_type(settings.MY_SETTING) # E: Revealed type is 'builtins.int' reveal_type(settings.MY_SETTING) # E: Revealed type is 'builtins.int'
[env MYPY_DJANGO_CONFIG=${MYPY_CWD}/mypy_django.ini]
[file mypy_django.ini] [file mypy_django.ini]
[[mypy_django_plugin] [[mypy_django_plugin]
django_settings = mysettings django_settings = mysettings
[file mysettings.py] [file mysettings.py]
MY_SETTING: int = 1 MY_SETTING: int = 1
[out] [/CASE]
[CASE mypy_django_ini_in_current_directory_is_a_default] [CASE mypy_django_ini_in_current_directory_is_a_default]
[disable_cache]
from django.conf import settings from django.conf import settings
reveal_type(settings.MY_SETTING) # E: Revealed type is 'builtins.int' reveal_type(settings.MY_SETTING) # E: Revealed type is 'builtins.int'
@@ -32,4 +34,4 @@ django_settings = mysettings
[file mysettings.py] [file mysettings.py]
MY_SETTING: int = 1 MY_SETTING: int = 1
[out] [/CASE]

View File

@@ -6,7 +6,8 @@ class User(models.Model):
array = ArrayField(base_field=models.Field()) array = ArrayField(base_field=models.Field())
user = User() user = User()
reveal_type(user.array) # E: Revealed type is 'builtins.list[Any]' reveal_type(user.array) # E: Revealed type is 'builtins.list*[Any]'
[/CASE]
[CASE array_field_base_field_parsed_into_generic_typevar] [CASE array_field_base_field_parsed_into_generic_typevar]
from django.db import models from django.db import models
@@ -17,8 +18,9 @@ class User(models.Model):
members_as_text = ArrayField(base_field=models.CharField(max_length=255)) members_as_text = ArrayField(base_field=models.CharField(max_length=255))
user = User() user = User()
reveal_type(user.members) # E: Revealed type is 'builtins.list[builtins.int*]' reveal_type(user.members) # E: Revealed type is 'builtins.list*[builtins.int]'
reveal_type(user.members_as_text) # E: Revealed type is 'builtins.list[builtins.str*]' reveal_type(user.members_as_text) # E: Revealed type is 'builtins.list*[builtins.str]'
[/CASE]
[CASE test_model_fields_classes_present_as_primitives] [CASE test_model_fields_classes_present_as_primitives]
from django.db import models from django.db import models
@@ -31,13 +33,14 @@ class User(models.Model):
text = models.TextField() text = models.TextField()
user = User() user = User()
reveal_type(user.id) # E: Revealed type is 'builtins.int' reveal_type(user.id) # E: Revealed type is 'builtins.int*'
reveal_type(user.small_int) # E: Revealed type is 'builtins.int' reveal_type(user.small_int) # E: Revealed type is 'builtins.int*'
reveal_type(user.name) # E: Revealed type is 'builtins.str' reveal_type(user.name) # E: Revealed type is 'builtins.str*'
reveal_type(user.slug) # E: Revealed type is 'builtins.str' reveal_type(user.slug) # E: Revealed type is 'builtins.str*'
reveal_type(user.text) # E: Revealed type is 'builtins.str' reveal_type(user.text) # E: Revealed type is 'builtins.str*'
[/CASE]
[CASE test_model_field_classes_from_exciting_locations] [CASE test_model_field_classes_from_existing_locations]
from django.db import models from django.db import models
from django.contrib.postgres import fields as pg_fields from django.contrib.postgres import fields as pg_fields
from decimal import Decimal from decimal import Decimal
@@ -48,9 +51,10 @@ class Booking(models.Model):
some_decimal = models.DecimalField(max_digits=10, decimal_places=5) some_decimal = models.DecimalField(max_digits=10, decimal_places=5)
booking = Booking() booking = Booking()
reveal_type(booking.id) # E: Revealed type is 'builtins.int' reveal_type(booking.id) # E: Revealed type is 'builtins.int*'
reveal_type(booking.time_range) # E: Revealed type is 'Any' reveal_type(booking.time_range) # E: Revealed type is 'Any'
reveal_type(booking.some_decimal) # E: Revealed type is 'decimal.Decimal' reveal_type(booking.some_decimal) # E: Revealed type is 'decimal.Decimal*'
[/CASE]
[CASE test_add_id_field_if_no_primary_key_defined] [CASE test_add_id_field_if_no_primary_key_defined]
from django.db import models from django.db import models
@@ -59,6 +63,7 @@ class User(models.Model):
pass pass
reveal_type(User().id) # E: Revealed type is 'builtins.int' reveal_type(User().id) # E: Revealed type is 'builtins.int'
[/CASE]
[CASE test_do_not_add_id_if_field_with_primary_key_True_defined] [CASE test_do_not_add_id_if_field_with_primary_key_True_defined]
from django.db import models from django.db import models
@@ -66,9 +71,9 @@ from django.db import models
class User(models.Model): class User(models.Model):
my_pk = models.IntegerField(primary_key=True) my_pk = models.IntegerField(primary_key=True)
reveal_type(User().my_pk) # E: Revealed type is 'builtins.int' reveal_type(User().my_pk) # E: Revealed type is 'builtins.int*'
reveal_type(User().id) # E: Revealed type is 'Any' reveal_type(User().id) # E: Revealed type is 'Any'
[out] [/CASE]
[CASE test_meta_nested_class_allows_subclassing_in_multiple_inheritance] [CASE test_meta_nested_class_allows_subclassing_in_multiple_inheritance]
from typing import Any from typing import Any
@@ -84,7 +89,7 @@ class Mixin2(models.Model):
class User(Mixin1, Mixin2): class User(Mixin1, Mixin2):
pass pass
[out] [/CASE]
[CASE test_inheritance_from_abstract_model_does_not_fail_if_field_with_id_exists] [CASE test_inheritance_from_abstract_model_does_not_fail_if_field_with_id_exists]
from django.db import models from django.db import models
@@ -93,4 +98,21 @@ class Abstract(models.Model):
abstract = True abstract = True
class User(Abstract): class User(Abstract):
id = models.AutoField(primary_key=True) id = models.AutoField(primary_key=True)
[out] [/CASE]
[CASE standard_it_from_parent_model_could_be_overridden_with_non_integer_field_in_child_model]
from django.db import models
import uuid
class ParentModel(models.Model):
pass
class MyModel(ParentModel):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
reveal_type(MyModel().id) # E: Revealed type is 'uuid.UUID*'
[/CASE]
[CASE blank_for_charfield_is_the_same_as_null]
from django.db import models
class MyModel(models.Model):
text = models.CharField(max_length=30, blank=True)
MyModel(text=None)
[/CASE]

View File

@@ -0,0 +1,19 @@
[CASE no_incompatible_meta_nested_class_false_positive]
from django.db import models
from django import forms
class Article(models.Model):
pass
class Category(models.Model):
pass
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
fields = '__all__'
class CategoryForm(forms.ModelForm):
class Meta:
model = Category
fields = '__all__'
class CompositeForm(ArticleForm, CategoryForm):
pass
[out]

View File

@@ -6,7 +6,7 @@ class User(models.Model):
age = models.IntegerField() age = models.IntegerField()
User.objects.create(name='Max', age=10) User.objects.create(name='Max', age=10)
User.objects.create(age='hello') # E: Incompatible type for "age" of "User" (got "str", expected "Union[int, Combinable, Literal['']]") User.objects.create(age=[]) # E: Incompatible type for "age" of "User" (got "List[Any]", expected "Union[float, int, str, Combinable]")
[out] [out]
[CASE model_recognises_parent_attributes] [CASE model_recognises_parent_attributes]
@@ -31,4 +31,34 @@ class Child1(Parent1, Parent2):
value = models.IntegerField() value = models.IntegerField()
class Child4(Child1): class Child4(Child1):
value4 = models.IntegerField() value4 = models.IntegerField()
Child4.objects.create(name1='n1', name2='n2', value=1, value4=4) Child4.objects.create(name1='n1', name2='n2', value=1, value4=4)
[out]
[CASE optional_primary_key_for_create_is_error]
from django.db import models
class MyModel(models.Model):
pass
MyModel.objects.create(id=None) # E: Incompatible type for "id" of "MyModel" (got "None", expected "int")
[CASE optional_related_model_for_create_is_error]
from django.db import models
class Publisher(models.Model):
pass
class Book(models.Model):
publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)
Book.objects.create(publisher=None) # E: Incompatible type for "publisher" of "Book" (got "None", expected "Union[Publisher, Combinable]")
[CASE when_default_for_primary_key_is_specified_allow_none_to_be_set]
from django.db import models
def return_int():
return 0
class MyModel(models.Model):
id = models.IntegerField(primary_key=True, default=return_int)
MyModel(id=None)
MyModel.objects.create(id=None)
class MyModel2(models.Model):
id = models.IntegerField(primary_key=True, default=None)
MyModel2(id=None) # E: Incompatible type for "id" of "MyModel2" (got "None", expected "Union[float, int, str, Combinable]")
MyModel2.objects.create(id=None) # E: Incompatible type for "id" of "MyModel2" (got "None", expected "Union[float, int, str, Combinable]")
[out]

View File

@@ -14,9 +14,9 @@ from django.db import models
class MyUser(models.Model): class MyUser(models.Model):
name = models.CharField(max_length=100) name = models.CharField(max_length=100)
age = models.IntegerField() age = models.IntegerField()
user = MyUser(name='hello', age='world') user = MyUser(name='hello', age=[])
[out] [out]
main:6: error: Incompatible type for "age" of "MyUser" (got "str", expected "Union[int, Combinable, Literal['']]") main:6: error: Incompatible type for "age" of "MyUser" (got "List[Any]", expected "Union[float, int, str, Combinable]")
[CASE arguments_to_init_combined_from_base_classes] [CASE arguments_to_init_combined_from_base_classes]
from django.db import models from django.db import models
@@ -63,7 +63,7 @@ from django.db import models
class MyUser1(models.Model): class MyUser1(models.Model):
mypk = models.IntegerField(primary_key=True) mypk = models.IntegerField(primary_key=True)
user = MyUser1(pk='hello') # E: Incompatible type for "pk" of "MyUser1" (got "str", expected "Union[int, Combinable, Literal['']]") user = MyUser1(pk=[]) # E: Incompatible type for "pk" of "MyUser1" (got "List[Any]", expected "Union[float, int, str, Combinable]")
[out] [out]
[CASE can_set_foreign_key_by_its_primary_key] [CASE can_set_foreign_key_by_its_primary_key]
@@ -71,14 +71,15 @@ from django.db import models
class Publisher(models.Model): class Publisher(models.Model):
pass pass
class PublisherWithCharPK(models.Model): class PublisherDatetime(models.Model):
id = models.IntegerField(primary_key=True) dt_pk = models.DateTimeField(primary_key=True)
class Book(models.Model): class Book(models.Model):
publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE) publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)
publisher_with_char_pk = models.ForeignKey(PublisherWithCharPK, on_delete=models.CASCADE) publisher_dt = models.ForeignKey(PublisherDatetime, on_delete=models.CASCADE)
Book(publisher_id=1, publisher_with_char_pk_id=1) Book(publisher_id=1)
Book(publisher_id=1, publisher_with_char_pk_id='hello') # E: Incompatible type for "publisher_with_char_pk_id" of "Book" (got "str", expected "Union[int, Combinable, Literal['']]") Book(publisher_id=[]) # E: Incompatible type for "publisher_id" of "Book" (got "List[Any]", expected "Union[Combinable, int, str, None]")
Book(publisher_dt_id=11) # E: Incompatible type for "publisher_dt_id" of "Book" (got "int", expected "Union[str, date, Combinable, None]")
[out] [out]
[CASE setting_value_to_an_array_of_ints] [CASE setting_value_to_an_array_of_ints]
@@ -94,7 +95,7 @@ MyModel(array=array_val)
array_val2: List[int] = [1] array_val2: List[int] = [1]
MyModel(array=array_val2) MyModel(array=array_val2)
array_val3: List[str] = ['hello'] array_val3: List[str] = ['hello']
MyModel(array=array_val3) # E: Incompatible type for "array" of "MyModel" (got "List[str]", expected "Sequence[int]") MyModel(array=array_val3) # E: Incompatible type for "array" of "MyModel" (got "List[str]", expected "Union[Sequence[int], Combinable]")
[out] [out]
[CASE if_no_explicit_primary_key_id_can_be_passed] [CASE if_no_explicit_primary_key_id_can_be_passed]
@@ -114,7 +115,7 @@ MyModel(1)
class MyModel2(models.Model): class MyModel2(models.Model):
name = models.IntegerField() name = models.IntegerField()
MyModel2(1, 12) MyModel2(1, 12)
MyModel2(1, 'Maxim') # E: Incompatible type for "name" of "MyModel2" (got "str", expected "Union[int, Combinable, Literal['']]") MyModel2(1, []) # E: Incompatible type for "name" of "MyModel2" (got "List[Any]", expected "Union[float, int, str, Combinable]")
[out] [out]
[CASE arguments_passed_as_dictionary_unpacking_are_not_supported] [CASE arguments_passed_as_dictionary_unpacking_are_not_supported]
@@ -135,20 +136,6 @@ Restaurant(place_ptr=place)
Restaurant(place_ptr_id=place.id) Restaurant(place_ptr_id=place.id)
[out] [out]
[CASE extract_type_of_init_param_from_set_method]
from typing import Union
from datetime import time
from django.db import models
class MyField(models.Field):
def __set__(self, instance, value: Union[str, time]) -> None: pass
def __get__(self, instance, owner) -> time: pass
class MyModel(models.Model):
field = MyField()
MyModel(field=time())
MyModel(field='12:00')
MyModel(field=100) # E: Incompatible type for "field" of "MyModel" (got "int", expected "Union[str, time]")
[CASE charfield_with_integer_choices] [CASE charfield_with_integer_choices]
from django.db import models from django.db import models
class MyModel(models.Model): class MyModel(models.Model):
@@ -171,3 +158,20 @@ InvoiceRow.objects.create(base_amount=Decimal(0), vat_rate=Decimal(0))
main:3: error: Cannot find module named 'fields2' main:3: error: Cannot find module named 'fields2'
main:3: note: See https://mypy.readthedocs.io/en/latest/running_mypy.html#missing-imports main:3: note: See https://mypy.readthedocs.io/en/latest/running_mypy.html#missing-imports
[CASE optional_primary_key_is_allowed_for_init]
from django.db import models
class MyModel(models.Model):
pass
MyModel(id=None)
MyModel(None)
[out]
[CASE optional_related_model_is_allowed_for_init]
from django.db import models
class Publisher(models.Model):
pass
class Book(models.Model):
publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)
Book(publisher=None)
Book(publisher_id=None)
[out]

View File

@@ -0,0 +1,42 @@
[CASE nullable_field_with_strict_optional_true]
from django.db import models
class MyModel(models.Model):
text_nullable = models.CharField(max_length=100, null=True)
text = models.CharField(max_length=100)
reveal_type(MyModel().text) # E: Revealed type is 'builtins.str*'
reveal_type(MyModel().text_nullable) # E: Revealed type is 'Union[builtins.str, None]'
MyModel().text = None # E: Incompatible types in assignment (expression has type "None", variable has type "Union[str, int, Combinable]")
MyModel().text_nullable = None
[out]
[CASE nullable_array_field]
from django.db import models
from django.contrib.postgres.fields import ArrayField
class MyModel(models.Model):
lst = ArrayField(base_field=models.CharField(max_length=100), null=True)
reveal_type(MyModel().lst) # E: Revealed type is 'Union[builtins.list[builtins.str], None]'
[out]
[CASE nullable_foreign_key]
from django.db import models
class Publisher(models.Model):
pass
class Book(models.Model):
publisher = models.ForeignKey(to=Publisher, on_delete=models.CASCADE, null=True)
reveal_type(Book().publisher) # E: Revealed type is 'Union[main.Publisher, None]'
Book().publisher = 11 # E: Incompatible types in assignment (expression has type "int", variable has type "Union[Publisher, Combinable, None]")
[out]
[CASE nullable_self_foreign_key]
from django.db import models
class Inventory(models.Model):
parent = models.ForeignKey('self', on_delete=models.SET_NULL, null=True)
parent = Inventory()
core = Inventory(parent_id=parent.id)
reveal_type(core.parent_id) # E: Revealed type is 'Union[builtins.int, None]'
reveal_type(core.parent) # E: Revealed type is 'Union[main.Inventory, None]'
Inventory(parent=None)
Inventory(parent_id=None)
[out]

View File

@@ -191,6 +191,8 @@ class Profile(models.Model):
from django.db import models from django.db import models
class App(models.Model): class App(models.Model):
owner = models.ForeignKey(to='myapp.User', on_delete=models.CASCADE, related_name='apps') owner = models.ForeignKey(to='myapp.User', on_delete=models.CASCADE, related_name='apps')
[disable_cache]
[/CASE]
[CASE many_to_many_field_converts_to_queryset_of_model_type] [CASE many_to_many_field_converts_to_queryset_of_model_type]
from django.db import models from django.db import models
@@ -198,7 +200,7 @@ class App(models.Model):
pass pass
class Member(models.Model): class Member(models.Model):
apps = models.ManyToManyField(to=App, related_name='members') apps = models.ManyToManyField(to=App, related_name='members')
reveal_type(Member().apps) # E: Revealed type is 'django.db.models.manager.RelatedManager[main.App*]' reveal_type(Member().apps) # E: Revealed type is 'django.db.models.manager.RelatedManager*[main.App]'
reveal_type(App().members) # E: Revealed type is 'django.db.models.manager.RelatedManager[main.Member]' reveal_type(App().members) # E: Revealed type is 'django.db.models.manager.RelatedManager[main.Member]'
[out] [out]
@@ -207,7 +209,7 @@ from django.db import models
from myapp.models import App from myapp.models import App
class Member(models.Model): class Member(models.Model):
apps = models.ManyToManyField(to='myapp.App', related_name='members') apps = models.ManyToManyField(to='myapp.App', related_name='members')
reveal_type(Member().apps) # E: Revealed type is 'django.db.models.manager.RelatedManager[myapp.models.App*]' reveal_type(Member().apps) # E: Revealed type is 'django.db.models.manager.RelatedManager*[myapp.models.App]'
[file myapp/__init__.py] [file myapp/__init__.py]
[file myapp/models.py] [file myapp/models.py]
@@ -226,8 +228,8 @@ reveal_type(User().parent) # E: Revealed type is 'main.User*'
[CASE many_to_many_with_self] [CASE many_to_many_with_self]
from django.db import models from django.db import models
class User(models.Model): class User(models.Model):
friends = models.ManyToManyField('self', on_delete=models.CASCADE) friends = models.ManyToManyField('self')
reveal_type(User().friends) # E: Revealed type is 'django.db.models.manager.RelatedManager[main.User*]' reveal_type(User().friends) # E: Revealed type is 'django.db.models.manager.RelatedManager*[main.User]'
[out] [out]
[CASE recursively_checking_for_base_model_in_to_parameter] [CASE recursively_checking_for_base_model_in_to_parameter]
@@ -260,7 +262,7 @@ class Book(models.Model):
reveal_type(Book().publisher_id) # E: Revealed type is 'builtins.str' reveal_type(Book().publisher_id) # E: Revealed type is 'builtins.str'
Book(publisher_id=1) Book(publisher_id=1)
Book(publisher_id='hello') Book(publisher_id='hello')
Book(publisher_id=datetime.datetime.now()) # E: Incompatible type for "publisher_id" of "Book" (got "datetime", expected "Union[str, int, Combinable]") Book(publisher_id=datetime.datetime.now()) # E: Incompatible type for "publisher_id" of "Book" (got "datetime", expected "Union[str, int, Combinable, None]")
Book.objects.create(publisher_id=1) Book.objects.create(publisher_id=1)
Book.objects.create(publisher_id='hello') Book.objects.create(publisher_id='hello')
@@ -271,7 +273,17 @@ class Book2(models.Model):
reveal_type(Book2().publisher_id) # E: Revealed type is 'builtins.int' reveal_type(Book2().publisher_id) # E: Revealed type is 'builtins.int'
Book2(publisher_id=1) Book2(publisher_id=1)
Book2(publisher_id='hello') # E: Incompatible type for "publisher_id" of "Book2" (got "str", expected "Union[int, Combinable, Literal['']]") Book2(publisher_id=[]) # E: Incompatible type for "publisher_id" of "Book2" (got "List[Any]", expected "Union[float, int, str, Combinable, None]")
Book2.objects.create(publisher_id=1) Book2.objects.create(publisher_id=1)
Book2.objects.create(publisher_id='hello') # E: Incompatible type for "publisher_id" of "Book2" (got "str", expected "Union[int, Combinable, Literal['']]") Book2.objects.create(publisher_id=[]) # E: Incompatible type for "publisher_id" of "Book2" (got "List[Any]", expected "Union[float, int, str, Combinable]")
[out]
[CASE if_model_is_defined_as_name_of_the_class_look_for_it_in_the_same_file]
from django.db import models
class Book(models.Model):
publisher = models.ForeignKey(to='Publisher', on_delete=models.CASCADE)
class Publisher(models.Model):
pass
reveal_type(Book().publisher) # E: Revealed type is 'main.Publisher*'
[out] [out]

View File

@@ -1,4 +1,6 @@
[CASE test_settings_are_parsed_into_django_conf_settings] [CASE test_settings_are_parsed_into_django_conf_settings]
[env DJANGO_SETTINGS_MODULE=mysettings]
[disable_cache]
from django.conf import settings from django.conf import settings
reveal_type(settings.ROOT_DIR) # E: Revealed type is 'builtins.str' reveal_type(settings.ROOT_DIR) # E: Revealed type is 'builtins.str'
@@ -6,7 +8,6 @@ reveal_type(settings.APPS_DIR) # E: Revealed type is 'pathlib.Path'
reveal_type(settings.OBJ) # E: Revealed type is 'django.utils.functional.LazyObject' reveal_type(settings.OBJ) # E: Revealed type is 'django.utils.functional.LazyObject'
reveal_type(settings.NUMBERS) # E: Revealed type is 'builtins.list[builtins.str]' reveal_type(settings.NUMBERS) # E: Revealed type is 'builtins.list[builtins.str]'
reveal_type(settings.DICT) # E: Revealed type is 'builtins.dict[Any, Any]' reveal_type(settings.DICT) # E: Revealed type is 'builtins.dict[Any, Any]'
[env DJANGO_SETTINGS_MODULE=mysettings]
[file base.py] [file base.py]
from pathlib import Path from pathlib import Path
ROOT_DIR = '/etc' ROOT_DIR = '/etc'
@@ -18,14 +19,16 @@ NUMBERS = ['one', 'two']
DICT = {} # type: ignore DICT = {} # type: ignore
from django.utils.functional import LazyObject from django.utils.functional import LazyObject
OBJ = LazyObject() OBJ = LazyObject()
[/CASE]
[CASE test_settings_could_be_defined_in_different_module_and_imported_with_star] [CASE test_settings_could_be_defined_in_different_module_and_imported_with_star]
[env DJANGO_SETTINGS_MODULE=mysettings]
[disable_cache]
from django.conf import settings from django.conf import settings
reveal_type(settings.ROOT_DIR) # E: Revealed type is 'pathlib.Path' reveal_type(settings.ROOT_DIR) # E: Revealed type is 'pathlib.Path'
reveal_type(settings.SETUP) # E: Revealed type is 'builtins.int' reveal_type(settings.SETUP) # E: Revealed type is 'builtins.int'
reveal_type(settings.DATABASES) # E: Revealed type is 'builtins.dict[builtins.str, builtins.str]' reveal_type(settings.DATABASES) # E: Revealed type is 'builtins.dict[builtins.str, builtins.str]'
[env DJANGO_SETTINGS_MODULE=mysettings]
[file mysettings.py] [file mysettings.py]
from local import * from local import *
DATABASES = {'default': 'mydb'} DATABASES = {'default': 'mydb'}
@@ -36,24 +39,25 @@ SETUP = 3
from pathlib import Path from pathlib import Path
ROOT_DIR = Path(__file__) ROOT_DIR = Path(__file__)
[/CASE]
[CASE global_settings_are_always_loaded] [CASE global_settings_are_always_loaded]
from django.conf import settings from django.conf import settings
reveal_type(settings.AUTH_USER_MODEL) # E: Revealed type is 'builtins.str' reveal_type(settings.AUTH_USER_MODEL) # E: Revealed type is 'builtins.str'
reveal_type(settings.AUTHENTICATION_BACKENDS) # E: Revealed type is 'builtins.list[builtins.str]' reveal_type(settings.AUTHENTICATION_BACKENDS) # E: Revealed type is 'typing.Sequence[builtins.str]'
[out] [/CASE]
[CASE test_circular_dependency_in_settings_works_if_settings_have_annotations] [CASE test_circular_dependency_in_settings_works_if_settings_have_annotations]
[env DJANGO_SETTINGS_MODULE=mysettings]
[disable_cache]
from django.conf import settings from django.conf import settings
class Class: class Class:
pass pass
reveal_type(settings.MYSETTING) # E: Revealed type is 'builtins.int' reveal_type(settings.MYSETTING) # E: Revealed type is 'builtins.int'
reveal_type(settings.REGISTRY) # E: Revealed type is 'Union[main.Class, None]' reveal_type(settings.REGISTRY) # E: Revealed type is 'Union[main.Class, None]'
reveal_type(settings.LIST) # E: Revealed type is 'builtins.list[builtins.str]' reveal_type(settings.LIST) # E: Revealed type is 'builtins.list[builtins.str]'
[out]
[env DJANGO_SETTINGS_MODULE=mysettings]
[file mysettings.py] [file mysettings.py]
from typing import TYPE_CHECKING, Optional, List from typing import TYPE_CHECKING, Optional, List
@@ -63,13 +67,16 @@ if TYPE_CHECKING:
MYSETTING = 1122 MYSETTING = 1122
REGISTRY: Optional['Class'] = None REGISTRY: Optional['Class'] = None
LIST: List[str] = ['1', '2'] LIST: List[str] = ['1', '2']
[/CASE]
[CASE fail_if_there_is_no_setting] [CASE fail_if_there_is_no_setting]
from django.conf import settings from django.conf import settings
reveal_type(settings.NOT_EXISTING) reveal_type(settings.NOT_EXISTING)
[env DJANGO_SETTINGS_MODULE=mysettings] [env DJANGO_SETTINGS_MODULE=mysettings]
[disable_cache]
[file mysettings.py] [file mysettings.py]
[out] [out]
main:2: error: Revealed type is 'Any' main:2: error: Revealed type is 'Any'
main:2: error: "LazySettings" has no attribute "NOT_EXISTING" main:2: error: "LazySettings" has no attribute "NOT_EXISTING"
[/CASE]

View File

@@ -11,9 +11,11 @@ reveal_type(get_object_or_404(MyModel.objects.get_queryset())) # E: Revealed ty
reveal_type(get_list_or_404(MyModel)) # E: Revealed type is 'builtins.list[main.MyModel*]' reveal_type(get_list_or_404(MyModel)) # E: Revealed type is 'builtins.list[main.MyModel*]'
reveal_type(get_list_or_404(MyModel.objects)) # E: Revealed type is 'builtins.list[main.MyModel*]' reveal_type(get_list_or_404(MyModel.objects)) # E: Revealed type is 'builtins.list[main.MyModel*]'
reveal_type(get_list_or_404(MyModel.objects.get_queryset())) # E: Revealed type is 'builtins.list[main.MyModel*]' reveal_type(get_list_or_404(MyModel.objects.get_queryset())) # E: Revealed type is 'builtins.list[main.MyModel*]'
[out] [/CASE]
[CASE get_user_model_returns_proper_class] [CASE get_user_model_returns_proper_class]
[env DJANGO_SETTINGS_MODULE=mysettings]
[disable_cache]
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from myapp.models import MyUser from myapp.models import MyUser
@@ -22,7 +24,6 @@ from django.contrib.auth import get_user_model
UserModel = get_user_model() UserModel = get_user_model()
reveal_type(UserModel.objects) # E: Revealed type is 'django.db.models.manager.Manager[myapp.models.MyUser]' reveal_type(UserModel.objects) # E: Revealed type is 'django.db.models.manager.Manager[myapp.models.MyUser]'
[env DJANGO_SETTINGS_MODULE=mysettings]
[file mysettings.py] [file mysettings.py]
INSTALLED_APPS = ('myapp',) INSTALLED_APPS = ('myapp',)
AUTH_USER_MODEL = 'myapp.MyUser' AUTH_USER_MODEL = 'myapp.MyUser'
@@ -32,15 +33,16 @@ AUTH_USER_MODEL = 'myapp.MyUser'
from django.db import models from django.db import models
class MyUser(models.Model): class MyUser(models.Model):
pass pass
[out] [/CASE]
[CASE return_type_model_and_show_error_if_model_not_yet_imported] [CASE return_type_model_and_show_error_if_model_not_yet_imported]
[env DJANGO_SETTINGS_MODULE=mysettings]
[disable_cache]
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
UserModel = get_user_model() UserModel = get_user_model()
reveal_type(UserModel.objects) reveal_type(UserModel.objects)
[env DJANGO_SETTINGS_MODULE=mysettings]
[file mysettings.py] [file mysettings.py]
INSTALLED_APPS = ('myapp',) INSTALLED_APPS = ('myapp',)
AUTH_USER_MODEL = 'myapp.MyUser' AUTH_USER_MODEL = 'myapp.MyUser'
@@ -53,4 +55,5 @@ class MyUser(models.Model):
[out] [out]
main:3: error: "myapp.MyUser" model class is not imported so far. Try to import it (under if TYPE_CHECKING) at the beginning of the current file main:3: error: "myapp.MyUser" model class is not imported so far. Try to import it (under if TYPE_CHECKING) at the beginning of the current file
main:4: error: Revealed type is 'Any' main:4: error: Revealed type is 'Any'
main:4: error: "Type[Model]" has no attribute "objects" main:4: error: "Type[Model]" has no attribute "objects"
[/CASE]

View File

@@ -0,0 +1,50 @@
[CASE test_transaction_atomic]
from django.db import transaction
with transaction.atomic():
pass
with transaction.atomic(using="mydb"):
pass
with transaction.atomic(using="mydb", savepoint=False):
pass
@transaction.atomic()
def decorated_func(param1: str, param2: int) -> bool:
pass
# Ensure that the function's type is preserved
reveal_type(decorated_func) # E: Revealed type is 'def (param1: builtins.str, param2: builtins.int) -> builtins.bool'
@transaction.atomic(using="mydb")
def decorated_func_using(param1: str, param2: int) -> bool:
pass
# Ensure that the function's type is preserved
reveal_type(decorated_func_using) # E: Revealed type is 'def (param1: builtins.str, param2: builtins.int) -> builtins.bool'
class ClassWithAtomicMethod:
# Bare decorator
@transaction.atomic
def atomic_method1(self, abc: int) -> str:
pass
@transaction.atomic(savepoint=True)
def atomic_method2(self):
pass
@transaction.atomic(using="db", savepoint=True)
def atomic_method3(self, myparam: str) -> int:
pass
ClassWithAtomicMethod().atomic_method1("abc") # E: Argument 1 to "atomic_method1" of "ClassWithAtomicMethod" has incompatible type "str"; expected "int"
# Ensure that the method's type is preserved
reveal_type(ClassWithAtomicMethod().atomic_method1) # E: Revealed type is 'def (abc: builtins.int) -> builtins.str'
# Ensure that the method's type is preserved
reveal_type(ClassWithAtomicMethod().atomic_method3) # E: Revealed type is 'def (myparam: builtins.str) -> builtins.int'
[out]