3 Commits

Author SHA1 Message Date
Nikita Sobolev
b846c0f2ed Update README.md 2020-02-06 10:32:47 +03:00
Nikita Sobolev
7af2adb665 Update README.md 2020-02-05 00:03:40 +03:00
Nikita Sobolev
0052a83968 Update README.md 2020-02-04 13:52:06 +03:00
49 changed files with 171 additions and 728 deletions

View File

@@ -4,19 +4,10 @@ dist: xenial
sudo: required
jobs:
include:
- name: Run plugin test suite with python 3.8
python: 3.8
script: 'pytest'
- name: Run plugin test suite with python 3.7
python: 3.7
script: 'pytest'
- name: Typecheck Django 3.0 test suite with python 3.8
python: 3.8
script: |
python ./scripts/typecheck_tests.py --django_version=3.0
- name: Typecheck Django 3.0 test suite with python 3.7
python: 3.7
script: |

View File

@@ -1,111 +0,0 @@
# How to contribute
## Tutorials
If you want to start working on this project,
you will need to get familiar with these projects:
- [Django docs](https://docs.djangoproject.com/en/dev/)
- [Typing in Python](https://inventwithpython.com/blog/2019/11/24/type-hints-for-busy-python-programmers/)
- [How to write custom mypy plugins](https://mypy.readthedocs.io/en/stable/extending_mypy.html)
- [Typechecking Django and DRF](https://sobolevn.me/2019/08/typechecking-django-and-drf) guide
- [Testing mypy stubs, plugins, and types](https://sobolevn.me/2019/08/testing-mypy-types) guide
It is also recommended to take a look at these resources:
- [Awesome Python Typing](https://github.com/typeddjango/awesome-python-typing)
## Dev documentation
TODO
## Dependencies
We use `pip` to manage the dependencies.
To install them you would need to activate your `virtualenv` and run `install` command:
```bash
pip install -r ./dev-requirements.txt
```
## Tests and linters
We use `mypy`, `pytest`, `flake8`, and `black` for quality control.
Here's [how we run our CI](https://github.com/typeddjango/django-stubs/blob/master/.travis.yml).
### Typechecking
To run typechecking use:
```bash
mypy ./mypy_django_plugin
```
### Testing
There are unit tests and type-related tests.
To run unit tests:
```bash
pytest
```
Type-related tests ensure that different Django versions do work correctly.
To run type-related tests:
```bash
python ./scripts/typecheck_tests.py --django_version=2.2
python ./scripts/typecheck_tests.py --django_version=3.0
```
Currently we only support two Django versions.
### Linting
To run auto-formatting:
```bash
isort -rc .
black django-stubs/
```
To run linting:
```bash
flake8
flake8 --config flake8-pyi.ini
```
## Submitting your code
We use [trunk based](https://trunkbaseddevelopment.com/)
development (we also sometimes call it `wemake-git-flow`).
What the point of this method?
1. We use protected `master` branch,
so the only way to push your code is via pull request
2. We use issue branches: to implement a new feature or to fix a bug
create a new branch named `issue-$TASKNUMBER`
3. Then create a pull request to `master` branch
4. We use `git tag`s to make releases, so we can track what has changed
since the latest release
So, this way we achieve an easy and scalable development process
which frees us from merging hell and long-living branches.
In this method, the latest version of the app is always in the `master` branch.
## Other help
You can contribute by spreading a word about this library.
It would also be a huge contribution to write
a short article on how you are using this project.
You can also share your best practices with us.

View File

@@ -15,7 +15,7 @@ This package contains [type stubs](https://www.python.org/dev/peps/pep-0561/) an
pip install django-stubs
```
See [Configuration](#configuration) section to get started.
See [Configutation](#configuration) section to get started.
## Configuration
@@ -47,9 +47,7 @@ We rely on different `django` and `mypy` versions:
| django-stubs | mypy version | django version | python version
| ------------ | ---- | ---- | ---- |
| 1.5.0 | 0.780 | 2.2.x \|\| 3.x | ^3.6
| 1.4.0 | 0.770 | 2.2.x \|\| 3.x | ^3.6
| 1.3.0 | 0.750 | 2.2.x \|\| 3.x | ^3.6
| 1.3.0 | 0.750 | 2.2.x | ^3.6
| 1.2.0 | 0.730 | 2.2.x | ^3.6
| 1.1.0 | 0.720 | 2.2.x | ^3.6
| 0.12.x | old semantic analyzer (<0.711), dmypy support | 2.1.x | ^3.6
@@ -57,39 +55,39 @@ We rely on different `django` and `mypy` versions:
## FAQ
### Is this an official Django project?
> Is this an official Django project?
No, it is not. We are indendepent from Django at the moment.
There's a [proposal](https://github.com/django/deps/pull/65) to merge our project into the Django itself.
You show your support by linking the PR.
### Is it safe to use this in production?
> Is it safe to use this in production?
Yes, it is! This project does not affect your runtime at all.
It only affects `mypy` type checking process.
But, it does not make any sense to use this project without `mypy`.
But, it does not make sense to use this project without `mypy`.
### mypy crashes when I run it with this plugin installed
> mypy crashes when I run it with this plugin installed
Current implementation uses Django runtime to extract models information, so it will crash, if your installed apps or `models.py` is not correct. For this same reason, you cannot use `reveal_type` inside global scope of any Python file that will be executed for `django.setup()`.
Current implementation uses Django runtime to extract models information, so it will crash, if your installed apps `models.py` is not correct. For this same reason, you cannot use `reveal_type` inside global scope of any Python file that will be executed for `django.setup()`.
In other words, if your `manage.py runserver` crashes, mypy will crash too.
You can also run `mypy` with [`--tb`](https://mypy.readthedocs.io/en/stable/command_line.html#cmdoption-mypy-show-traceback)
option to get extra information about the error.
### I cannot use QuerySet or Manager with type annotations
> I cannot use QuerySet or Manager with type annotations
You can get a `TypeError: 'type' object is not subscriptable`
when you will try to use `QuerySet[MyModel]` or `Manager[MyModel]`.
This happens because Django classes do not support [`__class_getitem__`](https://www.python.org/dev/peps/pep-0560/#class-getitem) magic method.
You can use strings instead: `'QuerySet[MyModel]'` and `'Manager[MyModel]'`, this way it will work as a type for `mypy` and as a regular `str` in runtime.
There are several things you can use strings instead: `'QuerySet[MyModel]'` and `'Manager[MyModel'`, this way it will work as a type for `mypy` and as a regular `str` in runtime.
Currently we [are working](https://github.com/django/django/pull/12405) on providing `__class_getitem__` to the classes where we need them.
### How can I use HttpRequest with custom user model?
> How can I use HttpRequest with custom user model?
You can subclass standard request like so:

View File

@@ -1,8 +1,8 @@
black
pytest-mypy-plugins==1.3.0
pytest-mypy-plugins==1.2.0
psycopg2
flake8==3.7.9
flake8-pyi==19.3.0
isort==4.3.21
gitpython==3.1.0
gitpython==3.0.5
-e .

View File

@@ -1,7 +1,7 @@
from typing import Any, Callable, Dict, Iterator, List, Optional, Tuple, Union, Iterable
from django.contrib.auth.forms import AdminPasswordChangeForm
from django.forms.boundfield import BoundField
from django.forms.forms import BaseForm
from django.forms.utils import ErrorDict
from django.forms.widgets import Media, Widget
from django.utils.safestring import SafeText
@@ -23,7 +23,7 @@ class AdminForm:
readonly_fields: Any = ...
def __init__(
self,
form: BaseForm,
form: AdminPasswordChangeForm,
fieldsets: List[Tuple[None, Dict[str, List[str]]]],
prepopulated_fields: Dict[Any, Any],
readonly_fields: Optional[Iterable[Any]] = ...,

View File

@@ -1,9 +1,5 @@
from collections import OrderedDict
from typing import Any, Callable, Dict, Iterator, List, Optional, Sequence, Set, Tuple, Type, Union, Mapping, TypeVar
from django.forms.forms import BaseForm
from django.forms.formsets import BaseFormSet
from typing_extensions import Literal, TypedDict
from typing import Any, Callable, Dict, Iterator, List, Optional, Sequence, Set, Tuple, Type, Union
from django.contrib.admin.filters import ListFilter
from django.contrib.admin.models import LogEntry
@@ -30,10 +26,8 @@ from django.db.models.fields import Field
IS_POPUP_VAR: str
TO_FIELD_VAR: str
HORIZONTAL: Literal[1] = ...
VERTICAL: Literal[2] = ...
_Direction = Union[Literal[1], Literal[2]]
HORIZONTAL: Any
VERTICAL: Any
def get_content_type_for_model(obj: Union[Type[Model], Model]) -> ContentType: ...
def get_ul_class(radio_style: int) -> str: ...
@@ -43,35 +37,21 @@ class IncorrectLookupParameters(Exception): ...
FORMFIELD_FOR_DBFIELD_DEFAULTS: Any
csrf_protect_m: Any
class _OptionalFieldOpts(TypedDict, total=False):
classes: Sequence[str]
description: str
class _FieldOpts(_OptionalFieldOpts, total=True):
fields: Sequence[Union[str, Sequence[str]]]
# Workaround for mypy issue, a Sequence type should be preferred here.
# https://github.com/python/mypy/issues/8921
# _FieldsetSpec = Sequence[Tuple[Optional[str], _FieldOpts]]
_T = TypeVar("_T")
_ListOrTuple = Union[Tuple[_T, ...], List[_T]]
_FieldsetSpec = _ListOrTuple[Tuple[Optional[str], _FieldOpts]]
class BaseModelAdmin:
autocomplete_fields: Sequence[str] = ...
raw_id_fields: Sequence[str] = ...
fields: Sequence[Union[str, Sequence[str]]] = ...
exclude: Sequence[str] = ...
fieldsets: _FieldsetSpec = ...
form: Type[BaseForm] = ...
filter_vertical: Sequence[str] = ...
filter_horizontal: Sequence[str] = ...
radio_fields: Mapping[str, _Direction] = ...
prepopulated_fields: Mapping[str, Sequence[str]] = ...
formfield_overrides: Mapping[Type[Field], Mapping[str, Any]] = ...
readonly_fields: Sequence[Union[str, Callable[[Model], Any]]] = ...
ordering: Sequence[str] = ...
sortable_by: Sequence[str] = ...
autocomplete_fields: Any = ...
raw_id_fields: Any = ...
fields: Any = ...
exclude: Any = ...
fieldsets: Any = ...
form: Any = ...
filter_vertical: Any = ...
filter_horizontal: Any = ...
radio_fields: Any = ...
prepopulated_fields: Any = ...
formfield_overrides: Any = ...
readonly_fields: Any = ...
ordering: Any = ...
sortable_by: Any = ...
view_on_site: bool = ...
show_full_result_count: bool = ...
checks_class: Any = ...
@@ -113,7 +93,7 @@ class BaseModelAdmin:
def has_module_permission(self, request: HttpRequest) -> bool: ...
class ModelAdmin(BaseModelAdmin):
list_display: Sequence[Union[str, Callable[[Model], Any]]] = ...
list_display: Sequence[Union[str, Callable]] = ...
list_display_links: Optional[Sequence[Union[str, Callable]]] = ...
list_filter: Sequence[Union[str, Type[ListFilter], Tuple[str, Type[ListFilter]]]] = ...
list_select_related: Union[bool, Sequence[str]] = ...
@@ -121,21 +101,21 @@ class ModelAdmin(BaseModelAdmin):
list_max_show_all: int = ...
list_editable: Sequence[str] = ...
search_fields: Sequence[str] = ...
date_hierarchy: Optional[str] = ...
date_hierarchy: Optional[Any] = ...
save_as: bool = ...
save_as_continue: bool = ...
save_on_top: bool = ...
paginator: Type = ...
paginator: Any = ...
preserve_filters: bool = ...
inlines: Sequence[Type[InlineModelAdmin]] = ...
add_form_template: str = ...
change_form_template: str = ...
change_list_template: str = ...
delete_confirmation_template: str = ...
delete_selected_confirmation_template: str = ...
object_history_template: str = ...
popup_response_template: str = ...
actions: Sequence[Callable[[ModelAdmin, HttpRequest, QuerySet], None]] = ...
add_form_template: Any = ...
change_form_template: Any = ...
change_list_template: Any = ...
delete_confirmation_template: Any = ...
delete_selected_confirmation_template: Any = ...
object_history_template: Any = ...
popup_response_template: Any = ...
actions: Any = ...
action_form: Any = ...
actions_on_top: bool = ...
actions_on_bottom: bool = ...
@@ -247,9 +227,9 @@ class ModelAdmin(BaseModelAdmin):
def history_view(self, request: HttpRequest, object_id: str, extra_context: None = ...) -> HttpResponse: ...
class InlineModelAdmin(BaseModelAdmin):
model: Type[Model] = ...
fk_name: str = ...
formset: BaseFormSet = ...
model: Any = ...
fk_name: Any = ...
formset: Any = ...
extra: int = ...
min_num: Optional[int] = ...
max_num: Optional[int] = ...
@@ -258,8 +238,8 @@ class InlineModelAdmin(BaseModelAdmin):
verbose_name_plural: Optional[str] = ...
can_delete: bool = ...
show_change_link: bool = ...
classes: Optional[Sequence[str]] = ...
admin_site: AdminSite = ...
classes: Any = ...
admin_site: Any = ...
parent_model: Any = ...
opts: Any = ...
has_registered_model: Any = ...

View File

@@ -63,7 +63,6 @@ class AdminSite:
def i18n_javascript(self, request: WSGIRequest, extra_context: Optional[Dict[Any, Any]] = ...) -> HttpResponse: ...
def logout(self, request: WSGIRequest, extra_context: Optional[Dict[str, Any]] = ...) -> TemplateResponse: ...
def login(self, request: WSGIRequest, extra_context: Optional[Dict[str, Any]] = ...) -> HttpResponse: ...
def _build_app_dict(self, request: WSGIRequest, label: Optional[str] = ...) -> Dict[str, Any]: ...
def get_app_list(self, request: WSGIRequest) -> List[Any]: ...
def index(self, request: WSGIRequest, extra_context: Optional[Dict[str, Any]] = ...) -> TemplateResponse: ...
def app_index(

View File

@@ -1,15 +1,9 @@
import sys
from typing import Any, Optional, Tuple, List, overload, TypeVar
from django.db.models.base import Model
from django.db import models
if sys.version_info < (3, 8):
from typing_extensions import Literal
else:
from typing import Literal
_T = TypeVar("_T", bound=Model)
class BaseUserManager(models.Manager[_T]):
@@ -26,9 +20,9 @@ class AbstractBaseUser(models.Model):
def get_username(self) -> str: ...
def natural_key(self) -> Tuple[str]: ...
@property
def is_anonymous(self) -> Literal[False]: ...
def is_anonymous(self) -> bool: ...
@property
def is_authenticated(self) -> Literal[True]: ...
def is_authenticated(self) -> bool: ...
def set_password(self, raw_password: Optional[str]) -> None: ...
def check_password(self, raw_password: str) -> bool: ...
def set_unusable_password(self) -> None: ...

View File

@@ -1,21 +1,13 @@
from typing import Callable, List, Optional, Set, Union, TypeVar, overload
from typing import Callable, List, Optional, Set, Union
from django.contrib.auth import REDIRECT_FIELD_NAME as REDIRECT_FIELD_NAME # noqa: F401
from django.http.response import HttpResponseBase
from django.contrib.auth.models import AbstractUser
_VIEW = TypeVar("_VIEW", bound=Callable[..., HttpResponseBase])
def user_passes_test(
test_func: Callable[[AbstractUser], bool], login_url: Optional[str] = ..., redirect_field_name: str = ...
) -> Callable[[_VIEW], _VIEW]: ...
# There are two ways of calling @login_required: @with(arguments) and @bare
@overload
def login_required(redirect_field_name: str = ..., login_url: Optional[str] = ...) -> Callable[[_VIEW], _VIEW]: ...
@overload
def login_required(function: _VIEW, redirect_field_name: str = ..., login_url: Optional[str] = ...) -> _VIEW: ...
test_func: Callable, login_url: Optional[str] = ..., redirect_field_name: str = ...
) -> Callable: ...
def login_required(
function: Optional[Callable] = ..., redirect_field_name: str = ..., login_url: Optional[str] = ...
) -> Callable: ...
def permission_required(
perm: Union[List[str], Set[str], str], login_url: None = ..., raise_exception: bool = ...
) -> Callable[[_VIEW], _VIEW]: ...
) -> Callable: ...

View File

@@ -1,4 +1,3 @@
import sys
from typing import Any, Collection, Optional, Set, Tuple, Type, TypeVar, Union
from django.contrib.auth.backends import ModelBackend
@@ -10,11 +9,6 @@ from django.db.models.manager import EmptyManager
from django.db import models
if sys.version_info < (3, 8):
from typing_extensions import Literal
else:
from typing import Literal
_AnyUser = Union[Model, "AnonymousUser"]
def update_last_login(sender: Type[AbstractBaseUser], user: AbstractBaseUser, **kwargs: Any) -> None: ...
@@ -111,7 +105,7 @@ class AnonymousUser:
def has_perms(self, perm_list: Collection[str], obj: Optional[_AnyUser] = ...) -> bool: ...
def has_module_perms(self, module: str) -> bool: ...
@property
def is_anonymous(self) -> Literal[True]: ...
def is_anonymous(self) -> bool: ...
@property
def is_authenticated(self) -> Literal[False]: ...
def is_authenticated(self) -> bool: ...
def get_username(self) -> str: ...

View File

@@ -1,14 +1,15 @@
from datetime import date, datetime as datetime
from typing import Any, Optional, SupportsInt, Union
from decimal import Decimal
from typing import Any, Optional, Union
register: Any
def ordinal(value: Optional[Union[str, SupportsInt]]) -> Optional[str]: ...
def intcomma(value: Optional[Union[str, SupportsInt]], use_l10n: bool = ...) -> str: ...
def ordinal(value: Optional[str]) -> Optional[str]: ...
def intcomma(value: Optional[Union[Decimal, float, str]], use_l10n: bool = ...) -> str: ...
intword_converters: Any
def intword(value: Optional[Union[str, SupportsInt]]) -> Optional[Union[int, str]]: ...
def apnumber(value: Optional[Union[str, SupportsInt]]) -> Optional[Union[int, str]]: ...
def intword(value: Optional[str]) -> Optional[Union[int, str]]: ...
def apnumber(value: Optional[str]) -> Optional[Union[int, str]]: ...
def naturalday(value: Optional[Union[date, str]], arg: None = ...) -> Optional[str]: ...
def naturaltime(value: datetime) -> str: ...

View File

@@ -1,5 +1,5 @@
from datetime import datetime
from typing import Any, Dict, List, Optional, Union, Protocol
from typing import Any, Dict, List, Optional, Union
from django.contrib.sites.models import Site
from django.contrib.sites.requests import RequestSite
@@ -13,19 +13,10 @@ class SitemapNotFound(Exception): ...
def ping_google(sitemap_url: Optional[str] = ..., ping_url: str = ...) -> None: ...
class _SupportsLen(Protocol):
def __len__(self) -> int: ...
class _SupportsCount(Protocol):
def count(self) -> int: ...
class _SupportsOrdered(Protocol):
ordered: bool = ...
class Sitemap:
limit: int = ...
protocol: Optional[str] = ...
def items(self) -> Union[_SupportsLen, _SupportsCount, _SupportsOrdered]: ...
def items(self) -> List[Any]: ...
def location(self, obj: Model) -> str: ...
@property
def paginator(self) -> Paginator: ...

View File

@@ -19,16 +19,16 @@ class BaseCache:
def __init__(self, params: Dict[str, Any]) -> None: ...
def get_backend_timeout(self, timeout: Any = ...) -> Optional[float]: ...
def make_key(self, key: Any, version: Optional[Any] = ...) -> str: ...
def add(self, key: Any, value: Any, timeout: Any = ..., version: Optional[Any] = ...) -> bool: ...
def add(self, key: Any, value: Any, timeout: Any = ..., version: Optional[Any] = ...) -> None: ...
def get(self, key: Any, default: Optional[Any] = ..., version: Optional[Any] = ...) -> Any: ...
def set(self, key: Any, value: Any, timeout: Any = ..., version: Optional[Any] = ...) -> None: ...
def touch(self, key: Any, timeout: Any = ..., version: Optional[Any] = ...) -> bool: ...
def touch(self, key: Any, timeout: Any = ..., version: Optional[Any] = ...) -> None: ...
def delete(self, key: Any, version: Optional[Any] = ...) -> None: ...
def get_many(self, keys: List[str], version: Optional[int] = ...) -> Dict[str, Union[int, str]]: ...
def get_or_set(
self, key: Any, default: Optional[Any], timeout: Any = ..., version: Optional[int] = ...
) -> Optional[Any]: ...
def has_key(self, key: Any, version: Optional[Any] = ...) -> bool: ...
def has_key(self, key: Any, version: Optional[Any] = ...): ...
def incr(self, key: str, delta: int = ..., version: Optional[int] = ...) -> int: ...
def decr(self, key: str, delta: int = ..., version: Optional[int] = ...) -> int: ...
def __contains__(self, key: str) -> bool: ...

View File

@@ -15,14 +15,14 @@ class SkipFile(UploadFileException): ...
class StopFutureHandlers(UploadFileException): ...
class FileUploadHandler:
chunk_size: int = ...
file_name: Optional[str] = ...
content_type: Optional[str] = ...
content_length: Optional[int] = ...
charset: Optional[str] = ...
content_type_extra: Optional[Dict[str, str]] = ...
request: Optional[HttpRequest] = ...
field_name: str = ...
chunk_size = ... # type: int
file_name = ... # type: Optional[str]
content_type = ... # type: Optional[str]
content_length = ... # type: Optional[int]
charset = ... # type: Optional[str]
content_type_extra = ... # type: Optional[Dict[str, str]]
request = ... # type: Optional[HttpRequest]
field_name = ... # type: str
def __init__(self, request: Optional[HttpRequest] = ...) -> None: ...
def handle_raw_input(
self,

View File

@@ -1,10 +1,12 @@
from io import BytesIO
from typing import Any, Callable, Dict, Optional, Union
from django.contrib.auth.models import AbstractUser
from django.contrib.sessions.backends.base import SessionBase
from django.http.response import HttpResponse
from django.core.handlers import base
from django.http import HttpRequest
from django.http.response import HttpResponse
_Stream = Union[BytesIO, str]
_WSGIEnviron = Dict[str, Any]
@@ -20,6 +22,7 @@ class LimitedStream:
class WSGIRequest(HttpRequest):
environ: _WSGIEnviron = ...
user: AbstractUser
session: SessionBase
encoding: Any = ...
def __init__(self, environ: _WSGIEnviron) -> None: ...

View File

@@ -8,13 +8,13 @@ class InvalidPage(Exception): ...
class PageNotAnInteger(InvalidPage): ...
class EmptyPage(InvalidPage): ...
class _SupportsLen(Protocol):
class SupportsLen(Protocol):
def __len__(self) -> int: ...
class _SupportsCount(Protocol):
class SupportsCount(Protocol):
def count(self) -> int: ...
class _SupportsOrdered(Protocol):
class SupportsOrdered(Protocol):
ordered: bool = ...
class Paginator:
@@ -24,7 +24,7 @@ class Paginator:
allow_empty_first_page: bool = ...
def __init__(
self,
object_list: Union[_SupportsLen, _SupportsCount, _SupportsOrdered],
object_list: Union[SupportsLen, SupportsCount, SupportsOrdered],
per_page: Union[int, str],
orphans: int = ...,
allow_empty_first_page: bool = ...,

View File

@@ -21,11 +21,7 @@ def dumps(
obj: Any, key: None = ..., salt: str = ..., serializer: Type[Serializer] = ..., compress: bool = ...
) -> str: ...
def loads(
s: str,
key: None = ...,
salt: str = ...,
serializer: Type[Serializer] = ...,
max_age: Optional[Union[int, timedelta]] = ...,
s: str, key: None = ..., salt: str = ..., serializer: Type[Serializer] = ..., max_age: Optional[int] = ...
) -> Any: ...
class Signer:

View File

@@ -68,9 +68,6 @@ from .deletion import (
DO_NOTHING as DO_NOTHING,
PROTECT as PROTECT,
SET as SET,
RESTRICT as RESTRICT,
ProtectedError as ProtectedError,
RestrictedError as RestrictedError,
)
from .query import (

View File

@@ -1,4 +1,4 @@
from typing import Any, Callable, Dict, List, Optional, Sequence, Set, Tuple, Type, TypeVar, Union, Collection, ClassVar
from typing import Any, Callable, Dict, List, Optional, Sequence, Set, Tuple, Type, TypeVar, Union, Collection
from django.core.checks.messages import CheckMessage
from django.core.exceptions import ValidationError
@@ -7,13 +7,6 @@ from django.db.models.options import Options
_Self = TypeVar("_Self", bound="Model")
class ModelStateFieldsCacheDescriptor: ...
class ModelState:
db: Optional[str] = ...
adding: bool = ...
fields_cache: ModelStateFieldsCacheDescriptor = ...
class ModelBase(type): ...
class Model(metaclass=ModelBase):
@@ -22,14 +15,9 @@ class Model(metaclass=ModelBase):
class Meta: ...
_meta: Options[Any]
_default_manager: BaseManager[Model]
objects: ClassVar[BaseManager[Any]]
objects: BaseManager[Any]
pk: Any = ...
_state: ModelState
def __init__(self: _Self, *args, **kwargs) -> None: ...
@classmethod
def add_to_class(cls, name: str, value: Any): ...
@classmethod
def from_db(cls, db: Optional[str], field_names: Collection[str], values: Collection[Any]) -> _Self: ...
def delete(self, using: Any = ..., keep_parents: bool = ...) -> Tuple[int, Dict[str, int]]: ...
def full_clean(self, exclude: Optional[Collection[str]] = ..., validate_unique: bool = ...) -> None: ...
def clean(self) -> None: ...
@@ -58,3 +46,10 @@ class Model(metaclass=ModelBase):
@classmethod
def check(cls, **kwargs: Any) -> List[CheckMessage]: ...
def __getstate__(self) -> dict: ...
class ModelStateFieldsCacheDescriptor: ...
class ModelState:
db: None = ...
adding: bool = ...
fields_cache: ModelStateFieldsCacheDescriptor = ...

View File

@@ -11,12 +11,10 @@ def SET_NULL(collector, field, sub_objs, using): ...
def SET_DEFAULT(collector, field, sub_objs, using): ...
def DO_NOTHING(collector, field, sub_objs, using): ...
def PROTECT(collector, field, sub_objs, using): ...
def RESTRICT(collector, field, sub_objs, using): ...
def SET(value: Any) -> Callable: ...
def get_candidate_relations_to_delete(opts: Options) -> Iterable[Field]: ...
class ProtectedError(IntegrityError): ...
class RestrictedError(IntegrityError): ...
class Collector:
def __init__(self, using: str) -> None: ...

View File

@@ -108,10 +108,7 @@ class Field(RegisterLookupMixin, Generic[_ST, _GT]):
def set_attributes_from_name(self, name: str) -> None: ...
def db_type(self, connection: Any) -> str: ...
def db_parameters(self, connection: Any) -> Dict[str, str]: ...
def pre_save(self, model_instance: Model, add: bool) -> Any: ...
def get_prep_value(self, value: Any) -> Any: ...
def get_db_prep_value(self, value: Any, connection: Any, prepared: bool) -> Any: ...
def get_db_prep_save(self, value: Any, connection: Any) -> Any: ...
def get_internal_type(self) -> str: ...
# TODO: plugin support
def formfield(self, **kwargs) -> Any: ...

View File

@@ -39,11 +39,4 @@ def atomic(using: _C) -> _C: ...
# Decorator or context-manager with parameters
@overload
def atomic(using: Optional[str] = ..., savepoint: bool = ...) -> Atomic: ...
# Bare decorator
@overload
def non_atomic_requests(using: _C) -> _C: ...
# Decorator with arguments
@overload
def non_atomic_requests(using: Optional[str] = ...) -> Callable[[_C], _C]: ...
def non_atomic_requests(using: Callable = ...) -> Callable: ...

View File

@@ -14,7 +14,7 @@ class Field:
initial: Any
label: Optional[str]
required: bool
widget: Type[Widget] = ...
widget: Widget = ...
hidden_widget: Any = ...
default_validators: Any = ...
default_error_messages: Any = ...

View File

@@ -1,13 +1,11 @@
from typing import Any, Dict, Iterator, List, Mapping, Optional, Sequence, Type, Union
from django.core.exceptions import ValidationError as ValidationError
from django.core.files import uploadedfile
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.datastructures import MultiValueDict
from django.utils.safestring import SafeText
class DeclarativeFieldsMetaclass(MediaDefiningClass): ...
@@ -20,11 +18,11 @@ class BaseForm:
use_required_attribute: bool = ...
is_bound: bool = ...
data: Dict[str, Any] = ...
files: MultiValueDict[str, uploadedfile.UploadedFile] = ...
auto_id: Union[bool, str] = ...
files: Optional[Dict[str, Any]] = ...
auto_id: str = ...
initial: Dict[str, Any] = ...
error_class: Type[ErrorList] = ...
prefix: Optional[str] = ...
prefix: str = ...
label_suffix: str = ...
empty_permitted: bool = ...
fields: Dict[str, Any] = ...
@@ -69,9 +67,6 @@ class BaseForm:
def hidden_fields(self): ...
def visible_fields(self): ...
def get_initial_for_field(self, field: Field, field_name: str) -> Any: ...
def _html_output(
self, normal_row: str, error_row: str, row_ender: str, help_text_html: str, errors_on_separate_row: bool,
) -> SafeText: ...
class Form(BaseForm):
base_fields: Dict[str, Field]

View File

@@ -17,7 +17,6 @@ from typing import (
)
from django.contrib.auth.base_user import AbstractBaseUser
from django.contrib.auth.models import AnonymousUser
from django.contrib.sessions.backends.base import SessionBase
from django.contrib.sites.models import Site
from django.utils.datastructures import CaseInsensitiveMapping, ImmutableList, MultiValueDict
@@ -52,7 +51,7 @@ class HttpRequest(BytesIO):
resolver_match: ResolverMatch = ...
content_type: Optional[str] = ...
content_params: Optional[Dict[str, str]] = ...
user: Union[AbstractBaseUser, AnonymousUser]
user: AbstractBaseUser
site: Site
session: SessionBase
encoding: Optional[str] = ...

View File

@@ -62,27 +62,25 @@ class HttpResponseBase(Iterable[Any]):
def __iter__(self) -> Iterator[Any]: ...
class HttpResponse(HttpResponseBase):
client: Client
context: Context
content: Any
csrf_cookie_set: bool
redirect_chain: List[Tuple[str, int]]
request: Dict[str, Any]
resolver_match: ResolverMatch
sameorigin: bool
templates: List[Template]
test_server_port: str
test_was_secure_request: bool
wsgi_request: WSGIRequest
xframe_options_exempt: bool
streaming: bool = ...
def __init__(self, content: object = ..., *args: Any, **kwargs: Any) -> None: ...
def serialize(self) -> bytes: ...
@property
def url(self) -> str: ...
# Attributes assigned by monkey-patching in test client ClientHandler.__call__()
wsgi_request: WSGIRequest
# Attributes assigned by monkey-patching in test client Client.request()
client: Client
request: Dict[str, Any]
templates: List[Template]
context: Context
resolver_match: ResolverMatch
def json(self) -> Any: ...
def json(self) -> Dict[str, Any]: ...
class StreamingHttpResponse(HttpResponseBase):
content: Any

View File

@@ -1,5 +1,4 @@
import sys
from typing import Any, Callable, List, Mapping, Optional, overload, Protocol, Sequence, Type, TypeVar, Union
from typing import Any, Callable, Dict, List, Optional, Protocol, Sequence, Type, TypeVar, Union
from django.db.models.base import Model
from django.http.response import (
@@ -11,14 +10,9 @@ from django.http.response import (
from django.db.models import Manager, QuerySet
from django.http import HttpRequest
if sys.version_info < (3, 8):
from typing_extensions import Literal
else:
from typing import Literal
def render_to_response(
template_name: Union[str, Sequence[str]],
context: Optional[Mapping[str, Any]] = ...,
context: Optional[Dict[str, Any]] = ...,
content_type: Optional[str] = ...,
status: Optional[int] = ...,
using: Optional[str] = ...,
@@ -26,7 +20,7 @@ def render_to_response(
def render(
request: HttpRequest,
template_name: Union[str, Sequence[str]],
context: Optional[Mapping[str, Any]] = ...,
context: Optional[Dict[str, Any]] = ...,
content_type: Optional[str] = ...,
status: Optional[int] = ...,
using: Optional[str] = ...,
@@ -34,15 +28,6 @@ def render(
class SupportsGetAbsoluteUrl(Protocol): ...
@overload
def redirect(
to: Union[Callable, str, SupportsGetAbsoluteUrl], *args: Any, permanent: Literal[True], **kwargs: Any
) -> HttpResponsePermanentRedirect: ...
@overload
def redirect(
to: Union[Callable, str, SupportsGetAbsoluteUrl], *args: Any, permanent: Literal[False], **kwargs: Any
) -> HttpResponseRedirect: ...
@overload
def redirect(
to: Union[Callable, str, SupportsGetAbsoluteUrl], *args: Any, permanent: bool = ..., **kwargs: Any
) -> Union[HttpResponseRedirect, HttpResponsePermanentRedirect]: ...

View File

@@ -1,16 +1,15 @@
from io import BytesIO
from types import TracebackType
from typing import Any, Dict, List, Optional, Pattern, Tuple, Type, Union
from django.contrib.auth.models import AbstractUser
from django.contrib.sessions.backends.base import SessionBase
from django.core.handlers.base import BaseHandler
from django.core.serializers.json import DjangoJSONEncoder
from django.http.cookie import SimpleCookie
from django.http.request import HttpRequest
from django.http.response import HttpResponse, HttpResponseBase
from django.core.handlers.wsgi import WSGIRequest
from json import JSONEncoder
BOUNDARY: str = ...
MULTIPART_CONTENT: str = ...
@@ -38,11 +37,11 @@ def encode_multipart(boundary: str, data: Dict[str, Any]) -> bytes: ...
def encode_file(boundary: str, key: str, file: Any) -> List[bytes]: ...
class RequestFactory:
json_encoder: Type[JSONEncoder]
defaults: Dict[str, str]
cookies: SimpleCookie
errors: BytesIO
def __init__(self, *, json_encoder: Type[JSONEncoder] = ..., **defaults: Any) -> None: ...
json_encoder: Type[DjangoJSONEncoder] = ...
defaults: Dict[str, str] = ...
cookies: SimpleCookie = ...
errors: BytesIO = ...
def __init__(self, *, json_encoder: Any = ..., **defaults: Any) -> None: ...
def request(self, **request: Any) -> WSGIRequest: ...
def get(self, path: str, data: Any = ..., secure: bool = ..., **extra: Any) -> WSGIRequest: ...
def post(
@@ -55,7 +54,6 @@ class RequestFactory:
path: str,
data: Union[Dict[str, str], str] = ...,
content_type: str = ...,
follow: bool = ...,
secure: bool = ...,
**extra: Any
) -> WSGIRequest: ...
@@ -78,50 +76,39 @@ class RequestFactory:
**extra: Any
) -> WSGIRequest: ...
class Client(RequestFactory):
handler: ClientHandler
raise_request_exception: bool
exc_info: Optional[Tuple[Type[BaseException], BaseException, TracebackType]]
def __init__(
self,
enforce_csrf_checks: bool = ...,
raise_request_exception: bool = ...,
*,
json_encoder: Type[JSONEncoder] = ...,
**defaults: Any
) -> None: ...
# Silence type warnings, since this class overrides arguments and return types in an unsafe manner.
def request(self, **request: Any) -> HttpResponse: ... # type: ignore
def get( # type: ignore
self, path: str, data: Any = ..., follow: bool = ..., secure: bool = ..., **extra: Any
) -> HttpResponse: ... # type: ignore
def post( # type: ignore
self, path: str, data: Any = ..., content_type: str = ..., follow: bool = ..., secure: bool = ..., **extra: Any
) -> HttpResponse: ... # type: ignore
def head( # type: ignore
self, path: str, data: Any = ..., follow: bool = ..., secure: bool = ..., **extra: Any
) -> HttpResponse: ... # type: ignore
def trace( # type: ignore
self, path: str, follow: bool = ..., secure: bool = ..., **extra: Any
) -> HttpResponse: ... # type: ignore
def options( # type: ignore
class Client:
json_encoder: Type[DjangoJSONEncoder] = ...
defaults: Dict[str, str] = ...
cookies: SimpleCookie = ...
errors: BytesIO = ...
handler: ClientHandler = ...
exc_info: None = ...
def __init__(self, enforce_csrf_checks: bool = ..., **defaults: Any) -> None: ...
def request(self, **request: Any) -> Any: ...
def get(self, path: str, data: Any = ..., secure: bool = ..., **extra: Any) -> Any: ...
def post(self, path: str, data: Any = ..., content_type: str = ..., secure: bool = ..., **extra: Any) -> Any: ...
def head(self, path: str, data: Any = ..., secure: bool = ..., **extra: Any) -> Any: ...
def trace(self, path: str, secure: bool = ..., **extra: Any) -> Any: ...
def options(
self,
path: str,
data: Union[Dict[str, str], str] = ...,
content_type: str = ...,
follow: bool = ...,
secure: bool = ...,
**extra: Any
) -> HttpResponse: ... # type: ignore
def put( # type: ignore
self, path: str, data: Any = ..., content_type: str = ..., follow: bool = ..., secure: bool = ..., **extra: Any
) -> HttpResponse: ... # type: ignore
def patch( # type: ignore
self, path: str, data: Any = ..., content_type: str = ..., follow: bool = ..., secure: bool = ..., **extra: Any
) -> HttpResponse: ... # type: ignore
def delete( # type: ignore
self, path: str, data: Any = ..., content_type: str = ..., follow: bool = ..., secure: bool = ..., **extra: Any
) -> HttpResponse: ... # type: ignore
) -> Any: ...
def put(self, path: str, data: Any = ..., content_type: str = ..., secure: bool = ..., **extra: Any) -> Any: ...
def patch(self, path: str, data: Any = ..., content_type: str = ..., secure: bool = ..., **extra: Any) -> Any: ...
def delete(self, path: str, data: Any = ..., content_type: str = ..., secure: bool = ..., **extra: Any) -> Any: ...
def generic(
self,
method: str,
path: str,
data: Any = ...,
content_type: Optional[str] = ...,
secure: bool = ...,
**extra: Any
) -> Any: ...
def store_exc_info(self, **kwargs: Any) -> None: ...
@property
def session(self) -> SessionBase: ...

View File

@@ -126,17 +126,9 @@ class SimpleTestCase(unittest.TestCase):
self, needle: str, haystack: SafeText, count: Optional[int] = ..., msg_prefix: str = ...
) -> None: ...
def assertJSONEqual(
self,
raw: str,
expected_data: Union[Dict[str, Any], List[Any], str, int, float, bool, None],
msg: Optional[str] = ...,
) -> None: ...
def assertJSONNotEqual(
self,
raw: str,
expected_data: Union[Dict[str, Any], List[Any], str, int, float, bool, None],
msg: Optional[str] = ...,
self, raw: str, expected_data: Union[Dict[str, str], bool, str], msg: Optional[str] = ...
) -> None: ...
def assertJSONNotEqual(self, raw: str, expected_data: str, msg: Optional[str] = ...) -> None: ...
def assertXMLEqual(self, xml1: str, xml2: str, msg: Optional[str] = ...) -> None: ...
def assertXMLNotEqual(self, xml1: str, xml2: str, msg: Optional[str] = ...) -> None: ...

View File

@@ -16,7 +16,6 @@ from typing import (
Type,
Union,
ContextManager,
TypeVar,
)
from django.apps.registry import Apps
@@ -30,7 +29,6 @@ from django.conf import LazySettings, Settings
_TestClass = Type[SimpleTestCase]
_DecoratedTest = Union[Callable, _TestClass]
_C = TypeVar("_C", bound=Callable) # Any callable
TZ_SUPPORT: bool = ...
@@ -58,7 +56,7 @@ class TestContextDecorator:
def __enter__(self) -> Optional[Apps]: ...
def __exit__(self, exc_type: None, exc_value: None, traceback: None) -> None: ...
def decorate_class(self, cls: _TestClass) -> _TestClass: ...
def decorate_callable(self, func: _C) -> _C: ...
def decorate_callable(self, func: Callable) -> Callable: ...
def __call__(self, decorated: _DecoratedTest) -> Any: ...
class override_settings(TestContextDecorator):
@@ -148,7 +146,7 @@ def get_unique_databases_and_mirrors() -> Tuple[Dict[_Signature, _TestDatabase],
def teardown_databases(
old_config: Iterable[Tuple[Any, str, bool]], verbosity: int, parallel: int = ..., keepdb: bool = ...
) -> None: ...
def require_jinja2(test_func: _C) -> _C: ...
def require_jinja2(test_func: Callable) -> Callable: ...
@contextmanager
def register_lookup(
field: Type[RegisterLookupMixin], *lookups: Type[Union[Lookup, Transform]], lookup_name: Optional[str] = ...

View File

@@ -22,7 +22,6 @@ _V = TypeVar("_V")
class OrderedSet(MutableSet[_K]):
dict: Dict[_K, None] = ...
def __init__(self, iterable: Optional[Iterable[_K]] = ...) -> None: ...
def __contains__(self, item: object) -> bool: ...
def __iter__(self) -> Iterator[_K]: ...
def __len__(self) -> int: ...

View File

@@ -1,16 +1,13 @@
from typing import Any, Callable, Iterable, Optional, Type, Union, TypeVar
from typing import Any, Callable, Iterable, Optional, Type, Union
from django.utils.deprecation import MiddlewareMixin
from django.views.generic.base import View
_T = TypeVar("_T", bound=Union[View, Callable]) # Any callable
class classonlymethod(classmethod): ...
def method_decorator(decorator: Union[Callable, Iterable[Callable]], name: str = ...) -> Callable[[_T], _T]: ...
def method_decorator(decorator: Union[Callable, Iterable[Callable]], name: str = ...) -> Callable: ...
def decorator_from_middleware_with_args(middleware_class: type) -> Callable: ...
def decorator_from_middleware(middleware_class: type) -> Callable: ...
def available_attrs(fn: Callable): ...
def available_attrs(fn: Any): ...
def make_middleware_decorator(middleware_class: Type[MiddlewareMixin]) -> Callable: ...
class classproperty:

View File

@@ -13,6 +13,6 @@ NOCOLOR_PALETTE: str
DARK_PALETTE: str
LIGHT_PALETTE: str
PALETTES: Any
DEFAULT_PALETTE: str = ...
DEFAULT_PALETTE = DARK_PALETTE
def parse_color_setting(config_string: str) -> Optional[Dict[str, Dict[str, Union[Tuple[str], str]]]]: ...

View File

@@ -66,7 +66,6 @@ def to_locale(language: str) -> str: ...
def get_language_from_request(request: WSGIRequest, check_path: bool = ...) -> str: ...
def templatize(src: str, **kwargs: Any) -> str: ...
def deactivate_all() -> None: ...
def get_supported_language_variant(lang_code: str, strict: bool = ...) -> str: ...
def get_language_info(lang_code: str) -> Any: ...
from . import trans_real as trans_real

View File

@@ -10,5 +10,3 @@ select = F401, Y
max_line_length = 120
per-file-ignores =
*__init__.pyi: F401
base_user.pyi: Y003
models.pyi: Y003

View File

@@ -1,7 +1,6 @@
from mypy.plugin import AttributeContext
from mypy.types import Instance
from mypy.types import Type as MypyType
from mypy.types import UnionType
from mypy_django_plugin.django.context import DjangoContext
from mypy_django_plugin.lib import helpers
@@ -9,18 +8,9 @@ from mypy_django_plugin.lib import helpers
def set_auth_user_model_as_type_for_request_user(ctx: AttributeContext, django_context: DjangoContext) -> MypyType:
auth_user_model = django_context.settings.AUTH_USER_MODEL
user_cls = django_context.apps_registry.get_model(auth_user_model)
user_info = helpers.lookup_class_typeinfo(helpers.get_typechecker_api(ctx), user_cls)
if user_info is None:
model_cls = django_context.apps_registry.get_model(auth_user_model)
model_info = helpers.lookup_class_typeinfo(helpers.get_typechecker_api(ctx), model_cls)
if model_info is None:
return ctx.default_attr_type
# Imported here because django isn't properly loaded yet when module is loaded
from django.contrib.auth.models import AnonymousUser
anonymous_user_info = helpers.lookup_class_typeinfo(helpers.get_typechecker_api(ctx), AnonymousUser)
if anonymous_user_info is None:
# This shouldn't be able to happen, as we managed to import the model above...
return Instance(user_info, [])
return UnionType([Instance(user_info, []), Instance(anonymous_user_info, [])])
return Instance(model_info, [])

View File

@@ -64,12 +64,7 @@ IGNORED_ERRORS = {
'Incompatible types in string interpolation',
'"None" has no attribute',
'has no attribute "assert',
'Unsupported dynamic base class',
'error: "HttpResponse" has no attribute "streaming_content"',
'error: "HttpResponse" has no attribute "context_data"',
],
'admin_inlines': [
'error: "HttpResponse" has no attribute "rendered_content"',
'Unsupported dynamic base class'
],
'admin_utils': [
'"Article" has no attribute "non_field"',
@@ -109,7 +104,6 @@ IGNORED_ERRORS = {
],
'builtin_server': [
'"ServerHandler" has no attribute',
'Incompatible types in assignment (expression has type "Tuple[BytesIO, BytesIO]"',
],
'bulk_create': [
'has incompatible type "List[Country]"; expected "Iterable[TwoFields]"',
@@ -170,8 +164,7 @@ IGNORED_ERRORS = {
'Incompatible types in assignment (expression has type "Optional[Any]", variable has type "FloatModel")'
],
'decorators': [
'"Type[object]" has no attribute "method"',
'Value of type variable "_T" of function cannot be "descriptor_wrapper"'
'"Type[object]" has no attribute "method"'
],
'expressions_window': [
'has incompatible type "str"'
@@ -185,8 +178,6 @@ IGNORED_ERRORS = {
],
'files': [
'Incompatible types in assignment (expression has type "IOBase", variable has type "File")',
'Argument 1 to "TextIOWrapper" has incompatible type "File"; expected "BinaryIO"',
'Incompatible types in assignment (expression has type "BinaryIO", variable has type "File")',
],
'filtered_relation': [
'has no attribute "name"',
@@ -200,7 +191,7 @@ IGNORED_ERRORS = {
],
'from_db_value': [
'"Cash" has no attribute',
'"__str__" of "Decimal"',
'Argument 1 to "__str__" of "Decimal"',
],
'get_object_or_404': [
'Argument 1 to "get_object_or_404" has incompatible type "str"; '
@@ -249,7 +240,6 @@ IGNORED_ERRORS = {
],
'middleware': [
re.compile(r'"(HttpRequest|WSGIRequest)" has no attribute'),
'Incompatible types in assignment (expression has type "HttpResponseBase", variable has type "HttpResponse")',
],
'many_to_many': [
'(expression has type "List[Article]", variable has type "Article_RelatedManager2',
@@ -286,7 +276,6 @@ IGNORED_ERRORS = {
'"ImageFile" has no attribute "was_opened"',
'Incompatible type for "size" of "FloatModel" (got "object", expected "Union[float, int, str, Combinable]")',
'Incompatible type for "value" of "IntegerModel" (got "object", expected',
'"Child" has no attribute "get_foo_display"',
],
'model_forms': [
'"render" of "Widget"',
@@ -380,7 +369,6 @@ IGNORED_ERRORS = {
'responses': [
'Argument 1 to "TextIOWrapper" has incompatible type "HttpResponse"; expected "IO[bytes]"',
'"FileLike" has no attribute "closed"',
'Argument 1 to "TextIOWrapper" has incompatible type "HttpResponse"; expected "BinaryIO"',
],
'reverse_lookup': [
"Cannot resolve keyword 'choice' into field"
@@ -388,9 +376,6 @@ IGNORED_ERRORS = {
'settings_tests': [
'Argument 1 to "Settings" has incompatible type "Optional[str]"; expected "str"'
],
'shortcuts': [
'error: "Context" has no attribute "request"',
],
'signals': [
'Argument 1 to "append" of "list" has incompatible type "Tuple[Any, Any, Optional[Any], Any]";'
],
@@ -402,7 +387,7 @@ IGNORED_ERRORS = {
'"RequestSite" of "Union[Site, RequestSite]" has no attribute "id"',
],
'syndication_tests': [
'Argument 1 to "add_domain" has incompatible type "*Tuple[object, ...]"',
'List or tuple expected as variable arguments'
],
'sessions_tests': [
'Incompatible types in assignment (expression has type "None", variable has type "int")',
@@ -443,7 +428,6 @@ IGNORED_ERRORS = {
'test_client_regress': [
'(expression has type "Dict[<nothing>, <nothing>]", variable has type "SessionBase")',
'Unsupported left operand type for + ("None")',
'Argument 1 to "len" has incompatible type "Context"; expected "Sized"',
],
'transactions': [
'Incompatible types in assignment (expression has type "Thread", variable has type "Callable[[], Any]")'

View File

@@ -1,5 +1,5 @@
from pytest_mypy_plugins.collect import File
from pytest_mypy_plugins.item import YamlTestItem
from pytest_mypy.collect import File
from pytest_mypy.item import YamlTestItem
def django_plugin_hook(test_item: YamlTestItem) -> None:

View File

@@ -14,8 +14,8 @@ from scripts.enabled_test_modules import (
)
DJANGO_COMMIT_REFS: Dict[str, Tuple[str, str]] = {
'2.2': ('stable/2.2.x', '996be04c3ceb456754d9d527d4d708f30727f07e'),
'3.0': ('stable/3.0.x', 'd9f1792c7649e9f946f4a3a35a76bddf5a412b8b')
'2.2': ('stable/2.2.x', '86befcc172c23170a720b3e0c06db51a99b3da59'),
'3.0': ('stable/3.0.x', '6cb30414bc0f83b49afc4cae76d4af5656effe9a')
}
PROJECT_DIRECTORY = Path(__file__).parent.parent
DJANGO_SOURCE_DIRECTORY = PROJECT_DIRECTORY / 'django-sources' # type: Path

View File

@@ -21,14 +21,14 @@ with open('README.md', 'r') as f:
readme = f.read()
dependencies = [
'mypy>=0.780,<0.790',
'mypy>=0.760,<0.770',
'typing-extensions',
'django',
]
setup(
name="django-stubs",
version="1.5.0",
version="1.4.0",
description='Mypy stubs for Django',
long_description=readme,
long_description_content_type='text/markdown',
@@ -45,10 +45,6 @@ setup(
'Development Status :: 3 - Alpha',
'License :: OSI Approved :: MIT License',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8'
],
project_urls={
'Release notes': 'https://github.com/typeddjango/django-stubs/releases',
},
'Programming Language :: Python :: 3.7'
]
)

View File

@@ -1,126 +0,0 @@
# "Happy path" test for model admin, trying to cover as many valid
# configurations as possible.
- case: test_full_admin
main: |
from django.contrib import admin
from django.forms import Form, Textarea
from django.db import models
from django.core.paginator import Paginator
from django.contrib.admin.sites import AdminSite
from django.db.models.options import Options
from django.http.request import HttpRequest
from django.db.models.query import QuerySet
def an_action(modeladmin: admin.ModelAdmin, request: HttpRequest, queryset: QuerySet) -> None:
pass
class A(admin.ModelAdmin):
# BaseModelAdmin
autocomplete_fields = ("strs",)
raw_id_fields = ["strs"]
fields = (
"a field",
["a", "list of", "fields"],
)
exclude = ("a", "b")
fieldsets = [
(None, {"fields": ["a", "b"]}),
("group", {"fields": ("c",), "classes": ("a",), "description": "foo"}),
]
form = Form
filter_vertical = ("fields",)
filter_horizontal = ("plenty", "of", "fields")
radio_fields = {
"some_field": admin.VERTICAL,
"another_field": admin.HORIZONTAL,
}
prepopulated_fields = {"slug": ("title",)}
formfield_overrides = {models.TextField: {"widget": Textarea}}
readonly_fields = ("date_modified",)
ordering = ("-pk", "date_modified")
sortable_by = ["pk"]
view_on_site = True
show_full_result_count = False
# ModelAdmin
list_display = ("pk",)
list_display_links = ("str",)
list_filter = ("str", admin.SimpleListFilter, ("str", admin.SimpleListFilter))
list_select_related = True
list_per_page = 1
list_max_show_all = 2
list_editable = ("a", "b")
search_fields = ("c", "d")
date_hirearchy = "f"
save_as = False
save_as_continue = True
save_on_top = False
paginator = Paginator
presserve_filters = False
inlines = (admin.TabularInline, admin.StackedInline)
add_form_template = "template"
change_form_template = "template"
change_list_template = "template"
delete_confirmation_template = "template"
delete_selected_confirmation_template = "template"
object_history_template = "template"
popup_response_template = "template"
actions = (an_action,)
actions_on_top = True
actions_on_bottom = False
actions_selection_counter = True
admin_site = AdminSite()
# This test is here to make sure we're not running into a mypy issue which is
# worked around using a somewhat complicated _ListOrTuple union type. Once the
# issue is solved upstream this test should pass even with the workaround
# replaced by a simpler Sequence type.
# https://github.com/python/mypy/issues/8921
- case: test_fieldset_workaround_regression
main: |
from django.contrib import admin
class A(admin.ModelAdmin):
fieldsets = (
(None, {
'fields': ('name',),
}),
)
- case: errors_on_omitting_fields_from_fieldset_opts
main: |
from django.contrib import admin
class A(admin.ModelAdmin):
fieldsets = [ # type: ignore
(None, {}), # E: Key 'fields' missing for TypedDict "_FieldOpts"
]
- case: errors_on_invalid_radio_fields
main: |
from django.contrib import admin
class A(admin.ModelAdmin):
radio_fields = {"some_field": 0} # E: Dict entry 0 has incompatible type "str": "Literal[0]"; expected "str": "Union[Literal[1], Literal[2]]"
class B(admin.ModelAdmin):
radio_fields = {1: admin.VERTICAL} # E: Dict entry 0 has incompatible type "int": "Literal[2]"; expected "str": "Union[Literal[1], Literal[2]]"
- case: errors_for_invalid_formfield_overrides
main: |
from django.contrib import admin
from django.forms import Textarea
class A(admin.ModelAdmin):
formfield_overrides = {
"not a field": { # E: Dict entry 0 has incompatible type "str": "Dict[str, Any]"; expected "Type[Field[Any, Any]]": "Mapping[str, Any]"
"widget": Textarea
}
}
- case: errors_for_invalid_action_signature
main: |
from django.contrib import admin
from django.http.request import HttpRequest
from django.db.models.query import QuerySet
def an_action(modeladmin: None) -> None:
pass
class A(admin.ModelAdmin):
actions = [an_action] # E: List item 0 has incompatible type "Callable[[None], None]"; expected "Callable[[ModelAdmin, HttpRequest, QuerySet[Any]], None]"

View File

@@ -1,43 +0,0 @@
- case: login_required_bare
main: |
from django.contrib.auth.decorators import login_required
@login_required
def view_func(request): ...
reveal_type(view_func) # N: Revealed type is 'def (request: Any) -> Any'
- case: login_required_fancy
main: |
from django.contrib.auth.decorators import login_required
from django.core.handlers.wsgi import WSGIRequest
from django.http import HttpResponse
@login_required(redirect_field_name='a', login_url='b')
def view_func(request: WSGIRequest, arg: str) -> HttpResponse: ...
reveal_type(view_func) # N: Revealed type is 'def (request: django.core.handlers.wsgi.WSGIRequest, arg: builtins.str) -> django.http.response.HttpResponse'
- case: login_required_weird
main: |
from django.contrib.auth.decorators import login_required
# This is non-conventional usage, but covered in Django tests, so we allow it.
def view_func(request): ...
wrapped_view = login_required(view_func, redirect_field_name='a', login_url='b')
reveal_type(wrapped_view) # N: Revealed type is 'def (request: Any) -> Any'
- case: login_required_incorrect_return
main: |
from django.contrib.auth.decorators import login_required
@login_required() # E: Value of type variable "_VIEW" of function cannot be "Callable[[Any], str]"
def view_func2(request) -> str: ...
- case: user_passes_test
main: |
from django.contrib.auth.decorators import user_passes_test
@user_passes_test(lambda u: u.username.startswith('super'))
def view_func(request): ...
reveal_type(view_func) # N: Revealed type is 'def (request: Any) -> Any'
- case: user_passes_test_bare_is_error
main: |
from django.http.response import HttpResponse
from django.contrib.auth.decorators import user_passes_test
@user_passes_test # E: Argument 1 to "user_passes_test" has incompatible type "Callable[[Any], HttpResponse]"; expected "Callable[[AbstractUser], bool]"
def view_func(request) -> HttpResponse: ...
- case: permission_required
main: |
from django.contrib.auth.decorators import permission_required
@permission_required('polls.can_vote')
def view_func(request): ...

View File

@@ -1,28 +0,0 @@
- case: atomic_bare
main: |
from django.db.transaction import atomic
@atomic
def func(x: int) -> list: ...
reveal_type(func) # N: Revealed type is 'def (x: builtins.int) -> builtins.list[Any]'
- case: atomic_args
main: |
from django.db.transaction import atomic
@atomic(using='bla', savepoint=False)
def func(x: int) -> list: ...
reveal_type(func) # N: Revealed type is 'def (x: builtins.int) -> builtins.list[Any]'
- case: non_atomic_requests_bare
main: |
from django.db.transaction import non_atomic_requests
@non_atomic_requests
def view_func(request): ...
reveal_type(view_func) # N: Revealed type is 'def (request: Any) -> Any'
- case: non_atomic_requests_args
main: |
from django.http.request import HttpRequest
from django.http.response import HttpResponse
from django.db.transaction import non_atomic_requests
@non_atomic_requests
def view_func(request: HttpRequest, arg: str) -> HttpResponse: ...
reveal_type(view_func) # N: Revealed type is 'def (request: django.http.request.HttpRequest, arg: builtins.str) -> django.http.response.HttpResponse'

View File

@@ -10,12 +10,6 @@
reveal_type(User().is_active) # N: Revealed type is 'builtins.bool*'
reveal_type(User().date_joined) # N: Revealed type is 'datetime.datetime*'
reveal_type(User().last_login) # N: Revealed type is 'Union[datetime.datetime, None]'
reveal_type(User().is_authenticated) # N: Revealed type is 'Literal[True]'
reveal_type(User().is_anonymous) # N: Revealed type is 'Literal[False]'
from django.contrib.auth.models import AnonymousUser
reveal_type(AnonymousUser().is_authenticated) # N: Revealed type is 'Literal[False]'
reveal_type(AnonymousUser().is_anonymous) # N: Revealed type is 'Literal[True]'
from django.contrib.auth.models import Permission
reveal_type(Permission().name) # N: Revealed type is 'builtins.str*'

View File

@@ -1,14 +0,0 @@
- case: state_attribute_has_a_type_of_model_state
main: |
from myapp.models import MyUser
user = MyUser(pk=1)
reveal_type(user._state) # N: Revealed type is 'django.db.models.base.ModelState'
installed_apps:
- myapp
files:
- path: myapp/__init__.py
- path: myapp/models.py
content: |
from django.db import models
class MyUser(models.Model):
pass

View File

@@ -1,12 +0,0 @@
- case: testcase_client_attr
main: |
from django.test.testcases import TestCase
class ExampleTestCase(TestCase):
def test_method(self) -> None:
reveal_type(self.client) # N: Revealed type is 'django.test.client.Client'
resp = self.client.post('/url', {'doit': 'srs'}, 'application/json', False, True, extra='value')
reveal_type(resp.status_code) # N: Revealed type is 'builtins.int'
# Attributes monkey-patched by test Client class:
resp.json()
reveal_type(resp.wsgi_request) # N: Revealed type is 'django.core.handlers.wsgi.WSGIRequest'

View File

@@ -2,11 +2,11 @@
disable_cache: true
main: |
from django.http.request import HttpRequest
reveal_type(HttpRequest().user) # N: Revealed type is 'Union[myapp.models.MyUser, django.contrib.auth.models.AnonymousUser]'
reveal_type(HttpRequest().user) # N: Revealed type is 'myapp.models.MyUser'
# check that other fields work ok
reveal_type(HttpRequest().method) # N: Revealed type is 'Union[builtins.str, None]'
custom_settings: |
INSTALLED_APPS = ('django.contrib.contenttypes', 'django.contrib.auth', 'myapp')
INSTALLED_APPS = ('django.contrib.contenttypes', 'myapp')
AUTH_USER_MODEL='myapp.MyUser'
files:
- path: myapp/__init__.py
@@ -15,15 +15,3 @@
from django.db import models
class MyUser(models.Model):
pass
- case: request_object_user_can_be_descriminated
disable_cache: true
main: |
from django.http.request import HttpRequest
request = HttpRequest()
reveal_type(request.user) # N: Revealed type is 'Union[django.contrib.auth.models.User, django.contrib.auth.models.AnonymousUser]'
if not request.user.is_anonymous:
reveal_type(request.user) # N: Revealed type is 'django.contrib.auth.models.User'
if request.user.is_authenticated:
reveal_type(request.user) # N: Revealed type is 'django.contrib.auth.models.User'
custom_settings: |
INSTALLED_APPS = ('django.contrib.contenttypes', 'django.contrib.auth')

View File

@@ -36,24 +36,3 @@
from django.db import models
class MyUser(models.Model):
pass
- case: check_render_function_arguments_annotations
main: |
from typing import Any
from typing_extensions import TypedDict
from django.shortcuts import render
from django.http.request import HttpRequest
TestContext = TypedDict("TestContext", {"user": Any})
test_context: TestContext = {"user": "test"}
reveal_type(test_context) # N: Revealed type is 'TypedDict('main.TestContext', {'user': Any})'
reveal_type(render(HttpRequest(), '', test_context)) # N: Revealed type is 'django.http.response.HttpResponse'
- case: check_redirect_return_annotation
main: |
from django.shortcuts import redirect
reveal_type(redirect(to = '', permanent = True)) # N: Revealed type is 'django.http.response.HttpResponsePermanentRedirect'
reveal_type(redirect(to = '', permanent = False)) # N: Revealed type is 'django.http.response.HttpResponseRedirect'
var = True
reveal_type(redirect(to = '', permanent = var)) # N: Revealed type is 'Union[django.http.response.HttpResponseRedirect, django.http.response.HttpResponsePermanentRedirect]'

View File

@@ -1,20 +0,0 @@
- case: method_decorator_class
main: |
from django.views.generic.base import View
from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import login_required
@method_decorator(login_required, name='dispatch')
class TestView(View): ...
reveal_type(TestView()) # N: Revealed type is 'main.TestView'
- case: method_decorator_function
main: |
from django.views.generic.base import View
from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import login_required
from django.http.response import HttpResponse
from django.http.request import HttpRequest
class TestView(View):
@method_decorator(login_required)
def dispatch(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
return super().dispatch(request, *args, **kwargs)
reveal_type(dispatch) # N: Revealed type is 'def (self: main.TestView, request: django.http.request.HttpRequest, *args: Any, **kwargs: Any) -> django.http.response.HttpResponse'