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.
Supports Python 3.6/3.7, and Django 2.1.x series.
Could be run on earlier versions of Django, but expect some missing imports warnings.
## Installation
```

View File

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

View File

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

View File

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

View File

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

View File

@@ -8,7 +8,6 @@ from django.core.exceptions import FieldDoesNotExist as FieldDoesNotExist
from django.db.models.expressions import Combinable
from django.db.models.query_utils import RegisterLookupMixin
from django.forms import Field as FormField, Widget
from typing_extensions import Literal
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: ...
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
class PositiveIntegerRelDbTypeMixin:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -8,7 +8,7 @@ from mypy_django_plugin import helpers
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)
if 'to' not in ctx.callee_arg_names:
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:
model_fullname = api.tscope.classes[-1].fullname()
except helpers.SameFileModel as exc:
model_fullname = api.tscope.classes[-1].module_name + '.' + exc.model_cls_name
if model_fullname is None:
return None
model_info = helpers.lookup_fully_qualified_generic(model_fullname,
@@ -52,8 +55,8 @@ def convert_any_to_type(typ: Type, referred_to_type: Type) -> Type:
converted_items = []
for item in typ.items:
converted_items.append(convert_any_to_type(item, referred_to_type))
return UnionType.make_simplified_union(converted_items,
line=typ.line, column=typ.column)
return UnionType.make_union(converted_items,
line=typ.line, column=typ.column)
if isinstance(typ, Instance):
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
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:
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:
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
import dataclasses
from mypy.nodes import ARG_STAR, ARG_STAR2, Argument, CallExpr, ClassDef, Context, Expression, IndexExpr, \
from mypy.nodes import ARG_STAR, ARG_STAR2, Argument, CallExpr, ClassDef, Expression, IndexExpr, \
Lvalue, MDEF, MemberExpr, MypyFile, NameExpr, StrExpr, SymbolTableNode, TypeInfo, Var
from mypy.plugin import ClassDefContext
from mypy.plugins.common import add_method
@@ -21,14 +21,8 @@ class ModelClassInitializer(metaclass=ABCMeta):
def from_ctx(cls, ctx: ClassDefContext):
return cls(api=cast(SemanticAnalyzerPass2, ctx.api), model_classdef=ctx.cls)
def get_nested_meta_node(self) -> Optional[TypeInfo]:
metaclass_sym = self.model_classdef.info.names.get('Meta')
if metaclass_sym is not None and isinstance(metaclass_sym.node, TypeInfo):
return metaclass_sym.node
return None
def get_meta_attribute(self, name: str) -> Optional[Expression]:
meta_node = self.get_nested_meta_node()
meta_node = helpers.get_nested_meta_node_for_current_class(self.model_classdef.info)
if meta_node is None:
return None
@@ -80,7 +74,7 @@ class SetIdAttrsForRelatedFields(ModelClassInitializer):
class InjectAnyAsBaseForNestedMeta(ModelClassInitializer):
def run(self) -> None:
meta_node = self.get_nested_meta_node()
meta_node = helpers.get_nested_meta_node_for_current_class(self.model_classdef.info)
if meta_node is None:
return None
meta_node.fallback_to_any = True
@@ -173,10 +167,9 @@ class AddRelatedManagers(ModelClassInitializer):
all_modules=self.api.modules)
except helpers.SelfReference:
ref_to_fullname = defn.fullname
except helpers.InvalidModelString as exc:
self.api.fail(f'Invalid value for a to= parameter: {exc.model_string!r}',
Context(line=rvalue.line))
return None
except helpers.SameFileModel as exc:
ref_to_fullname = module_name + '.' + exc.model_cls_name
if self.model_classdef.fullname == ref_to_fullname:
related_manager_name = defn.name.lower() + '_set'

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -7,6 +7,7 @@ class User(models.Model):
user = User()
reveal_type(user.array) # E: Revealed type is 'builtins.list*[Any]'
[/CASE]
[CASE array_field_base_field_parsed_into_generic_typevar]
from django.db import models
@@ -19,6 +20,7 @@ class User(models.Model):
user = User()
reveal_type(user.members) # E: Revealed type is 'builtins.list*[builtins.int]'
reveal_type(user.members_as_text) # E: Revealed type is 'builtins.list*[builtins.str]'
[/CASE]
[CASE test_model_fields_classes_present_as_primitives]
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.slug) # 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]
from django.db import models
@@ -51,6 +54,7 @@ booking = Booking()
reveal_type(booking.id) # E: Revealed type is 'builtins.int*'
reveal_type(booking.time_range) # E: Revealed type is 'Any'
reveal_type(booking.some_decimal) # E: Revealed type is 'decimal.Decimal*'
[/CASE]
[CASE test_add_id_field_if_no_primary_key_defined]
from django.db import models
@@ -59,6 +63,7 @@ class User(models.Model):
pass
reveal_type(User().id) # E: Revealed type is 'builtins.int'
[/CASE]
[CASE test_do_not_add_id_if_field_with_primary_key_True_defined]
from django.db import models
@@ -68,7 +73,7 @@ class User(models.Model):
reveal_type(User().my_pk) # E: Revealed type is 'builtins.int*'
reveal_type(User().id) # E: Revealed type is 'Any'
[out]
[/CASE]
[CASE test_meta_nested_class_allows_subclassing_in_multiple_inheritance]
from typing import Any
@@ -84,7 +89,7 @@ class Mixin2(models.Model):
class User(Mixin1, Mixin2):
pass
[out]
[/CASE]
[CASE test_inheritance_from_abstract_model_does_not_fail_if_field_with_id_exists]
from django.db import models
@@ -93,7 +98,7 @@ class Abstract(models.Model):
abstract = True
class User(Abstract):
id = models.AutoField(primary_key=True)
[out]
[/CASE]
[CASE standard_it_from_parent_model_could_be_overridden_with_non_integer_field_in_child_model]
from django.db import models
@@ -103,11 +108,11 @@ class ParentModel(models.Model):
class MyModel(ParentModel):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
reveal_type(MyModel().id) # E: Revealed type is 'uuid.UUID*'
[out]
[/CASE]
[CASE blank_for_charfield_is_the_same_as_null]
from django.db import models
class MyModel(models.Model):
text = models.CharField(max_length=30, blank=True)
MyModel(text=None)
[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()
User.objects.create(name='Max', age=10)
User.objects.create(age='hello') # E: Incompatible type for "age" of "User" (got "str", expected "Union[int, Combinable, Literal['']]")
User.objects.create(age=[]) # E: Incompatible type for "age" of "User" (got "List[Any]", expected "Union[float, int, str, Combinable]")
[out]
[CASE model_recognises_parent_attributes]
@@ -59,6 +59,6 @@ MyModel.objects.create(id=None)
class MyModel2(models.Model):
id = models.IntegerField(primary_key=True, default=None)
MyModel2(id=None) # E: Incompatible type for "id" of "MyModel2" (got "None", expected "Union[int, Combinable, Literal['']]")
MyModel2.objects.create(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[float, int, str, Combinable]")
[out]

View File

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

View File

@@ -191,6 +191,8 @@ class Profile(models.Model):
from django.db import models
class App(models.Model):
owner = models.ForeignKey(to='myapp.User', on_delete=models.CASCADE, related_name='apps')
[disable_cache]
[/CASE]
[CASE many_to_many_field_converts_to_queryset_of_model_type]
from django.db import models
@@ -271,7 +273,17 @@ class Book2(models.Model):
reveal_type(Book2().publisher_id) # E: Revealed type is 'builtins.int'
Book2(publisher_id=1)
Book2(publisher_id='hello') # E: Incompatible type for "publisher_id" of "Book2" (got "str", expected "Union[int, Combinable, Literal[''], 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='hello') # E: Incompatible type for "publisher_id" of "Book2" (got "str", expected "Union[int, Combinable, Literal['']]")
[out]
Book2.objects.create(publisher_id=[]) # E: Incompatible type for "publisher_id" of "Book2" (got "List[Any]", expected "Union[float, int, str, Combinable]")
[out]
[CASE if_model_is_defined_as_name_of_the_class_look_for_it_in_the_same_file]
from django.db import models
class Book(models.Model):
publisher = models.ForeignKey(to='Publisher', on_delete=models.CASCADE)
class Publisher(models.Model):
pass
reveal_type(Book().publisher) # E: Revealed type is 'main.Publisher*'
[out]

View File

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

View File

@@ -11,9 +11,11 @@ reveal_type(get_object_or_404(MyModel.objects.get_queryset())) # E: Revealed ty
reveal_type(get_list_or_404(MyModel)) # E: Revealed type is 'builtins.list[main.MyModel*]'
reveal_type(get_list_or_404(MyModel.objects)) # E: Revealed type is 'builtins.list[main.MyModel*]'
reveal_type(get_list_or_404(MyModel.objects.get_queryset())) # E: Revealed type is 'builtins.list[main.MyModel*]'
[out]
[/CASE]
[CASE get_user_model_returns_proper_class]
[env DJANGO_SETTINGS_MODULE=mysettings]
[disable_cache]
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from myapp.models import MyUser
@@ -22,7 +24,6 @@ from django.contrib.auth import get_user_model
UserModel = get_user_model()
reveal_type(UserModel.objects) # E: Revealed type is 'django.db.models.manager.Manager[myapp.models.MyUser]'
[env DJANGO_SETTINGS_MODULE=mysettings]
[file mysettings.py]
INSTALLED_APPS = ('myapp',)
AUTH_USER_MODEL = 'myapp.MyUser'
@@ -32,15 +33,16 @@ AUTH_USER_MODEL = 'myapp.MyUser'
from django.db import models
class MyUser(models.Model):
pass
[out]
[/CASE]
[CASE return_type_model_and_show_error_if_model_not_yet_imported]
[env DJANGO_SETTINGS_MODULE=mysettings]
[disable_cache]
from django.contrib.auth import get_user_model
UserModel = get_user_model()
reveal_type(UserModel.objects)
[env DJANGO_SETTINGS_MODULE=mysettings]
[file mysettings.py]
INSTALLED_APPS = ('myapp',)
AUTH_USER_MODEL = 'myapp.MyUser'
@@ -53,4 +55,5 @@ class MyUser(models.Model):
[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: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]