20 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
34 changed files with 658 additions and 524 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -8,7 +8,6 @@ from django.core.exceptions import FieldDoesNotExist as FieldDoesNotExist
from django.db.models.expressions import Combinable from django.db.models.expressions import Combinable
from django.db.models.query_utils import RegisterLookupMixin from django.db.models.query_utils import RegisterLookupMixin
from django.forms import Field as FormField, Widget from django.forms import Field as FormField, Widget
from typing_extensions import Literal
from .mixins import NOT_PROVIDED as NOT_PROVIDED from .mixins import NOT_PROVIDED as NOT_PROVIDED
@@ -72,7 +71,7 @@ class Field(RegisterLookupMixin, Generic[_ST, _GT]):
def to_python(self, value: Any) -> Any: ... def to_python(self, value: Any) -> Any: ...
class IntegerField(Field[_ST, _GT]): class IntegerField(Field[_ST, _GT]):
_pyi_private_set_type: Union[int, Combinable, Literal[""]] _pyi_private_set_type: Union[float, int, str, Combinable]
_pyi_private_get_type: int _pyi_private_get_type: int
class PositiveIntegerRelDbTypeMixin: class PositiveIntegerRelDbTypeMixin:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -22,6 +22,7 @@ QUERYSET_CLASS_FULLNAME = 'django.db.models.query.QuerySet'
BASE_MANAGER_CLASS_FULLNAME = 'django.db.models.manager.BaseManager' BASE_MANAGER_CLASS_FULLNAME = 'django.db.models.manager.BaseManager'
MANAGER_CLASS_FULLNAME = 'django.db.models.manager.Manager' MANAGER_CLASS_FULLNAME = 'django.db.models.manager.Manager'
RELATED_MANAGER_CLASS_FULLNAME = 'django.db.models.manager.RelatedManager' RELATED_MANAGER_CLASS_FULLNAME = 'django.db.models.manager.RelatedManager'
MODELFORM_CLASS_FULLNAME = 'django.forms.models.ModelForm'
MANAGER_CLASSES = { MANAGER_CLASSES = {
MANAGER_CLASS_FULLNAME, MANAGER_CLASS_FULLNAME,
@@ -54,9 +55,9 @@ def get_model_fullname(app_name: str, model_name: str,
return None return None
class InvalidModelString(ValueError): class SameFileModel(Exception):
def __init__(self, model_string: str): def __init__(self, model_cls_name: str):
self.model_string = model_string self.model_cls_name = model_cls_name
class SelfReference(ValueError): class SelfReference(ValueError):
@@ -69,7 +70,7 @@ def get_model_fullname_from_string(model_string: str,
raise SelfReference() raise SelfReference()
if '.' not in model_string: if '.' not in model_string:
raise InvalidModelString(model_string) raise SameFileModel(model_string)
app_name, model_name = model_string.split('.') app_name, model_name = model_string.split('.')
return get_model_fullname(app_name, model_name, all_modules) return get_model_fullname(app_name, model_name, all_modules)
@@ -246,7 +247,7 @@ def extract_primary_key_type_for_get(model: TypeInfo) -> Optional[Type]:
def make_optional(typ: Type): def make_optional(typ: Type):
return UnionType.make_simplified_union([typ, NoneTyp()]) return UnionType.make_union([typ, NoneTyp()])
def make_required(typ: Type) -> Type: def make_required(typ: Type) -> Type:
@@ -273,3 +274,10 @@ def has_any_of_bases(info: TypeInfo, bases: typing.Sequence[str]) -> bool:
def is_none_expr(expr: Expression) -> bool: def is_none_expr(expr: Expression) -> bool:
return isinstance(expr, NameExpr) and expr.fullname == 'builtins.None' 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

@@ -6,9 +6,11 @@ from mypy.nodes import MemberExpr, TypeInfo
from mypy.options import Options from mypy.options import Options
from mypy.plugin import AttributeContext, ClassDefContext, FunctionContext, MethodContext, Plugin from mypy.plugin import AttributeContext, ClassDefContext, FunctionContext, MethodContext, Plugin
from mypy.types import AnyType, Instance, Type, TypeOfAny, TypeType, UnionType from mypy.types import AnyType, Instance, Type, TypeOfAny, TypeType, UnionType
from mypy_django_plugin import helpers, monkeypatch from mypy_django_plugin import helpers, monkeypatch
from mypy_django_plugin.config import Config from mypy_django_plugin.config import Config
from mypy_django_plugin.transformers import fields, init_create 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, \ from mypy_django_plugin.transformers.migrations import determine_model_cls_from_string_for_migrations, \
get_string_value_from_expr get_string_value_from_expr
from mypy_django_plugin.transformers.models import process_model_class from mypy_django_plugin.transformers.models import process_model_class
@@ -33,6 +35,14 @@ def transform_manager_class(ctx: ClassDefContext) -> None:
sym.node.metadata['django']['manager_bases'][ctx.cls.fullname] = 1 sym.node.metadata['django']['manager_bases'][ctx.cls.fullname] = 1
def transform_modelform_class(ctx: ClassDefContext) -> None:
sym = ctx.api.lookup_fully_qualified_or_none(helpers.MODELFORM_CLASS_FULLNAME)
if sym is not None and isinstance(sym.node, TypeInfo):
sym.node.metadata['django']['modelform_bases'][ctx.cls.fullname] = 1
make_meta_nested_class_inherit_from_any(ctx)
def determine_proper_manager_type(ctx: FunctionContext) -> Type: def determine_proper_manager_type(ctx: FunctionContext) -> Type:
api = cast(TypeChecker, ctx.api) api = cast(TypeChecker, ctx.api)
ret = ctx.default_return_type ret = ctx.default_return_type
@@ -176,31 +186,32 @@ class DjangoPlugin(Plugin):
def _get_current_model_bases(self) -> Dict[str, int]: def _get_current_model_bases(self) -> Dict[str, int]:
model_sym = self.lookup_fully_qualified(helpers.MODEL_CLASS_FULLNAME) model_sym = self.lookup_fully_qualified(helpers.MODEL_CLASS_FULLNAME)
if model_sym is not None and isinstance(model_sym.node, TypeInfo): if model_sym is not None and isinstance(model_sym.node, TypeInfo):
if 'django' not in model_sym.node.metadata: return (model_sym.node.metadata
model_sym.node.metadata['django'] = { .setdefault('django', {})
'model_bases': {helpers.MODEL_CLASS_FULLNAME: 1} .setdefault('model_bases', {helpers.MODEL_CLASS_FULLNAME: 1}))
}
return model_sym.node.metadata['django']['model_bases']
else: else:
return {} return {}
def _get_current_manager_bases(self) -> Dict[str, int]: def _get_current_manager_bases(self) -> Dict[str, int]:
manager_sym = self.lookup_fully_qualified(helpers.MANAGER_CLASS_FULLNAME) model_sym = self.lookup_fully_qualified(helpers.MANAGER_CLASS_FULLNAME)
if manager_sym is not None and isinstance(manager_sym.node, TypeInfo): if model_sym is not None and isinstance(model_sym.node, TypeInfo):
if 'django' not in manager_sym.node.metadata: return (model_sym.node.metadata
manager_sym.node.metadata['django'] = { .setdefault('django', {})
'manager_bases': {helpers.MANAGER_CLASS_FULLNAME: 1} .setdefault('manager_bases', {helpers.MANAGER_CLASS_FULLNAME: 1}))
} else:
return manager_sym.node.metadata['django']['manager_bases'] return {}
def _get_current_modelform_bases(self) -> Dict[str, int]:
model_sym = self.lookup_fully_qualified(helpers.MODELFORM_CLASS_FULLNAME)
if model_sym is not None and isinstance(model_sym.node, TypeInfo):
return (model_sym.node.metadata
.setdefault('django', {})
.setdefault('modelform_bases', {helpers.MODELFORM_CLASS_FULLNAME: 1}))
else: else:
return {} return {}
def get_function_hook(self, fullname: str def get_function_hook(self, fullname: str
) -> Optional[Callable[[FunctionContext], Type]]: ) -> Optional[Callable[[FunctionContext], Type]]:
sym = self.lookup_fully_qualified(fullname)
if sym and isinstance(sym.node, TypeInfo) and sym.node.has_base(helpers.FIELD_FULLNAME):
return fields.adjust_return_type_of_field_instantiation
if fullname == 'django.contrib.auth.get_user_model': if fullname == 'django.contrib.auth.get_user_model':
return return_user_model_hook return return_user_model_hook
@@ -209,27 +220,27 @@ class DjangoPlugin(Plugin):
return determine_proper_manager_type return determine_proper_manager_type
sym = self.lookup_fully_qualified(fullname) sym = self.lookup_fully_qualified(fullname)
if sym and isinstance(sym.node, TypeInfo): if sym is not None and isinstance(sym.node, TypeInfo):
if sym.node.has_base(helpers.FIELD_FULLNAME):
return fields.adjust_return_type_of_field_instantiation
if sym.node.metadata.get('django', {}).get('generated_init'): if sym.node.metadata.get('django', {}).get('generated_init'):
return init_create.redefine_and_typecheck_model_init return init_create.redefine_and_typecheck_model_init
def get_method_hook(self, fullname: str def get_method_hook(self, fullname: str
) -> Optional[Callable[[MethodContext], Type]]: ) -> Optional[Callable[[MethodContext], Type]]:
if fullname in {'django.apps.registry.Apps.get_model',
'django.db.migrations.state.StateApps.get_model'}:
return determine_model_cls_from_string_for_migrations
manager_classes = self._get_current_manager_bases() manager_classes = self._get_current_manager_bases()
class_fullname, _, method_name = fullname.rpartition('.') class_fullname, _, method_name = fullname.rpartition('.')
if class_fullname in manager_classes and method_name == 'create': if class_fullname in manager_classes and method_name == 'create':
return init_create.redefine_and_typecheck_model_create return init_create.redefine_and_typecheck_model_create
if fullname in {'django.apps.registry.Apps.get_model',
'django.db.migrations.state.StateApps.get_model'}:
return determine_model_cls_from_string_for_migrations
return None return None
def get_base_class_hook(self, fullname: str def get_base_class_hook(self, fullname: str
) -> Optional[Callable[[ClassDefContext], None]]: ) -> Optional[Callable[[ClassDefContext], None]]:
if fullname in self._get_current_model_bases():
return transform_model_class
if fullname == helpers.DUMMY_SETTINGS_BASE_CLASS: if fullname == helpers.DUMMY_SETTINGS_BASE_CLASS:
settings_modules = ['django.conf.global_settings'] settings_modules = ['django.conf.global_settings']
if self.django_settings_module: if self.django_settings_module:
@@ -237,13 +248,22 @@ class DjangoPlugin(Plugin):
return AddSettingValuesToDjangoConfObject(settings_modules, return AddSettingValuesToDjangoConfObject(settings_modules,
self.config.ignore_missing_settings) self.config.ignore_missing_settings)
if fullname in self._get_current_model_bases():
return transform_model_class
if fullname in self._get_current_manager_bases(): if fullname in self._get_current_manager_bases():
return transform_manager_class return transform_manager_class
if fullname in self._get_current_modelform_bases():
return transform_modelform_class
return None return None
def get_attribute_hook(self, fullname: str def get_attribute_hook(self, fullname: str
) -> Optional[Callable[[AttributeContext], Type]]: ) -> Optional[Callable[[AttributeContext], Type]]:
if fullname == 'builtins.object.id':
return return_integer_type_for_id_for_non_defined_primary_key_in_models
module, _, name = fullname.rpartition('.') module, _, name = fullname.rpartition('.')
sym = self.lookup_fully_qualified('django.conf.LazySettings') sym = self.lookup_fully_qualified('django.conf.LazySettings')
if sym and isinstance(sym.node, TypeInfo): if sym and isinstance(sym.node, TypeInfo):
@@ -251,9 +271,6 @@ class DjangoPlugin(Plugin):
if module == 'builtins.object' and name in metadata: if module == 'builtins.object' and name in metadata:
return ExtractSettingType(module_fullname=metadata[name]) return ExtractSettingType(module_fullname=metadata[name])
if fullname == 'builtins.object.id':
return return_integer_type_for_id_for_non_defined_primary_key_in_models
return extract_and_return_primary_key_of_bound_related_field_parameter return extract_and_return_primary_key_of_bound_related_field_parameter

View File

@@ -8,7 +8,7 @@ from mypy_django_plugin import helpers
from mypy_django_plugin.transformers.models import iter_over_assignments from mypy_django_plugin.transformers.models import iter_over_assignments
def get_valid_to_value_or_none(ctx: FunctionContext) -> Optional[Instance]: def extract_referred_to_type(ctx: FunctionContext) -> Optional[Instance]:
api = cast(TypeChecker, ctx.api) api = cast(TypeChecker, ctx.api)
if 'to' not in ctx.callee_arg_names: if 'to' not in ctx.callee_arg_names:
api.msg.fail(f'to= parameter must be set for {ctx.context.callee.fullname}', api.msg.fail(f'to= parameter must be set for {ctx.context.callee.fullname}',
@@ -27,6 +27,9 @@ def get_valid_to_value_or_none(ctx: FunctionContext) -> Optional[Instance]:
except helpers.SelfReference: except helpers.SelfReference:
model_fullname = api.tscope.classes[-1].fullname() 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: if model_fullname is None:
return None return None
model_info = helpers.lookup_fully_qualified_generic(model_fullname, model_info = helpers.lookup_fully_qualified_generic(model_fullname,
@@ -52,8 +55,8 @@ def convert_any_to_type(typ: Type, referred_to_type: Type) -> Type:
converted_items = [] converted_items = []
for item in typ.items: for item in typ.items:
converted_items.append(convert_any_to_type(item, referred_to_type)) converted_items.append(convert_any_to_type(item, referred_to_type))
return UnionType.make_simplified_union(converted_items, return UnionType.make_union(converted_items,
line=typ.line, column=typ.column) line=typ.line, column=typ.column)
if isinstance(typ, Instance): if isinstance(typ, Instance):
args = [] args = []
for default_arg in typ.args: for default_arg in typ.args:
@@ -69,19 +72,9 @@ def convert_any_to_type(typ: Type, referred_to_type: Type) -> Type:
return typ return typ
def _extract_referred_to_type(ctx: FunctionContext) -> Optional[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 None
return referred_to_type
def fill_descriptor_types_for_related_field(ctx: FunctionContext) -> Type: def fill_descriptor_types_for_related_field(ctx: FunctionContext) -> Type:
default_return_type = set_descriptor_types_for_field(ctx) default_return_type = set_descriptor_types_for_field(ctx)
referred_to_type = _extract_referred_to_type(ctx) referred_to_type = extract_referred_to_type(ctx)
if referred_to_type is None: if referred_to_type is None:
return default_return_type return default_return_type

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

@@ -2,7 +2,7 @@ from abc import ABCMeta, abstractmethod
from typing import Dict, Iterator, List, Optional, Tuple, cast from typing import Dict, Iterator, List, Optional, Tuple, cast
import dataclasses import dataclasses
from mypy.nodes import ARG_STAR, ARG_STAR2, Argument, CallExpr, ClassDef, Context, Expression, IndexExpr, \ from mypy.nodes import ARG_STAR, ARG_STAR2, Argument, CallExpr, ClassDef, Expression, IndexExpr, \
Lvalue, MDEF, MemberExpr, MypyFile, NameExpr, StrExpr, SymbolTableNode, TypeInfo, Var Lvalue, MDEF, MemberExpr, MypyFile, NameExpr, StrExpr, SymbolTableNode, TypeInfo, Var
from mypy.plugin import ClassDefContext from mypy.plugin import ClassDefContext
from mypy.plugins.common import add_method from mypy.plugins.common import add_method
@@ -21,14 +21,8 @@ class ModelClassInitializer(metaclass=ABCMeta):
def from_ctx(cls, ctx: ClassDefContext): def from_ctx(cls, ctx: ClassDefContext):
return cls(api=cast(SemanticAnalyzerPass2, ctx.api), model_classdef=ctx.cls) return cls(api=cast(SemanticAnalyzerPass2, ctx.api), model_classdef=ctx.cls)
def get_nested_meta_node(self) -> Optional[TypeInfo]:
metaclass_sym = self.model_classdef.info.names.get('Meta')
if metaclass_sym is not None and isinstance(metaclass_sym.node, TypeInfo):
return metaclass_sym.node
return None
def get_meta_attribute(self, name: str) -> Optional[Expression]: def get_meta_attribute(self, name: str) -> Optional[Expression]:
meta_node = self.get_nested_meta_node() meta_node = helpers.get_nested_meta_node_for_current_class(self.model_classdef.info)
if meta_node is None: if meta_node is None:
return None return None
@@ -80,7 +74,7 @@ class SetIdAttrsForRelatedFields(ModelClassInitializer):
class InjectAnyAsBaseForNestedMeta(ModelClassInitializer): class InjectAnyAsBaseForNestedMeta(ModelClassInitializer):
def run(self) -> None: def run(self) -> None:
meta_node = self.get_nested_meta_node() meta_node = helpers.get_nested_meta_node_for_current_class(self.model_classdef.info)
if meta_node is None: if meta_node is None:
return None return None
meta_node.fallback_to_any = True meta_node.fallback_to_any = True
@@ -173,10 +167,9 @@ class AddRelatedManagers(ModelClassInitializer):
all_modules=self.api.modules) all_modules=self.api.modules)
except helpers.SelfReference: except helpers.SelfReference:
ref_to_fullname = defn.fullname ref_to_fullname = defn.fullname
except helpers.InvalidModelString as exc:
self.api.fail(f'Invalid value for a to= parameter: {exc.model_string!r}', except helpers.SameFileModel as exc:
Context(line=rvalue.line)) ref_to_fullname = module_name + '.' + exc.model_cls_name
return None
if self.model_classdef.fullname == ref_to_fullname: if self.model_classdef.fullname == ref_to_fullname:
related_manager_name = defn.name.lower() + '_set' related_manager_name = defn.name.lower() + '_set'

View File

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

View File

@@ -32,7 +32,7 @@ IGNORED_ERRORS = {
'Cannot assign to a type', 'Cannot assign to a type',
re.compile(r'Cannot assign to class variable "[a-z_]+" via instance'), re.compile(r'Cannot assign to class variable "[a-z_]+" via instance'),
# forms <-> models plugin support # forms <-> models plugin support
'"Model" has no attribute', # '"Model" has no attribute',
re.compile(r'Cannot determine type of \'(objects|stuff)\''), re.compile(r'Cannot determine type of \'(objects|stuff)\''),
# settings # settings
re.compile(r'Module has no attribute "[A-Z_]+"'), re.compile(r'Module has no attribute "[A-Z_]+"'),
@@ -54,7 +54,8 @@ IGNORED_ERRORS = {
'ValuesIterable', 'ValuesIterable',
'Value of type "Optional[Dict[str, Any]]" is not indexable', '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 "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]"' 'Argument 1 to "loads" has incompatible type "Union[bytes, str, None]"; expected "Union[str, bytes, bytearray]"',
'Incompatible types in assignment (expression has type "None", variable has type Module)'
], ],
'admin_changelist': [ 'admin_changelist': [
'Incompatible types in assignment (expression has type "FilteredChildAdmin", variable has type "ChildAdmin")' 'Incompatible types in assignment (expression has type "FilteredChildAdmin", variable has type "ChildAdmin")'
@@ -166,6 +167,29 @@ IGNORED_ERRORS = {
'fixtures': [ 'fixtures': [
'Incompatible types in assignment (expression has type "int", target has type "Iterable[str]")' 'Incompatible types in assignment (expression has type "int", target has type "Iterable[str]")'
], ],
'forms_tests': [
'List item 0 has incompatible type "Jinja2"; expected "DjangoTemplates"',
'Not enough arguments for format string',
'Argument after ** must be a mapping, not "object"',
'"media" undefined in superclass',
'expression has type "None", base class "TestFormParent"',
'variable has type "SongForm"',
'"full_clean" of "BaseForm" does not return a value',
'No overload variant of "zip" matches argument types "Tuple[str, str, str]", "object"',
'note:',
'Incompatible types in assignment (expression has type "GetDateShowHiddenInitial", variable has type "GetDate")',
re.compile(r'Incompatible types in assignment \(expression has type "[a-zA-Z]+Field", '
r'base class "BaseForm" defined the type as "Dict\[str, Any\]"\)'),
'List or tuple expected as variable arguments',
'Argument 1 to "__init__" of "MultiWidget" has incompatible type "List[object]"; '
+ 'expected "Sequence[Union[Widget, Type[Widget]]]"',
'Argument 1 to "issubclass" has incompatible type "ModelFormMetaclass"; expected "type"',
'Incompatible types in assignment (expression has type "List[str]", target has type "str")',
'Incompatible types in assignment (expression has type "TestForm", variable has type "Person")',
'Incompatible types in assignment (expression has type "Type[Textarea]", '
+ 'base class "Field" defined the type as "Widget")',
'Incompatible types in assignment (expression has type "SimpleUploadedFile", variable has type "BinaryIO")'
],
'get_object_or_404': [ 'get_object_or_404': [
'Argument 1 to "get_object_or_404" has incompatible type "str"; ' 'Argument 1 to "get_object_or_404" has incompatible type "str"; '
+ 'expected "Union[Type[<nothing>], Manager[<nothing>], QuerySet[<nothing>]]"', + 'expected "Union[Type[<nothing>], Manager[<nothing>], QuerySet[<nothing>]]"',
@@ -208,6 +232,12 @@ IGNORED_ERRORS = {
+ 'base class "ImageFieldTestMixin" defined the type as "Type[PersonWithHeightAndWidth]")', + 'base class "ImageFieldTestMixin" defined the type as "Type[PersonWithHeightAndWidth]")',
'note: "Person" defined here' 'note: "Person" defined here'
], ],
'model_formsets': [
'Incompatible types in string interpolation (expression has type "object", placeholder has type "Union[int, float]")'
],
'model_formsets_regress': [
'Incompatible types in assignment (expression has type "Model", variable has type "User")'
],
'model_regress': [ 'model_regress': [
'Too many arguments for "Worker"', 'Too many arguments for "Worker"',
re.compile(r'Incompatible type for "[a-z]+" of "Worker" \(got "int", expected') re.compile(r'Incompatible type for "[a-z]+" of "Worker" \(got "int", expected')
@@ -286,7 +316,8 @@ IGNORED_ERRORS = {
], ],
'schema': [ 'schema': [
'Incompatible type for "info" of "Note" (got "None", expected "Union[str, Combinable]")', '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 "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': [ 'settings_tests': [
'Argument 1 to "Settings" has incompatible type "Optional[str]"; expected "str"' 'Argument 1 to "Settings" has incompatible type "Optional[str]"; expected "str"'
@@ -329,7 +360,6 @@ IGNORED_ERRORS = {
'template_backends': [ 'template_backends': [
'Incompatible import of "Jinja2" (imported name has type "Type[Jinja2]", local name has type "object")', 'Incompatible import of "Jinja2" (imported name has type "Type[Jinja2]", local name has type "object")',
'TemplateStringsTests', 'TemplateStringsTests',
'Incompatible types in assignment (expression has type "None", variable has type Module)'
], ],
'urlpatterns': [ 'urlpatterns': [
'"object" has no attribute "__iter__"; maybe "__str__" or "__dir__"? (not iterable)', '"object" has no attribute "__iter__"; maybe "__str__" or "__dir__"? (not iterable)',
@@ -348,7 +378,7 @@ IGNORED_ERRORS = {
], ],
'sessions_tests': [ 'sessions_tests': [
'base class "SessionTestsMixin" defined the type as "None")', 'base class "SessionTestsMixin" defined the type as "None")',
'Incompatible types in assignment (expression has type "None", variable has type "int")' 'Incompatible types in assignment (expression has type "None", variable has type "int")',
], ],
'select_related_onetoone': [ 'select_related_onetoone': [
'"None" has no attribute' '"None" has no attribute'
@@ -439,7 +469,7 @@ TESTS_DIRS = [
'flatpages_tests', 'flatpages_tests',
'force_insert_update', 'force_insert_update',
'foreign_object', 'foreign_object',
# TODO: 'forms_tests', 'forms_tests',
'from_db_value', 'from_db_value',
'generic_inline_admin', 'generic_inline_admin',
'generic_relations', 'generic_relations',

View File

@@ -31,7 +31,7 @@ if sys.version_info[:2] < (3, 7):
setup( setup(
name="django-stubs", name="django-stubs",
version="0.5.1", version="0.7.0",
description='Django mypy stubs', description='Django mypy stubs',
long_description=readme, long_description=readme,
long_description_content_type='text/markdown', long_description_content_type='text/markdown',

View File

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

View File

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

View File

@@ -7,6 +7,7 @@ class User(models.Model):
user = User() user = User()
reveal_type(user.array) # E: Revealed type is 'builtins.list*[Any]' reveal_type(user.array) # E: Revealed type is 'builtins.list*[Any]'
[/CASE]
[CASE array_field_base_field_parsed_into_generic_typevar] [CASE array_field_base_field_parsed_into_generic_typevar]
from django.db import models from django.db import models
@@ -19,6 +20,7 @@ class User(models.Model):
user = User() user = User()
reveal_type(user.members) # E: Revealed type is 'builtins.list*[builtins.int]' reveal_type(user.members) # E: Revealed type is 'builtins.list*[builtins.int]'
reveal_type(user.members_as_text) # E: Revealed type is 'builtins.list*[builtins.str]' reveal_type(user.members_as_text) # E: Revealed type is 'builtins.list*[builtins.str]'
[/CASE]
[CASE test_model_fields_classes_present_as_primitives] [CASE test_model_fields_classes_present_as_primitives]
from django.db import models from django.db import models
@@ -36,6 +38,7 @@ reveal_type(user.small_int) # E: Revealed type is 'builtins.int*'
reveal_type(user.name) # E: Revealed type is 'builtins.str*' reveal_type(user.name) # E: Revealed type is 'builtins.str*'
reveal_type(user.slug) # E: Revealed type is 'builtins.str*' reveal_type(user.slug) # E: Revealed type is 'builtins.str*'
reveal_type(user.text) # E: Revealed type is 'builtins.str*' reveal_type(user.text) # E: Revealed type is 'builtins.str*'
[/CASE]
[CASE test_model_field_classes_from_existing_locations] [CASE test_model_field_classes_from_existing_locations]
from django.db import models from django.db import models
@@ -51,6 +54,7 @@ booking = Booking()
reveal_type(booking.id) # E: Revealed type is 'builtins.int*' reveal_type(booking.id) # E: Revealed type is 'builtins.int*'
reveal_type(booking.time_range) # E: Revealed type is 'Any' reveal_type(booking.time_range) # E: Revealed type is 'Any'
reveal_type(booking.some_decimal) # E: Revealed type is 'decimal.Decimal*' reveal_type(booking.some_decimal) # E: Revealed type is 'decimal.Decimal*'
[/CASE]
[CASE test_add_id_field_if_no_primary_key_defined] [CASE test_add_id_field_if_no_primary_key_defined]
from django.db import models from django.db import models
@@ -59,6 +63,7 @@ class User(models.Model):
pass pass
reveal_type(User().id) # E: Revealed type is 'builtins.int' reveal_type(User().id) # E: Revealed type is 'builtins.int'
[/CASE]
[CASE test_do_not_add_id_if_field_with_primary_key_True_defined] [CASE test_do_not_add_id_if_field_with_primary_key_True_defined]
from django.db import models from django.db import models
@@ -68,7 +73,7 @@ class User(models.Model):
reveal_type(User().my_pk) # E: Revealed type is 'builtins.int*' reveal_type(User().my_pk) # E: Revealed type is 'builtins.int*'
reveal_type(User().id) # E: Revealed type is 'Any' reveal_type(User().id) # E: Revealed type is 'Any'
[out] [/CASE]
[CASE test_meta_nested_class_allows_subclassing_in_multiple_inheritance] [CASE test_meta_nested_class_allows_subclassing_in_multiple_inheritance]
from typing import Any from typing import Any
@@ -84,7 +89,7 @@ class Mixin2(models.Model):
class User(Mixin1, Mixin2): class User(Mixin1, Mixin2):
pass pass
[out] [/CASE]
[CASE test_inheritance_from_abstract_model_does_not_fail_if_field_with_id_exists] [CASE test_inheritance_from_abstract_model_does_not_fail_if_field_with_id_exists]
from django.db import models from django.db import models
@@ -93,7 +98,7 @@ class Abstract(models.Model):
abstract = True abstract = True
class User(Abstract): class User(Abstract):
id = models.AutoField(primary_key=True) id = models.AutoField(primary_key=True)
[out] [/CASE]
[CASE standard_it_from_parent_model_could_be_overridden_with_non_integer_field_in_child_model] [CASE standard_it_from_parent_model_could_be_overridden_with_non_integer_field_in_child_model]
from django.db import models from django.db import models
@@ -103,11 +108,11 @@ class ParentModel(models.Model):
class MyModel(ParentModel): class MyModel(ParentModel):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
reveal_type(MyModel().id) # E: Revealed type is 'uuid.UUID*' reveal_type(MyModel().id) # E: Revealed type is 'uuid.UUID*'
[out] [/CASE]
[CASE blank_for_charfield_is_the_same_as_null] [CASE blank_for_charfield_is_the_same_as_null]
from django.db import models from django.db import models
class MyModel(models.Model): class MyModel(models.Model):
text = models.CharField(max_length=30, blank=True) text = models.CharField(max_length=30, blank=True)
MyModel(text=None) MyModel(text=None)
[out] [/CASE]

View File

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

View File

@@ -6,7 +6,7 @@ class User(models.Model):
age = models.IntegerField() age = models.IntegerField()
User.objects.create(name='Max', age=10) User.objects.create(name='Max', age=10)
User.objects.create(age='hello') # E: Incompatible type for "age" of "User" (got "str", expected "Union[int, Combinable, Literal['']]") User.objects.create(age=[]) # E: Incompatible type for "age" of "User" (got "List[Any]", expected "Union[float, int, str, Combinable]")
[out] [out]
[CASE model_recognises_parent_attributes] [CASE model_recognises_parent_attributes]
@@ -59,6 +59,6 @@ MyModel.objects.create(id=None)
class MyModel2(models.Model): class MyModel2(models.Model):
id = models.IntegerField(primary_key=True, default=None) id = models.IntegerField(primary_key=True, default=None)
MyModel2(id=None) # E: Incompatible type for "id" of "MyModel2" (got "None", expected "Union[int, Combinable, Literal['']]") 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[int, Combinable, Literal['']]") MyModel2.objects.create(id=None) # E: Incompatible type for "id" of "MyModel2" (got "None", expected "Union[float, int, str, Combinable]")
[out] [out]

View File

@@ -14,9 +14,9 @@ from django.db import models
class MyUser(models.Model): class MyUser(models.Model):
name = models.CharField(max_length=100) name = models.CharField(max_length=100)
age = models.IntegerField() age = models.IntegerField()
user = MyUser(name='hello', age='world') user = MyUser(name='hello', age=[])
[out] [out]
main:6: error: Incompatible type for "age" of "MyUser" (got "str", expected "Union[int, Combinable, Literal['']]") main:6: error: Incompatible type for "age" of "MyUser" (got "List[Any]", expected "Union[float, int, str, Combinable]")
[CASE arguments_to_init_combined_from_base_classes] [CASE arguments_to_init_combined_from_base_classes]
from django.db import models from django.db import models
@@ -63,7 +63,7 @@ from django.db import models
class MyUser1(models.Model): class MyUser1(models.Model):
mypk = models.IntegerField(primary_key=True) mypk = models.IntegerField(primary_key=True)
user = MyUser1(pk='hello') # E: Incompatible type for "pk" of "MyUser1" (got "str", expected "Union[int, Combinable, Literal['']]") user = MyUser1(pk=[]) # E: Incompatible type for "pk" of "MyUser1" (got "List[Any]", expected "Union[float, int, str, Combinable]")
[out] [out]
[CASE can_set_foreign_key_by_its_primary_key] [CASE can_set_foreign_key_by_its_primary_key]
@@ -115,7 +115,7 @@ MyModel(1)
class MyModel2(models.Model): class MyModel2(models.Model):
name = models.IntegerField() name = models.IntegerField()
MyModel2(1, 12) MyModel2(1, 12)
MyModel2(1, 'Maxim') # E: Incompatible type for "name" of "MyModel2" (got "str", expected "Union[int, Combinable, Literal['']]") MyModel2(1, []) # E: Incompatible type for "name" of "MyModel2" (got "List[Any]", expected "Union[float, int, str, Combinable]")
[out] [out]
[CASE arguments_passed_as_dictionary_unpacking_are_not_supported] [CASE arguments_passed_as_dictionary_unpacking_are_not_supported]

View File

@@ -191,6 +191,8 @@ class Profile(models.Model):
from django.db import models from django.db import models
class App(models.Model): class App(models.Model):
owner = models.ForeignKey(to='myapp.User', on_delete=models.CASCADE, related_name='apps') owner = models.ForeignKey(to='myapp.User', on_delete=models.CASCADE, related_name='apps')
[disable_cache]
[/CASE]
[CASE many_to_many_field_converts_to_queryset_of_model_type] [CASE many_to_many_field_converts_to_queryset_of_model_type]
from django.db import models from django.db import models
@@ -271,7 +273,17 @@ class Book2(models.Model):
reveal_type(Book2().publisher_id) # E: Revealed type is 'builtins.int' reveal_type(Book2().publisher_id) # E: Revealed type is 'builtins.int'
Book2(publisher_id=1) Book2(publisher_id=1)
Book2(publisher_id='hello') # E: Incompatible type for "publisher_id" of "Book2" (got "str", expected "Union[int, Combinable, Literal[''], None]") Book2(publisher_id=[]) # E: Incompatible type for "publisher_id" of "Book2" (got "List[Any]", expected "Union[float, int, str, Combinable, None]")
Book2.objects.create(publisher_id=1) Book2.objects.create(publisher_id=1)
Book2.objects.create(publisher_id='hello') # E: Incompatible type for "publisher_id" of "Book2" (got "str", expected "Union[int, Combinable, Literal['']]") Book2.objects.create(publisher_id=[]) # E: Incompatible type for "publisher_id" of "Book2" (got "List[Any]", expected "Union[float, int, str, Combinable]")
[out] [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] [CASE test_settings_are_parsed_into_django_conf_settings]
[env DJANGO_SETTINGS_MODULE=mysettings]
[disable_cache]
from django.conf import settings from django.conf import settings
reveal_type(settings.ROOT_DIR) # E: Revealed type is 'builtins.str' reveal_type(settings.ROOT_DIR) # E: Revealed type is 'builtins.str'
@@ -6,7 +8,6 @@ reveal_type(settings.APPS_DIR) # E: Revealed type is 'pathlib.Path'
reveal_type(settings.OBJ) # E: Revealed type is 'django.utils.functional.LazyObject' reveal_type(settings.OBJ) # E: Revealed type is 'django.utils.functional.LazyObject'
reveal_type(settings.NUMBERS) # E: Revealed type is 'builtins.list[builtins.str]' reveal_type(settings.NUMBERS) # E: Revealed type is 'builtins.list[builtins.str]'
reveal_type(settings.DICT) # E: Revealed type is 'builtins.dict[Any, Any]' reveal_type(settings.DICT) # E: Revealed type is 'builtins.dict[Any, Any]'
[env DJANGO_SETTINGS_MODULE=mysettings]
[file base.py] [file base.py]
from pathlib import Path from pathlib import Path
ROOT_DIR = '/etc' ROOT_DIR = '/etc'
@@ -18,14 +19,16 @@ NUMBERS = ['one', 'two']
DICT = {} # type: ignore DICT = {} # type: ignore
from django.utils.functional import LazyObject from django.utils.functional import LazyObject
OBJ = LazyObject() OBJ = LazyObject()
[/CASE]
[CASE test_settings_could_be_defined_in_different_module_and_imported_with_star] [CASE test_settings_could_be_defined_in_different_module_and_imported_with_star]
[env DJANGO_SETTINGS_MODULE=mysettings]
[disable_cache]
from django.conf import settings from django.conf import settings
reveal_type(settings.ROOT_DIR) # E: Revealed type is 'pathlib.Path' reveal_type(settings.ROOT_DIR) # E: Revealed type is 'pathlib.Path'
reveal_type(settings.SETUP) # E: Revealed type is 'builtins.int' reveal_type(settings.SETUP) # E: Revealed type is 'builtins.int'
reveal_type(settings.DATABASES) # E: Revealed type is 'builtins.dict[builtins.str, builtins.str]' reveal_type(settings.DATABASES) # E: Revealed type is 'builtins.dict[builtins.str, builtins.str]'
[env DJANGO_SETTINGS_MODULE=mysettings]
[file mysettings.py] [file mysettings.py]
from local import * from local import *
DATABASES = {'default': 'mydb'} DATABASES = {'default': 'mydb'}
@@ -36,24 +39,25 @@ SETUP = 3
from pathlib import Path from pathlib import Path
ROOT_DIR = Path(__file__) ROOT_DIR = Path(__file__)
[/CASE]
[CASE global_settings_are_always_loaded] [CASE global_settings_are_always_loaded]
from django.conf import settings from django.conf import settings
reveal_type(settings.AUTH_USER_MODEL) # E: Revealed type is 'builtins.str' reveal_type(settings.AUTH_USER_MODEL) # E: Revealed type is 'builtins.str'
reveal_type(settings.AUTHENTICATION_BACKENDS) # E: Revealed type is 'builtins.list[builtins.str]' reveal_type(settings.AUTHENTICATION_BACKENDS) # E: Revealed type is 'typing.Sequence[builtins.str]'
[out] [/CASE]
[CASE test_circular_dependency_in_settings_works_if_settings_have_annotations] [CASE test_circular_dependency_in_settings_works_if_settings_have_annotations]
[env DJANGO_SETTINGS_MODULE=mysettings]
[disable_cache]
from django.conf import settings from django.conf import settings
class Class: class Class:
pass pass
reveal_type(settings.MYSETTING) # E: Revealed type is 'builtins.int' reveal_type(settings.MYSETTING) # E: Revealed type is 'builtins.int'
reveal_type(settings.REGISTRY) # E: Revealed type is 'Union[main.Class, None]' reveal_type(settings.REGISTRY) # E: Revealed type is 'Union[main.Class, None]'
reveal_type(settings.LIST) # E: Revealed type is 'builtins.list[builtins.str]' reveal_type(settings.LIST) # E: Revealed type is 'builtins.list[builtins.str]'
[out]
[env DJANGO_SETTINGS_MODULE=mysettings]
[file mysettings.py] [file mysettings.py]
from typing import TYPE_CHECKING, Optional, List from typing import TYPE_CHECKING, Optional, List
@@ -63,13 +67,16 @@ if TYPE_CHECKING:
MYSETTING = 1122 MYSETTING = 1122
REGISTRY: Optional['Class'] = None REGISTRY: Optional['Class'] = None
LIST: List[str] = ['1', '2'] LIST: List[str] = ['1', '2']
[/CASE]
[CASE fail_if_there_is_no_setting] [CASE fail_if_there_is_no_setting]
from django.conf import settings from django.conf import settings
reveal_type(settings.NOT_EXISTING) reveal_type(settings.NOT_EXISTING)
[env DJANGO_SETTINGS_MODULE=mysettings] [env DJANGO_SETTINGS_MODULE=mysettings]
[disable_cache]
[file mysettings.py] [file mysettings.py]
[out] [out]
main:2: error: Revealed type is 'Any' main:2: error: Revealed type is 'Any'
main:2: error: "LazySettings" has no attribute "NOT_EXISTING" main:2: error: "LazySettings" has no attribute "NOT_EXISTING"
[/CASE]

View File

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

View File

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