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.
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
```

View File

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

View File

@@ -12,7 +12,7 @@ class AppConfig:
verbose_name: str = ...
path: str = ...
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: ...
@classmethod
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
# 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 #
@@ -377,7 +377,7 @@ CACHE_MIDDLEWARE_ALIAS = "default"
AUTH_USER_MODEL: str = ...
AUTHENTICATION_BACKENDS: List[str] = ...
AUTHENTICATION_BACKENDS: Sequence[str] = ...
LOGIN_URL = "/accounts/login/"

View File

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

View File

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

View File

@@ -43,7 +43,7 @@ class NestedObjects(Collector):
def add_edge(self, source: Optional[Model], target: Model) -> None: ...
def collect(
self,
objs: Union[Sequence[Model], QuerySet],
objs: Union[Sequence[Optional[Model]], QuerySet],
source: Optional[Type[Model]] = ...,
source_attr: Optional[str] = ...,
**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.core.handlers.wsgi import WSGIRequest
from django.db.models.fields import Field
from django.db.models.fields.related import ManyToManyField
from django.db.models.options import Options
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.urls.resolvers import URLPattern
@@ -27,10 +31,7 @@ class GroupAdmin(admin.ModelAdmin):
class UserAdmin(admin.ModelAdmin):
admin_site: AdminSite
formfield_overrides: Dict[
Type[Union[django.db.models.fields.DateTimeCheckMixin, Field]],
Dict[str, Type[Union[django.forms.fields.SplitDateTimeField, Widget]]],
]
formfield_overrides: Dict[Type[Field], Dict[str, Type[Union[FormField, Widget]]]]
model: Type[User]
opts: Options
add_form_template: str = ...

View File

@@ -1,6 +1,6 @@
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
class AccessMixin:
@@ -14,15 +14,15 @@ class AccessMixin:
def handle_no_permission(self) -> HttpResponseRedirect: ...
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):
permission_required: Any = ...
def get_permission_required(self) -> List[str]: ...
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):
def test_func(self) -> None: ...
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):
content_type_id: int
name: models.CharField = ...
content_type: models.ForeignKey[ContentType] = ...
content_type: models.ForeignKey = models.ForeignKey(ContentType, on_delete=models.CASCADE)
codename: models.CharField = ...
def natural_key(self) -> Tuple[str, str, str]: ...
@@ -24,7 +24,7 @@ class GroupManager(models.Manager):
class Group(models.Model):
name: models.CharField = ...
permissions: models.ManyToManyField[Permission] = ...
permissions: models.ManyToManyField = models.ManyToManyField(Permission)
def natural_key(self): ...
class UserManager(BaseUserManager):
@@ -37,8 +37,8 @@ class UserManager(BaseUserManager):
class PermissionsMixin(models.Model):
is_superuser: models.BooleanField = ...
groups: models.ManyToManyField[Group] = ...
user_permissions: models.ManyToManyField[Permission] = ...
groups: models.ManyToManyField = models.ManyToManyField(Group)
user_permissions: models.ManyToManyField = models.ManyToManyField(Permission)
def get_group_permissions(self, obj: None = ...) -> 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: ...

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.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
_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 = ...
default_error_messages: Any = ...
base_field: Any = ...
size: Any = ...
default_validators: 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
def description(self): ...
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
class FieldDoesNotExist(Exception): ...
@@ -31,20 +30,13 @@ class ValidationError(Exception):
message: Any = ...
code: Any = ...
params: Any = ...
def __init__(
self,
message: Any,
code: Optional[str] = ...,
params: Optional[
Union[Dict[str, Union[Tuple[str], Type[Model], Model, str]], Dict[str, Union[int, str]]]
] = ...,
) -> None: ...
def __init__(self, message: Any, code: Optional[str] = ..., params: Optional[Mapping[str, Any]] = ...) -> None: ...
@property
def message_dict(self) -> Dict[str, List[str]]: ...
@property
def messages(self) -> List[str]: ...
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]: ...
def __iter__(self) -> Iterator[Union[Tuple[str, List[str]], str]]: ...

View File

@@ -39,10 +39,10 @@ class InMemoryUploadedFile(UploadedFile):
charset: Optional[str],
content_type_extra: Dict[str, str] = ...,
) -> None: ...
def chunks(self, chunk_size: int = None) -> Iterator[bytes]: ...
def multiple_chunks(self, chunk_size: int = None) -> bool: ...
def chunks(self, chunk_size: Optional[int] = ...) -> Iterator[bytes]: ...
def multiple_chunks(self, chunk_size: Optional[int] = ...) -> bool: ...
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
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):
option_name: 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):
option_name: 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):
order_with_respect_to: str = ...

View File

@@ -22,9 +22,9 @@ class ModelState:
name: str
app_label: str
fields: List[Tuple[str, Field]]
options: Optional[Dict[str, Any]] = ...
bases: Optional[Tuple[Type[Model]]] = ...
managers: Optional[List[Tuple[str, Manager]]] = ...
options: Dict[str, Any] = ...
bases: Tuple[Type[Model]] = ...
managers: List[Tuple[str, Manager]] = ...
def __init__(
self,
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
from typing_extensions import Literal
import uuid
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.query_utils import RegisterLookupMixin
from django.db.models.expressions import F, Combinable
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
_Choice = Tuple[Any, Any]
@@ -20,7 +18,15 @@ _FieldChoices = Iterable[Union[_Choice, _ChoiceNamedGroup]]
_ValidatorCallable = Callable[..., None]
_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
help_text: str
db_table: str
@@ -52,7 +58,8 @@ class Field(RegisterLookupMixin):
validators: Iterable[_ValidatorCallable] = ...,
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 set_attributes_from_name(self, name: str) -> None: ...
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 to_python(self, value: Any) -> Any: ...
class IntegerField(Field):
def __set__(self, instance, value: Union[int, Combinable, Literal[""]]) -> None: ...
def __get__(self, instance, owner) -> int: ...
class IntegerField(Field[_ST, _GT]):
_pyi_private_set_type: Union[float, int, str, Combinable]
_pyi_private_get_type: int
class PositiveIntegerRelDbTypeMixin:
def rel_db_type(self, connection: Any): ...
class PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField): ...
class PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField): ...
class SmallIntegerField(IntegerField): ...
class BigIntegerField(IntegerField): ...
class PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField[_ST, _GT]): ...
class PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField[_ST, _GT]): ...
class SmallIntegerField(IntegerField[_ST, _GT]): ...
class BigIntegerField(IntegerField[_ST, _GT]): ...
class FloatField(Field):
def __set__(self, instance, value: Union[float, int, str, Combinable]) -> float: ...
def __get__(self, instance, owner) -> float: ...
class FloatField(Field[_ST, _GT]):
_pyi_private_set_type: Union[float, int, str, Combinable]
_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__(
self,
verbose_name: Optional[Union[str, bytes]] = ...,
@@ -102,13 +111,14 @@ class DecimalField(Field):
validators: Iterable[_ValidatorCallable] = ...,
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):
def __get__(self, instance, owner) -> int: ...
class AutoField(Field[_ST, _GT]):
_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__(
self,
verbose_name: Optional[Union[str, bytes]] = ...,
@@ -133,10 +143,8 @@ class CharField(Field):
validators: Iterable[_ValidatorCallable] = ...,
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__(
self,
verbose_name: Optional[Union[str, bytes]] = ...,
@@ -163,25 +171,29 @@ class SlugField(CharField):
error_messages: Optional[_ErrorMessagesToOverride] = ...,
): ...
class EmailField(CharField): ...
class URLField(CharField): ...
class EmailField(CharField[_ST, _GT]): ...
class URLField(CharField[_ST, _GT]): ...
class TextField(Field):
def __set__(self, instance, value: Union[str, Combinable]) -> None: ...
def __get__(self, instance, owner) -> str: ...
class TextField(Field[_ST, _GT]):
_pyi_private_set_type: Union[str, Combinable]
_pyi_private_get_type: str
class BooleanField(Field):
def __set__(self, instance, value: Union[bool, Combinable]) -> None: ...
def __get__(self, instance, owner) -> bool: ...
class BooleanField(Field[_ST, _GT]):
_pyi_private_set_type: Union[bool, Combinable]
_pyi_private_get_type: bool
class NullBooleanField(Field):
def __set__(self, instance, value: Optional[Union[bool, Combinable]]) -> None: ...
def __get__(self, instance, owner) -> Optional[bool]: ...
class NullBooleanField(Field[_ST, _GT]):
_pyi_private_set_type: Optional[Union[bool, Combinable]]
_pyi_private_get_type: Optional[bool]
class IPAddressField(Field):
def __get__(self, instance, owner) -> str: ...
class IPAddressField(Field[_ST, _GT]):
_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 = ...
unpack_ipv4: Any = ...
protocol: Any = ...
@@ -207,12 +219,12 @@ class GenericIPAddressField(Field):
validators: Iterable[_ValidatorCallable] = ...,
error_messages: Optional[_ErrorMessagesToOverride] = ...,
) -> None: ...
def __set__(self, instance, value: Union[str, int, Callable[..., Any], Combinable]): ...
def __get__(self, instance, owner) -> str: ...
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__(
self,
verbose_name: Optional[Union[str, bytes]] = ...,
@@ -236,10 +248,10 @@ class DateField(DateTimeCheckMixin, Field):
validators: Iterable[_ValidatorCallable] = ...,
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__(
self,
verbose_name: Optional[Union[str, bytes]] = ...,
@@ -262,18 +274,15 @@ class TimeField(DateTimeCheckMixin, Field):
validators: Iterable[_ValidatorCallable] = ...,
error_messages: Optional[_ErrorMessagesToOverride] = ...,
): ...
def __set__(self, instance, value: Union[str, time, datetime, Combinable]) -> None: ...
def __get__(self, instance, owner) -> time: ...
class DateTimeField(DateField):
def __set__(self, instance, value: Union[str, date, datetime, Combinable]) -> None: ...
def __get__(self, instance, owner) -> datetime: ...
class DateTimeField(DateField[_ST, _GT]):
_pyi_private_get_type: datetime
class UUIDField(Field):
def __set__(self, instance, value: Union[str, uuid.UUID]) -> None: ...
def __get__(self, instance, owner) -> uuid.UUID: ...
class UUIDField(Field[_ST, _GT]):
_pyi_private_set_type: Union[str, uuid.UUID]
_pyi_private_get_type: uuid.UUID
class FilePathField(Field):
class FilePathField(Field[_ST, _GT]):
path: str = ...
match: Optional[Any] = ...
recursive: bool = ...
@@ -306,10 +315,10 @@ class FilePathField(Field):
error_messages: Optional[_ErrorMessagesToOverride] = ...,
): ...
class BinaryField(Field): ...
class BinaryField(Field[_ST, _GT]): ...
class DurationField(Field):
def __get__(self, instance, owner) -> timedelta: ...
class DurationField(Field[_ST, _GT]):
_pyi_private_get_type: timedelta
class BigAutoField(AutoField): ...
class CommaSeparatedIntegerField(CharField): ...
class BigAutoField(AutoField[_ST, _GT]): ...
class CommaSeparatedIntegerField(CharField[_ST, _GT]): ...

View File

@@ -49,7 +49,12 @@ _ErrorMessagesToOverride = Dict[str, Any]
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_one: bool = ...
many_to_many: bool = ...
@@ -83,6 +88,7 @@ class ForeignObject(RelatedField):
related_query_name: None = ...,
limit_choices_to: Optional[Union[Dict[str, Any], Callable[[], Any]]] = ...,
parent_link: bool = ...,
db_constraint: bool = ...,
swappable: bool = ...,
verbose_name: Optional[str] = ...,
name: Optional[str] = ...,
@@ -103,17 +109,82 @@ class ForeignObject(RelatedField):
error_messages: Optional[_ErrorMessagesToOverride] = ...,
): ...
class ForeignKey(RelatedField, Generic[_T]):
def __init__(self, to: Union[Type[_T], str], on_delete: Any, related_name: str = ..., **kwargs): ...
def __set__(self, instance, value: Union[Model, Combinable]) -> None: ...
def __get__(self, instance, owner) -> _T: ...
class ForeignKey(RelatedField[_ST, _GT]):
_pyi_private_set_type: Union[Any, Combinable]
_pyi_private_get_type: Any
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]):
def __init__(self, to: Union[Type[_T], str], on_delete: Any, related_name: str = ..., **kwargs): ...
def __set__(self, instance, value: Union[Model, Combinable]) -> None: ...
def __get__(self, instance, owner) -> _T: ...
class OneToOneField(RelatedField[_ST, _GT]):
_pyi_private_set_type: Union[Any, Combinable]
_pyi_private_get_type: Any
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_one: bool = ...
one_to_many: bool = ...
@@ -127,17 +198,35 @@ class ManyToManyField(RelatedField, Generic[_T]):
to: Union[Type[_T], str],
related_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] = ...,
through: Optional[Union[str, Type[Model]]] = ...,
through_fields: Optional[Tuple[str, str]] = ...,
db_constraint: bool = ...,
db_table: Optional[str] = ...,
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: ...
def __set__(self, instance, value: Sequence[_T]) -> None: ...
def __get__(self, instance, owner) -> RelatedManager[_T]: ...
def check(self, **kwargs: Any) -> List[Any]: ...
def deconstruct(self) -> Tuple[Optional[str], str, List[Any], Dict[str, str]]: ...
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
_T = TypeVar("_T", bound=Model, covariant=True)
_Self = TypeVar("_Self", bound="BaseManager")
class BaseManager(QuerySet[_T]):
creation_counter: int = ...
@@ -17,9 +16,7 @@ class BaseManager(QuerySet[_T]):
def deconstruct(self) -> Tuple[bool, str, None, Tuple, Dict[str, int]]: ...
def check(self, **kwargs: Any) -> List[Any]: ...
@classmethod
def from_queryset(
cls: Type[_Self], queryset_class: Type[QuerySet], class_name: Optional[str] = ...
) -> Type[_Self]: ...
def from_queryset(cls, queryset_class: Type[QuerySet], class_name: Optional[str] = ...) -> Any: ...
@classmethod
def _get_queryset_methods(cls, queryset_class: type) -> Dict[str, Any]: ...
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, Union, Iterator, overload, ContextManager
from typing import Any, Callable, Optional, overload, TypeVar
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 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] = ...
savepoint: bool = ...
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 __exit__(self, exc_type: None, exc_value: None, traceback: None) -> None: ...
# Bare decorator
@overload
def atomic() -> Atomic: ...
def atomic(using: _C) -> _C: ...
# Decorator or context-manager with parameters
@overload
def atomic(using: Optional[str] = ...,) -> ContextManager[Atomic]: ...
@overload
def atomic(using: Callable = ...) -> Callable: ...
@overload
def atomic(using: Optional[str] = ..., savepoint: bool = ...) -> ContextManager[Atomic]: ...
def atomic(using: Optional[str] = None, savepoint: bool = True) -> Atomic: ...
def non_atomic_requests(using: Callable = ...) -> Callable: ...

View File

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

View File

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

View File

@@ -1,8 +1,4 @@
import collections
from typing import Any, List, Optional, Union, Dict, Type
from django.forms.renderers import BaseRenderer
from django.forms.utils import ErrorList
from typing import Any, Dict, Mapping, Optional, Sequence, Sized
from django.forms import Form
@@ -17,21 +13,9 @@ DEFAULT_MIN_NUM: int = ...
DEFAULT_MAX_NUM: int = ...
class ManagementForm(Form):
auto_id: Union[bool, str]
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 = ...
prefix: Any = ...
auto_id: Any = ...
@@ -57,6 +41,7 @@ class BaseFormSet:
def management_form(self): ...
def total_form_count(self): ...
def initial_form_count(self): ...
@property
def forms(self): ...
def get_form_kwargs(self, index: Any): ...
@property
@@ -101,4 +86,4 @@ def formset_factory(
min_num: Optional[Any] = ...,
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 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 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.base import Model
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.formsets import BaseFormSet
from django.forms.utils import ErrorList
from django.forms.widgets import Input, Widget, Select
from django.http.request import QueryDict
from django.utils.datastructures import MultiValueDict
from django.forms.widgets import Input, Widget
from typing_extensions import Literal
ALL_FIELDS: str
@@ -57,21 +57,19 @@ class ModelFormOptions:
def __init__(self, options: Optional[type] = ...) -> None: ...
class ModelFormMetaclass(DeclarativeFieldsMetaclass):
def __new__(
mcs: Type[ModelFormMetaclass], name: str, bases: Tuple[Type[ModelForm]], attrs: OrderedDict
) -> Type[ModelForm]: ...
def __new__(mcs, name: str, bases: Sequence[Type[ModelForm]], attrs: Dict[str, Any]) -> Type[ModelForm]: ...
class BaseModelForm(BaseForm):
instance: Any = ...
def __init__(
self,
data: Optional[Union[Dict[str, Any], QueryDict]] = ...,
files: Optional[Union[Dict[str, SimpleUploadedFile], MultiValueDict]] = ...,
data: Optional[Dict[str, Any]] = ...,
files: Optional[Dict[str, File]] = ...,
auto_id: Union[bool, str] = ...,
prefix: None = ...,
initial: Optional[Union[Dict[str, List[int]], Dict[str, int]]] = ...,
prefix: Optional[str] = ...,
initial: Optional[Dict[str, Any]] = ...,
error_class: Type[ErrorList] = ...,
label_suffix: None = ...,
label_suffix: Optional[str] = ...,
empty_permitted: bool = ...,
instance: Optional[Model] = ...,
use_required_attribute: None = ...,
@@ -87,16 +85,16 @@ class ModelForm(BaseModelForm): ...
def modelform_factory(
model: Type[Model],
form: Type[ModelForm] = ...,
fields: Optional[Union[List[str], str]] = ...,
exclude: None = ...,
formfield_callback: Optional[str] = ...,
widgets: None = ...,
localized_fields: None = ...,
labels: None = ...,
help_texts: None = ...,
error_messages: None = ...,
field_classes: None = ...,
) -> Any: ...
fields: Optional[Union[Sequence[str], Literal["__all__"]]] = ...,
exclude: Optional[Sequence[str]] = ...,
formfield_callback: Optional[Union[str, Callable[[models.Field], Field]]] = ...,
widgets: Optional[MutableMapping[str, Widget]] = ...,
localized_fields: Optional[Sequence[str]] = ...,
labels: Optional[MutableMapping[str, str]] = ...,
help_texts: Optional[MutableMapping[str, str]] = ...,
error_messages: Optional[MutableMapping[str, Dict[str, Any]]] = ...,
field_classes: Optional[MutableMapping[str, Type[Field]]] = ...,
) -> Type[ModelForm]: ...
class BaseModelFormSet(BaseFormSet):
model: Any = ...
@@ -148,13 +146,13 @@ def modelformset_factory(
exclude: Optional[Sequence[str]] = ...,
widgets: Optional[Dict[str, Any]] = ...,
validate_max: bool = ...,
localized_fields: None = ...,
localized_fields: Optional[Sequence[str]] = ...,
labels: Optional[Dict[str, str]] = ...,
help_texts: Optional[Dict[str, str]] = ...,
error_messages: Optional[Dict[str, Dict[str, str]]] = ...,
validate_min: bool = ...,
field_classes: Optional[Dict[str, Any]] = ...,
) -> Any: ...
field_classes: Optional[Dict[str, Type[Field]]] = ...,
) -> Type[BaseModelFormSet]: ...
class BaseInlineFormSet(BaseModelFormSet):
instance: Any = ...
@@ -192,14 +190,14 @@ def inlineformset_factory(
formfield_callback: Optional[Callable] = ...,
widgets: Optional[Dict[str, Any]] = ...,
validate_max: bool = ...,
localized_fields: None = ...,
localized_fields: Optional[Sequence[str]] = ...,
labels: Optional[Dict[str, str]] = ...,
help_texts: Optional[Dict[str, str]] = ...,
error_messages: Optional[Dict[str, Dict[str, str]]] = ...,
min_num: Optional[int] = ...,
validate_min: bool = ...,
field_classes: Optional[Dict[str, Any]] = ...,
) -> Any: ...
) -> Type[BaseInlineFormSet]: ...
class InlineForeignKeyField(Field):
disabled: bool

View File

@@ -6,11 +6,11 @@ from django.core.exceptions import ValidationError
from django.utils.safestring import SafeText
def pretty_name(name: str) -> str: ...
def flatatt(attrs: Dict[str, Optional[str]]) -> SafeText: ...
def flatatt(attrs: Dict[str, Any]) -> SafeText: ...
class ErrorDict(dict):
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_ul(self) -> str: ...
def as_text(self) -> str: ...
@@ -19,7 +19,9 @@ class ErrorList(UserList):
data: List[Union[ValidationError, str]]
error_class: str = ...
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: ...
def as_data(self) -> List[ValidationError]: ...
def get_json_data(self, escape_html: bool = ...) -> List[Dict[str, str]]: ...

View File

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

View File

@@ -33,7 +33,7 @@ class HttpResponseBase(Iterable[AnyStr]):
def has_header(self, header: str) -> bool: ...
def items(self) -> Iterable[Tuple[str, str]]: ...
@overload
def get(self, header: Union[str, bytes], alternate: str) -> str: ...
def get(self, header: Union[str, bytes], alternate: Optional[str]) -> str: ...
@overload
def get(self, header: Union[str, bytes]) -> Optional[str]: ...
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.template.base import Node, Origin, Template
@@ -15,10 +15,10 @@ class ContextDict(dict):
def __enter__(self) -> ContextDict: ...
def __exit__(self, *args: Any, **kwargs: Any) -> None: ...
class BaseContext:
class BaseContext(Iterable[Any]):
def __init__(self, dict_: Any = ...) -> None: ...
def __copy__(self) -> BaseContext: ...
def __iter__(self) -> None: ...
def __iter__(self) -> Iterator[Any]: ...
def push(self, *args: Any, **kwargs: Any) -> ContextDict: ...
def pop(self) -> ContextDict: ...
def __setitem__(self, key: Union[Node, str], value: Any) -> None: ...
@@ -50,7 +50,6 @@ class Context(BaseContext):
class RenderContext(BaseContext):
dicts: List[Dict[Union[IncludeNode, str], str]]
template: Optional[Template] = ...
def __iter__(self) -> None: ...
def push_state(self, template: Template, isolated_context: bool = ...) -> Iterator[None]: ...
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
logger = ... # type: Any
class ContextMixin:
def get_context_data(self, **kwargs: object) -> Dict[str, Any]: ...
def get_context_data(self, **kwargs: Any) -> Dict[str, Any]: ...
class View:
http_method_names = ... # type: List[str]
request = ... # type: http.HttpRequest
args = ... # type: Tuple[object, ...]
kwargs = ... # type: Dict[str, object]
def __init__(self, **kwargs: object) -> None: ...
http_method_names: List[str] = ...
request: http.HttpRequest = ...
args: Any = ...
kwargs: Any = ...
def __init__(self, **kwargs: Any) -> None: ...
@classmethod
def as_view(cls: Any, **initkwargs: object) -> Callable[..., http.HttpResponse]: ...
def dispatch(self, request: http.HttpRequest, *args: object, **kwargs: object) -> http.HttpResponse: ...
def http_method_not_allowed(
self, request: http.HttpRequest, *args: object, **kwargs: object
) -> http.HttpResponse: ...
def options(self, request: http.HttpRequest, *args: object, **kwargs: object) -> http.HttpResponse: ...
def as_view(cls: Any, **initkwargs: Any) -> Callable[..., http.HttpResponse]: ...
def dispatch(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> http.HttpResponse: ...
def http_method_not_allowed(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> http.HttpResponse: ...
def options(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> http.HttpResponse: ...
class TemplateResponseMixin:
template_name = ... # type: str
template_engine = ... # type: Optional[str]
response_class = ... # type: Type[http.HttpResponse]
content_type = ... # type: Optional[str]
request = ... # type: http.HttpRequest
def render_to_response(self, context: Dict[str, object], **response_kwargs: object) -> http.HttpResponse: ...
template_name: str = ...
template_engine: Optional[str] = ...
response_class: Type[http.HttpResponse] = ...
content_type: Optional[str] = ...
request: http.HttpRequest = ...
def render_to_response(self, context: Dict[str, Any], **response_kwargs: Any) -> http.HttpResponse: ...
def get_template_names(self) -> List[str]: ...
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):
permanent = ... # type: bool
url = ... # type: Optional[str]
pattern_name = ... # type: Optional[str]
query_string = ... # type: bool
def get_redirect_url(self, *args: object, **kwargs: object) -> Optional[str]: ...
def get(self, request: http.HttpRequest, *args: object, **kwargs: object) -> http.HttpResponse: ...
def head(self, request: http.HttpRequest, *args: object, **kwargs: object) -> http.HttpResponse: ...
def post(self, request: http.HttpRequest, *args: object, **kwargs: object) -> http.HttpResponse: ...
def options(self, request: http.HttpRequest, *args: object, **kwargs: object) -> http.HttpResponse: ...
def delete(self, request: http.HttpRequest, *args: object, **kwargs: object) -> http.HttpResponse: ...
def put(self, request: http.HttpRequest, *args: object, **kwargs: object) -> http.HttpResponse: ...
def patch(self, request: http.HttpRequest, *args: object, **kwargs: object) -> http.HttpResponse: ...
permanent: bool = ...
url: Optional[str] = ...
pattern_name: Optional[str] = ...
query_string: bool = ...
def get_redirect_url(self, *args: Any, **kwargs: Any) -> Optional[str]: ...
def get(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> http.HttpResponse: ...
def head(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> http.HttpResponse: ...
def post(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> http.HttpResponse: ...
def options(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> http.HttpResponse: ...
def delete(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> http.HttpResponse: ...
def put(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> 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
class SingleObjectMixin(ContextMixin):
model = ... # type: Optional[Type[models.Model]]
queryset = ... # type: Optional[models.query.QuerySet]
slug_field = ... # type: str
context_object_name = ... # type: Optional[str]
slug_url_kwarg = ... # type: str
pk_url_kwarg = ... # type: str
query_pk_and_slug = ... # type: bool
object = ... # type: models.Model
kwargs = ... # type: Dict[str, object]
def get_object(self, queryset: models.query.QuerySet = None) -> models.Model: ...
model: Optional[Type[models.Model]] = ...
queryset: Optional[models.query.QuerySet] = ...
slug_field: str = ...
context_object_name: Optional[str] = ...
slug_url_kwarg: str = ...
pk_url_kwarg: str = ...
query_pk_and_slug: bool = ...
object: models.Model = ...
kwargs: Dict[str, Any] = ...
def get_object(self, queryset: Optional[models.query.QuerySet] = None) -> models.Model: ...
def get_queryset(self) -> models.query.QuerySet: ...
def get_slug_field(self) -> 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):
def render_to_response(self, context: Dict[str, object], **response_kwargs: object) -> HttpResponse: ...
object = ... # type: models.Model
def get(self, request: HttpRequest, *args: object, **kwargs: object) -> HttpResponse: ...
def render_to_response(self, context: Dict[str, Any], **response_kwargs: Any) -> HttpResponse: ...
def get(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse: ...
class SingleObjectTemplateResponseMixin(TemplateResponseMixin):
template_name_field = ... # type: Optional[str]
template_name_suffix = ... # type: str
model = ... # type: Optional[Type[models.Model]]
object = ... # type: models.Model
template_name_field: Optional[str] = ...
template_name_suffix: str = ...
model: Optional[Type[models.Model]] = ...
object: models.Model = ...
def get_template_names(self) -> List[str]: ...
class DetailView(SingleObjectTemplateResponseMixin, BaseDetailView): ...

View File

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

View File

@@ -2,13 +2,16 @@ import typing
from typing import Dict, Optional
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
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'
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'
FOREIGN_KEY_FULLNAME = 'django.db.models.fields.related.ForeignKey'
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'
MANAGER_CLASS_FULLNAME = 'django.db.models.manager.Manager'
RELATED_MANAGER_CLASS_FULLNAME = 'django.db.models.manager.RelatedManager'
MODELFORM_CLASS_FULLNAME = 'django.forms.models.ModelForm'
MANAGER_CLASSES = {
MANAGER_CLASS_FULLNAME,
@@ -51,9 +55,9 @@ def get_model_fullname(app_name: str, model_name: str,
return None
class InvalidModelString(ValueError):
def __init__(self, model_string: str):
self.model_string = model_string
class SameFileModel(Exception):
def __init__(self, model_cls_name: str):
self.model_cls_name = model_cls_name
class SelfReference(ValueError):
@@ -66,7 +70,7 @@ def get_model_fullname_from_string(model_string: str,
raise SelfReference()
if '.' not in model_string:
raise InvalidModelString(model_string)
raise SameFileModel(model_string)
app_name, model_name = model_string.split('.')
return get_model_fullname(app_name, model_name, all_modules)
@@ -95,12 +99,13 @@ def parse_bool(expr: Expression) -> Optional[bool]:
return None
def reparametrize_with(instance: Instance, new_typevars: typing.List[Type]):
return Instance(instance.type, args=new_typevars)
def reparametrize_instance(instance: Instance, new_args: typing.List[Type]) -> Instance:
return Instance(instance.type, args=new_args,
line=instance.line, column=instance.column)
def fill_typevars_with_any(instance: Instance) -> Type:
return reparametrize_with(instance, [AnyType(TypeOfAny.unannotated)])
def fill_typevars_with_any(instance: Instance) -> Instance:
return reparametrize_instance(instance, [AnyType(TypeOfAny.unannotated)])
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:
if isinstance(typevar_arg, TypeVarType):
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]:
@@ -189,28 +194,12 @@ def iter_over_assignments(
def extract_field_setter_type(tp: Instance) -> Optional[Type]:
if not isinstance(tp, Instance):
return None
""" Extract __set__ value of a field. """
if tp.type.has_base(FIELD_FULLNAME):
set_method = tp.type.get_method('__set__')
if isinstance(set_method, FuncDef) and isinstance(set_method.type, CallableType):
if 'value' in set_method.type.arg_names:
set_value_type = set_method.type.arg_types[set_method.type.arg_names.index('value')]
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 tp.args[0]
# GenericForeignKey
if tp.type.has_base(GENERIC_FOREIGN_KEY_FULLNAME):
return AnyType(TypeOfAny.special_form)
return None
@@ -218,9 +207,7 @@ def extract_field_getter_type(tp: Instance) -> Optional[Type]:
if not isinstance(tp, Instance):
return None
if tp.type.has_base(FIELD_FULLNAME):
get_method = tp.type.get_method('__get__')
if isinstance(get_method, FuncDef) and isinstance(get_method.type, CallableType):
return get_method.type.ret_type
return tp.args[1]
# GenericForeignKey
if tp.type.has_base(GENERIC_FOREIGN_KEY_FULLNAME):
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', {})
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():
is_primary_key = props.get('primary_key', False)
if is_primary_key:
@@ -254,3 +244,40 @@ def extract_primary_key_type_for_get(model: TypeInfo) -> Optional[Type]:
if is_primary_key:
return extract_field_getter_type(model.names[field_name].type)
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
from typing import Callable, Dict, Optional, cast
from typing import Callable, Dict, Optional, Union, cast
from mypy.checker import TypeChecker
from mypy.nodes import MemberExpr, TypeInfo
from mypy.options import Options
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.config import Config
from mypy_django_plugin.plugins import 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.plugins.migrations import determine_model_cls_from_string_for_migrations, get_string_value_from_expr
from mypy_django_plugin.plugins.models import process_model_class
from mypy_django_plugin.plugins.related_fields import extract_to_parameter_as_get_ret_type_for_related_field, reparametrize_with
from mypy_django_plugin.plugins.settings import AddSettingValuesToDjangoConfObject, get_settings_metadata
from mypy_django_plugin.transformers import fields, init_create
from mypy_django_plugin.transformers.forms import make_meta_nested_class_inherit_from_any
from mypy_django_plugin.transformers.migrations import determine_model_cls_from_string_for_migrations, \
get_string_value_from_expr
from mypy_django_plugin.transformers.models import process_model_class
from mypy_django_plugin.transformers.settings import AddSettingValuesToDjangoConfObject, get_settings_metadata
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
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:
api = cast(TypeChecker, ctx.api)
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,
helpers.RELATED_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
@@ -84,6 +93,17 @@ def return_user_model_hook(ctx: FunctionContext) -> Type:
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:
if not isinstance(ctx.default_attr_type, Instance) or not (ctx.default_attr_type.type.fullname() == 'builtins.int'):
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]
sym = ctx.type.type.get(field_name)
if sym and isinstance(sym.type, Instance) and len(sym.type.args) > 0:
to_arg = sym.type.args[0]
if isinstance(to_arg, AnyType):
return AnyType(TypeOfAny.special_form)
referred_to = sym.type.args[1]
if isinstance(referred_to, AnyType):
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)
if 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
@@ -152,22 +186,27 @@ class DjangoPlugin(Plugin):
def _get_current_model_bases(self) -> Dict[str, int]:
model_sym = self.lookup_fully_qualified(helpers.MODEL_CLASS_FULLNAME)
if model_sym is not None and isinstance(model_sym.node, TypeInfo):
if 'django' not in model_sym.node.metadata:
model_sym.node.metadata['django'] = {
'model_bases': {helpers.MODEL_CLASS_FULLNAME: 1}
}
return model_sym.node.metadata['django']['model_bases']
return (model_sym.node.metadata
.setdefault('django', {})
.setdefault('model_bases', {helpers.MODEL_CLASS_FULLNAME: 1}))
else:
return {}
def _get_current_manager_bases(self) -> Dict[str, int]:
manager_sym = self.lookup_fully_qualified(helpers.MANAGER_CLASS_FULLNAME)
if manager_sym is not None and isinstance(manager_sym.node, TypeInfo):
if 'django' not in manager_sym.node.metadata:
manager_sym.node.metadata['django'] = {
'manager_bases': {helpers.MANAGER_CLASS_FULLNAME: 1}
}
return manager_sym.node.metadata['django']['manager_bases']
model_sym = self.lookup_fully_qualified(helpers.MANAGER_CLASS_FULLNAME)
if model_sym is not None and isinstance(model_sym.node, TypeInfo):
return (model_sym.node.metadata
.setdefault('django', {})
.setdefault('manager_bases', {helpers.MANAGER_CLASS_FULLNAME: 1}))
else:
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:
return {}
@@ -176,43 +215,32 @@ class DjangoPlugin(Plugin):
if fullname == 'django.contrib.auth.get_user_model':
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()
if fullname in manager_bases:
return determine_proper_manager_type
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):
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'):
return init_create.redefine_and_typecheck_model_init
def get_method_hook(self, fullname: str
) -> 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()
class_fullname, _, method_name = fullname.rpartition('.')
if class_fullname in manager_classes and method_name == '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
def get_base_class_hook(self, fullname: str
) -> Optional[Callable[[ClassDefContext], None]]:
if fullname in self._get_current_model_bases():
return transform_model_class
if fullname == helpers.DUMMY_SETTINGS_BASE_CLASS:
settings_modules = ['django.conf.global_settings']
if self.django_settings_module:
@@ -220,13 +248,22 @@ class DjangoPlugin(Plugin):
return AddSettingValuesToDjangoConfObject(settings_modules,
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():
return transform_manager_class
if fullname in self._get_current_modelform_bases():
return transform_modelform_class
return None
def get_attribute_hook(self, fullname: str
) -> 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('.')
sym = self.lookup_fully_qualified('django.conf.LazySettings')
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.nodes import TypeInfo, Var
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.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]:
@@ -25,12 +25,13 @@ def redefine_and_typecheck_model_init(ctx: FunctionContext) -> Type:
api = cast(TypeChecker, ctx.api)
model: TypeInfo = ctx.default_return_type.type
expected_types = extract_expected_types(ctx, model)
# order is preserved, can use for positionals
expected_types = extract_expected_types(ctx, model, is_init=True)
# order is preserved, can be used for positionals
positional_names = list(expected_types.keys())
positional_names.remove('pk')
visited_positionals = set()
visited_positionals = set()
# check positionals
for i, (_, actual_pos_type) in enumerate(zip(ctx.arg_names[0], ctx.arg_types[0])):
actual_pos_name = positional_names[i]
@@ -111,42 +112,75 @@ def extract_choices_type(model: TypeInfo, field_name: str) -> Optional[str]:
return None
def extract_expected_types(ctx: FunctionContext, model: TypeInfo) -> Dict[str, Type]:
expected_types: Dict[str, Type] = {}
def extract_expected_types(ctx: FunctionContext, model: TypeInfo,
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:
# no explicit primary key, set pk to Any and add id
primary_key_type = AnyType(TypeOfAny.special_form)
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
for base in model.mro:
# extract all fields for all models in MRO
for name, sym in base.names.items():
# do not redefine special attrs
if name in {'_meta', 'pk'}:
continue
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
expected_types[name] = AnyType(TypeOfAny.from_unimported_type)
expected_types[name + '_id'] = AnyType(TypeOfAny.from_unimported_type)
elif isinstance(sym.node.type, Instance):
tp = sym.node.type
field_type = extract_field_setter_type(tp)
elif isinstance(typ, Instance):
field_type = extract_field_setter_type(typ)
if field_type is None:
continue
if tp.type.fullname() in {helpers.FOREIGN_KEY_FULLNAME, helpers.ONETOONE_FIELD_FULLNAME}:
ref_to_model = tp.args[0]
primary_key_type = AnyType(TypeOfAny.special_form)
if isinstance(ref_to_model, Instance) and ref_to_model.type.has_base(helpers.MODEL_CLASS_FULLNAME):
typ = extract_primary_key_type_for_set(ref_to_model.type)
if typ:
primary_key_type = typ
expected_types[name + '_id'] = primary_key_type
if helpers.has_any_of_bases(typ.type, (helpers.FOREIGN_KEY_FULLNAME,
helpers.ONETOONE_FIELD_FULLNAME)):
related_primary_key_type = AnyType(TypeOfAny.implementation_artifact)
# in case it's optional, we need Instance type
referred_to_model = typ.args[1]
is_nullable = helpers.is_optional(referred_to_model)
if is_nullable:
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:
# 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
return expected_types

View File

@@ -4,7 +4,6 @@ from mypy.checker import TypeChecker
from mypy.nodes import Expression, StrExpr, TypeInfo
from mypy.plugin import MethodContext
from mypy.types import Instance, Type, TypeType
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
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
from mypy.plugin import ClassDefContext
from mypy.plugins.common import add_method
from mypy.semanal import SemanticAnalyzerPass2
from mypy.types import AnyType, Instance, NoneTyp, TypeOfAny
from mypy_django_plugin import helpers
from mypy_django_plugin.helpers import iter_over_assignments
@@ -22,14 +21,8 @@ class ModelClassInitializer(metaclass=ABCMeta):
def from_ctx(cls, ctx: ClassDefContext):
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]:
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:
return None
@@ -74,8 +67,6 @@ def iter_over_one_to_n_related_fields(klass: ClassDef) -> Iterator[Tuple[NameExp
class SetIdAttrsForRelatedFields(ModelClassInitializer):
def run(self) -> None:
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'
self.add_new_node_to_model_class(name=node_name,
typ=self.api.builtin_type('builtins.int'))
@@ -83,7 +74,7 @@ class SetIdAttrsForRelatedFields(ModelClassInitializer):
class InjectAnyAsBaseForNestedMeta(ModelClassInitializer):
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:
return None
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')])):
break
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):
@@ -176,10 +167,9 @@ class AddRelatedManagers(ModelClassInitializer):
all_modules=self.api.modules)
except helpers.SelfReference:
ref_to_fullname = defn.fullname
except helpers.InvalidModelString as exc:
self.api.fail(f'Invalid value for a to= parameter: {exc.model_string!r}',
Context(line=rvalue.line))
return None
except helpers.SameFileModel as exc:
ref_to_fullname = module_name + '.' + exc.model_cls_name
if self.model_classdef.fullname == ref_to_fullname:
related_manager_name = defn.name.lower() + '_set'

View File

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

9
release.xsh Normal file → Executable file
View File

@@ -1,4 +1,9 @@
#!/usr/local/bin/xonsh
python setup.py sdist
twine upload dist/*
try:
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]
strict_optional = False
strict_optional = True
ignore_missing_imports = True
check_untyped_defs = True
warn_no_return = False
show_traceback = True
warn_redundant_casts = True
allow_redefinition = True
incremental = False
plugins =
mypy_django_plugin.main

View File

@@ -15,7 +15,7 @@ PROJECT_DIRECTORY = Path(__file__).parent.parent
DJANGO_BRANCH = 'stable/2.1.x'
# 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
# using this constant.
@@ -32,7 +32,7 @@ IGNORED_ERRORS = {
'Cannot assign to a type',
re.compile(r'Cannot assign to class variable "[a-z_]+" via instance'),
# forms <-> models plugin support
'"Model" has no attribute',
# '"Model" has no attribute',
re.compile(r'Cannot determine type of \'(objects|stuff)\''),
# settings
re.compile(r'Module has no attribute "[A-Z_]+"'),
@@ -51,7 +51,11 @@ IGNORED_ERRORS = {
# private members
re.compile(r'has no attribute "|\'_[a-z][a-z_]+"|\''),
'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': [
'Incompatible types in assignment (expression has type "FilteredChildAdmin", variable has type "ChildAdmin")'
@@ -62,7 +66,7 @@ IGNORED_ERRORS = {
'admin_widgets': [
'Incompatible types in assignment (expression has type "RelatedFieldWidgetWrapper", '
'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': [
re.compile(r'Argument [0-9] to "lookup_field" has incompatible type'),
@@ -78,13 +82,16 @@ IGNORED_ERRORS = {
'"object" not callable',
'Incompatible type for "pk" of "Collector" (got "int", expected "str")',
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': [
'Incompatible types in assignment (expression has type "QuerySet[Any]", variable has type "List[Any]")',
'"as_sql" undefined in superclass',
'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': [
'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")',
'"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': [
'"PasswordValidator" has no attribute "min_length"',
'"validate_password" does not return a value',
@@ -157,6 +167,29 @@ IGNORED_ERRORS = {
'fixtures': [
'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': [
'Argument 1 to "get_object_or_404" has incompatible type "str"; '
+ 'expected "Union[Type[<nothing>], Manager[<nothing>], QuerySet[<nothing>]]"',
@@ -165,11 +198,15 @@ IGNORED_ERRORS = {
'CustomClass'
],
'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': [
'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': [
'Argument "max_length" to "CharField" has incompatible type "str"; expected "Optional[int]"',
'Argument "choices" to "CharField" has incompatible type "str"'
@@ -177,6 +214,9 @@ IGNORED_ERRORS = {
'logging_tests': [
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': [
'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"',
'Cannot assign multiple types to name "PersonTwoImages" without an explicit "Type[...]" annotation',
'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': [
'Too many arguments for "Worker"',
@@ -203,6 +250,7 @@ IGNORED_ERRORS = {
],
'migrate_signals': [
'Value of type "None" is not indexable',
'Argument 1 to "set" has incompatible type "None"; expected "Iterable[<nothing>]"'
],
'migrations': [
'FakeMigration',
@@ -213,7 +261,8 @@ IGNORED_ERRORS = {
+ 'expected "Optional[Sequence[Union[Type[Model], str]]]"',
'Argument 1 to "RunPython" has incompatible type "str"; expected "Callable[..., Any]"',
'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': [
'Argument 1 to "append" of "list" has incompatible type "Tuple[Any, Any]"; expected "str"'
@@ -227,15 +276,20 @@ IGNORED_ERRORS = {
],
'postgres_tests': [
'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',
'DummyJSONField',
'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': [
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': [
'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"'
],
'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': [
'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"',
'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': [
'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")'
],
'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': [
'Too few arguments for "render" of "Template"'
@@ -289,11 +354,12 @@ IGNORED_ERRORS = {
'template_debug',
'"yield from" can\'t be applied to',
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': [
'Incompatible import of "Jinja2" (imported name has type "Type[Jinja2]", local name has type "object")',
'TemplateStringsTests'
'TemplateStringsTests',
],
'urlpatterns': [
'"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"
],
'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': [
'"None" has no attribute'
@@ -402,7 +469,7 @@ TESTS_DIRS = [
'flatpages_tests',
'force_insert_update',
'foreign_object',
# TODO: 'forms_tests',
'forms_tests',
'from_db_value',
'generic_inline_admin',
'generic_relations',

View File

@@ -31,12 +31,12 @@ if sys.version_info[:2] < (3, 7):
setup(
name="django-stubs",
version="0.5.0",
version="0.7.0",
description='Django mypy stubs',
long_description=readme,
long_description_content_type='text/markdown',
license='BSD',
url="https://github.com/mkurnikov/django-stubs.git",
license='MIT',
url="https://github.com/mkurnikov/django-stubs",
author="Maksim Kurnikov",
author_email="maxim.kurnikov@gmail.com",
py_modules=[],
@@ -46,6 +46,8 @@ setup(
package_data={'django-stubs': find_stub_files('django-stubs')},
classifiers=[
'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]
incremental = False
incremental = True
strict_optional = True
plugins =
mypy_django_plugin.main

View File

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

View File

@@ -6,7 +6,8 @@ class User(models.Model):
array = ArrayField(base_field=models.Field())
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]
from django.db import models
@@ -17,8 +18,9 @@ class User(models.Model):
members_as_text = ArrayField(base_field=models.CharField(max_length=255))
user = User()
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) # E: Revealed type is 'builtins.list*[builtins.int]'
reveal_type(user.members_as_text) # E: Revealed type is 'builtins.list*[builtins.str]'
[/CASE]
[CASE test_model_fields_classes_present_as_primitives]
from django.db import models
@@ -31,13 +33,14 @@ class User(models.Model):
text = models.TextField()
user = User()
reveal_type(user.id) # 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.slug) # E: Revealed type is 'builtins.str'
reveal_type(user.text) # E: Revealed type is 'builtins.str'
reveal_type(user.id) # 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.slug) # 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.contrib.postgres import fields as pg_fields
from decimal import Decimal
@@ -48,9 +51,10 @@ class Booking(models.Model):
some_decimal = models.DecimalField(max_digits=10, decimal_places=5)
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.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]
from django.db import models
@@ -59,6 +63,7 @@ class User(models.Model):
pass
reveal_type(User().id) # E: Revealed type is 'builtins.int'
[/CASE]
[CASE test_do_not_add_id_if_field_with_primary_key_True_defined]
from django.db import models
@@ -66,9 +71,9 @@ from django.db import models
class User(models.Model):
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'
[out]
[/CASE]
[CASE test_meta_nested_class_allows_subclassing_in_multiple_inheritance]
from typing import Any
@@ -84,7 +89,7 @@ class Mixin2(models.Model):
class User(Mixin1, Mixin2):
pass
[out]
[/CASE]
[CASE test_inheritance_from_abstract_model_does_not_fail_if_field_with_id_exists]
from django.db import models
@@ -93,4 +98,21 @@ class Abstract(models.Model):
abstract = True
class User(Abstract):
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()
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]
[CASE model_recognises_parent_attributes]
@@ -32,3 +32,33 @@ class Child1(Parent1, Parent2):
class Child4(Child1):
value4 = models.IntegerField()
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):
name = models.CharField(max_length=100)
age = models.IntegerField()
user = MyUser(name='hello', age='world')
user = MyUser(name='hello', age=[])
[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]
from django.db import models
@@ -63,7 +63,7 @@ from django.db import models
class MyUser1(models.Model):
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]
[CASE can_set_foreign_key_by_its_primary_key]
@@ -71,14 +71,15 @@ from django.db import models
class Publisher(models.Model):
pass
class PublisherWithCharPK(models.Model):
id = models.IntegerField(primary_key=True)
class PublisherDatetime(models.Model):
dt_pk = models.DateTimeField(primary_key=True)
class Book(models.Model):
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, 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=1)
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]
[CASE setting_value_to_an_array_of_ints]
@@ -94,7 +95,7 @@ MyModel(array=array_val)
array_val2: List[int] = [1]
MyModel(array=array_val2)
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]
[CASE if_no_explicit_primary_key_id_can_be_passed]
@@ -114,7 +115,7 @@ MyModel(1)
class MyModel2(models.Model):
name = models.IntegerField()
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]
[CASE arguments_passed_as_dictionary_unpacking_are_not_supported]
@@ -135,20 +136,6 @@ Restaurant(place_ptr=place)
Restaurant(place_ptr_id=place.id)
[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]
from django.db import models
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: 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
class App(models.Model):
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]
from django.db import models
@@ -198,7 +200,7 @@ class App(models.Model):
pass
class Member(models.Model):
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]'
[out]
@@ -207,7 +209,7 @@ from django.db import models
from myapp.models import App
class Member(models.Model):
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/models.py]
@@ -226,8 +228,8 @@ reveal_type(User().parent) # E: Revealed type is 'main.User*'
[CASE many_to_many_with_self]
from django.db import models
class User(models.Model):
friends = models.ManyToManyField('self', on_delete=models.CASCADE)
reveal_type(User().friends) # E: Revealed type is 'django.db.models.manager.RelatedManager[main.User*]'
friends = models.ManyToManyField('self')
reveal_type(User().friends) # E: Revealed type is 'django.db.models.manager.RelatedManager*[main.User]'
[out]
[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'
Book(publisher_id=1)
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='hello')
@@ -271,7 +273,17 @@ class Book2(models.Model):
reveal_type(Book2().publisher_id) # E: Revealed type is 'builtins.int'
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='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]

View File

@@ -1,4 +1,6 @@
[CASE test_settings_are_parsed_into_django_conf_settings]
[env DJANGO_SETTINGS_MODULE=mysettings]
[disable_cache]
from django.conf import settings
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.NUMBERS) # E: Revealed type is 'builtins.list[builtins.str]'
reveal_type(settings.DICT) # E: Revealed type is 'builtins.dict[Any, Any]'
[env DJANGO_SETTINGS_MODULE=mysettings]
[file base.py]
from pathlib import Path
ROOT_DIR = '/etc'
@@ -18,14 +19,16 @@ NUMBERS = ['one', 'two']
DICT = {} # type: ignore
from django.utils.functional import LazyObject
OBJ = LazyObject()
[/CASE]
[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
reveal_type(settings.ROOT_DIR) # E: Revealed type is 'pathlib.Path'
reveal_type(settings.SETUP) # E: Revealed type is 'builtins.int'
reveal_type(settings.DATABASES) # E: Revealed type is 'builtins.dict[builtins.str, builtins.str]'
[env DJANGO_SETTINGS_MODULE=mysettings]
[file mysettings.py]
from local import *
DATABASES = {'default': 'mydb'}
@@ -36,24 +39,25 @@ SETUP = 3
from pathlib import Path
ROOT_DIR = Path(__file__)
[/CASE]
[CASE global_settings_are_always_loaded]
from django.conf import settings
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]'
[out]
reveal_type(settings.AUTHENTICATION_BACKENDS) # E: Revealed type is 'typing.Sequence[builtins.str]'
[/CASE]
[CASE test_circular_dependency_in_settings_works_if_settings_have_annotations]
[env DJANGO_SETTINGS_MODULE=mysettings]
[disable_cache]
from django.conf import settings
class Class:
pass
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.LIST) # E: Revealed type is 'builtins.list[builtins.str]'
[out]
[env DJANGO_SETTINGS_MODULE=mysettings]
[file mysettings.py]
from typing import TYPE_CHECKING, Optional, List
@@ -63,13 +67,16 @@ if TYPE_CHECKING:
MYSETTING = 1122
REGISTRY: Optional['Class'] = None
LIST: List[str] = ['1', '2']
[/CASE]
[CASE fail_if_there_is_no_setting]
from django.conf import settings
reveal_type(settings.NOT_EXISTING)
[env DJANGO_SETTINGS_MODULE=mysettings]
[disable_cache]
[file mysettings.py]
[out]
main:2: error: Revealed type is 'Any'
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.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*]'
[out]
[/CASE]
[CASE get_user_model_returns_proper_class]
[env DJANGO_SETTINGS_MODULE=mysettings]
[disable_cache]
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from myapp.models import MyUser
@@ -22,7 +24,6 @@ from django.contrib.auth import get_user_model
UserModel = get_user_model()
reveal_type(UserModel.objects) # E: Revealed type is 'django.db.models.manager.Manager[myapp.models.MyUser]'
[env DJANGO_SETTINGS_MODULE=mysettings]
[file mysettings.py]
INSTALLED_APPS = ('myapp',)
AUTH_USER_MODEL = 'myapp.MyUser'
@@ -32,15 +33,16 @@ AUTH_USER_MODEL = 'myapp.MyUser'
from django.db import models
class MyUser(models.Model):
pass
[out]
[/CASE]
[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
UserModel = get_user_model()
reveal_type(UserModel.objects)
[env DJANGO_SETTINGS_MODULE=mysettings]
[file mysettings.py]
INSTALLED_APPS = ('myapp',)
AUTH_USER_MODEL = 'myapp.MyUser'
@@ -54,3 +56,4 @@ class MyUser(models.Model):
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: "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]