mirror of
https://github.com/davidhalter/django-stubs.git
synced 2025-12-10 22:11:54 +08:00
Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
540e28f4c6 | ||
|
|
4ac43c6ed6 | ||
|
|
cadd6c963b | ||
|
|
041754f817 | ||
|
|
c0c5d1e588 | ||
|
|
f824003cc4 | ||
|
|
58f1833cab | ||
|
|
cbb6a7a9ac | ||
|
|
2c4827bbaf | ||
|
|
5a151bf851 | ||
|
|
cbc7159995 | ||
|
|
df4c17a947 | ||
|
|
445abc046c | ||
|
|
557b7a4fa3 | ||
|
|
8343d76895 | ||
|
|
8d986a0f43 | ||
|
|
e9a90ebff0 | ||
|
|
7b74a6944a | ||
|
|
83f11a0fc6 | ||
|
|
2829faf1af | ||
|
|
d061e84cc7 | ||
|
|
3a9263dc62 | ||
|
|
14aea2b4d4 | ||
|
|
287c64d6fb | ||
|
|
6601121db2 | ||
|
|
87d59c7c1a | ||
|
|
8402e7c53e |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -9,3 +9,5 @@ build/
|
||||
dist/
|
||||
pip-wheel-metadata/
|
||||
.pytest_cache/
|
||||
/.envrc
|
||||
/.direnv
|
||||
|
||||
4
.gitmodules
vendored
4
.gitmodules
vendored
@@ -1,4 +0,0 @@
|
||||
[submodule "django-sources"]
|
||||
path = django-sources
|
||||
url = https://github.com/django/django.git
|
||||
branch = stable/2.2.x
|
||||
23
.travis.yml
23
.travis.yml
@@ -8,13 +8,23 @@ jobs:
|
||||
python: 3.7
|
||||
script: 'pytest'
|
||||
|
||||
- name: Typecheck Django test suite with python 3.7
|
||||
- name: Typecheck Django 3.0 test suite with python 3.7
|
||||
python: 3.7
|
||||
script: 'python ./scripts/typecheck_tests.py'
|
||||
script: |
|
||||
pip install Django==3.0.*
|
||||
python ./scripts/typecheck_tests.py --django_version=3.0
|
||||
|
||||
- name: Typecheck Django test suite with python 3.6
|
||||
- name: Typecheck Django 3.0 test suite with python 3.6
|
||||
python: 3.6
|
||||
script: 'python ./scripts/typecheck_tests.py'
|
||||
script: |
|
||||
pip install Django==3.0.*
|
||||
python ./scripts/typecheck_tests.py --django_version=3.0
|
||||
|
||||
- name: Typecheck Django 2.2 test suite with python 3.7
|
||||
python: 3.7
|
||||
script: |
|
||||
pip install Django==2.2.*
|
||||
python ./scripts/typecheck_tests.py --django_version=2.2
|
||||
|
||||
- name: Mypy for plugin code
|
||||
python: 3.7
|
||||
@@ -37,8 +47,9 @@ jobs:
|
||||
script: 'isort --check --diff'
|
||||
|
||||
before_install: |
|
||||
sudo apt update
|
||||
sudo apt install binutils libproj-dev gdal-bin
|
||||
sudo add-apt-repository ppa:ubuntugis/ubuntugis-unstable -y
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y binutils libproj-dev gdal-bin
|
||||
pip install -U pip setuptools wheel
|
||||
install: |
|
||||
pip install -r ./dev-requirements.txt
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
# pep484 stubs for Django framework
|
||||
|
||||
[](https://travis-ci.org/typeddjango/django-stubs)
|
||||
[](https://travis-ci.com/typeddjango/django-stubs)
|
||||
[](http://mypy-lang.org/)
|
||||
[](https://gitter.im/mypy-django/Lobby)
|
||||
|
||||
@@ -22,6 +22,7 @@ pip install django-stubs
|
||||
|
||||
| django-stubs | mypy version | django version | python version
|
||||
| ------------ | ---- | ---- | ---- |
|
||||
| 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
|
||||
@@ -69,7 +70,7 @@ class MyUserManager(models.Manager['MyUser']):
|
||||
pass
|
||||
|
||||
class MyUser(models.Model):
|
||||
objects = UserManager()
|
||||
objects = MyUserManager()
|
||||
```
|
||||
|
||||
work, which should make a error messages a bit better.
|
||||
|
||||
@@ -4,4 +4,5 @@ psycopg2
|
||||
flake8==3.7.8
|
||||
flake8-pyi==19.3.0
|
||||
isort==4.3.21
|
||||
gitpython==3.0.5
|
||||
-e .
|
||||
|
||||
Submodule django-sources deleted from f452d4232e
@@ -1,5 +1,6 @@
|
||||
from typing import Any, Iterator, Type, Optional, Dict
|
||||
|
||||
from django.apps.registry import Apps
|
||||
from django.db.models.base import Model
|
||||
|
||||
MODELS_MODULE_NAME: str
|
||||
@@ -7,11 +8,11 @@ MODELS_MODULE_NAME: str
|
||||
class AppConfig:
|
||||
name: str = ...
|
||||
module: Optional[Any] = ...
|
||||
apps: None = ...
|
||||
apps: Optional[Apps] = ...
|
||||
label: str = ...
|
||||
verbose_name: str = ...
|
||||
path: str = ...
|
||||
models_module: None = ...
|
||||
models_module: Optional[str] = ...
|
||||
models: Dict[str, Type[Model]] = ...
|
||||
def __init__(self, app_name: str, app_module: Optional[Any]) -> None: ...
|
||||
@classmethod
|
||||
|
||||
@@ -345,7 +345,7 @@ SESSION_COOKIE_PATH = "/"
|
||||
SESSION_COOKIE_HTTPONLY = True
|
||||
# Whether to set the flag restricting cookie leaks on cross-site requests.
|
||||
# This can be 'Lax', 'Strict', or None to disable the flag.
|
||||
SESSION_COOKIE_SAMESITE = "Lax"
|
||||
SESSION_COOKIE_SAMESITE: Optional[str] = ...
|
||||
# Whether to save the session data on every request.
|
||||
SESSION_SAVE_EVERY_REQUEST = False
|
||||
# Whether a user's session cookie expires when the Web browser is closed.
|
||||
@@ -413,7 +413,7 @@ CSRF_COOKIE_DOMAIN = None
|
||||
CSRF_COOKIE_PATH = "/"
|
||||
CSRF_COOKIE_SECURE = False
|
||||
CSRF_COOKIE_HTTPONLY = False
|
||||
CSRF_COOKIE_SAMESITE = "Lax"
|
||||
CSRF_COOKIE_SAMESITE: Optional[str] = ...
|
||||
CSRF_HEADER_NAME = "HTTP_X_CSRFTOKEN"
|
||||
CSRF_TRUSTED_ORIGINS: List[str] = ...
|
||||
CSRF_USE_SESSIONS = False
|
||||
|
||||
@@ -24,7 +24,7 @@ class SimpleListFilter(ListFilter):
|
||||
parameter_name: Any = ...
|
||||
lookup_choices: Any = ...
|
||||
def value(self) -> Optional[str]: ...
|
||||
def lookups(self, request: Any, model_admin: Any) -> None: ...
|
||||
def lookups(self, request: Any, model_admin: Any) -> List[Tuple[Any, str]]: ...
|
||||
|
||||
class FieldListFilter(ListFilter):
|
||||
field: Field = ...
|
||||
|
||||
@@ -86,7 +86,7 @@ class BaseModelAdmin:
|
||||
def get_sortable_by(self, request: HttpRequest) -> Union[List[Callable], List[str], Tuple]: ...
|
||||
def lookup_allowed(self, lookup: str, value: str) -> bool: ...
|
||||
def to_field_allowed(self, request: HttpRequest, to_field: str) -> bool: ...
|
||||
def has_add_permission(self, request: HttpRequest, obj: Optional[Model] = ...) -> bool: ...
|
||||
def has_add_permission(self, request: HttpRequest) -> bool: ...
|
||||
def has_change_permission(self, request: HttpRequest, obj: Optional[Model] = ...) -> bool: ...
|
||||
def has_delete_permission(self, request: HttpRequest, obj: Optional[Model] = ...) -> bool: ...
|
||||
def has_view_permission(self, request: HttpRequest, obj: Optional[Model] = ...) -> bool: ...
|
||||
|
||||
@@ -27,7 +27,7 @@ class ResultList(list):
|
||||
def results(cl: ChangeList) -> Iterator[ResultList]: ...
|
||||
def result_hidden_fields(cl: ChangeList) -> Iterator[BoundField]: ...
|
||||
def result_list(
|
||||
cl: ChangeList
|
||||
cl: ChangeList,
|
||||
) -> Dict[
|
||||
str, Union[List[Dict[str, Optional[Union[int, str]]]], List[ResultList], List[BoundField], ChangeList, int]
|
||||
]: ...
|
||||
|
||||
@@ -81,9 +81,7 @@ class ChangeList:
|
||||
paginator: Any = ...
|
||||
def get_results(self, request: WSGIRequest) -> None: ...
|
||||
def get_ordering_field(self, field_name: Union[Callable, str]) -> Optional[Union[CombinedExpression, str]]: ...
|
||||
def get_ordering(
|
||||
self, request: WSGIRequest, queryset: QuerySet
|
||||
) -> Union[List[Union[Combinable, str]], List[Union[OrderBy, str]]]: ...
|
||||
def get_ordering(self, request: WSGIRequest, queryset: QuerySet) -> List[Union[OrderBy, Combinable, str]]: ...
|
||||
def get_ordering_field_columns(self) -> OrderedDict: ...
|
||||
def get_queryset(self, request: WSGIRequest) -> QuerySet: ...
|
||||
def apply_select_related(self, qs: QuerySet) -> QuerySet: ...
|
||||
|
||||
@@ -3,20 +3,22 @@ from typing import Any, Optional, Set, Union
|
||||
from django.contrib.auth.base_user import AbstractBaseUser
|
||||
from django.contrib.auth.models import AnonymousUser, User
|
||||
|
||||
from django.db.models.base import Model
|
||||
|
||||
_AnyUser = Union[Model, AnonymousUser]
|
||||
|
||||
UserModel: Any
|
||||
|
||||
class ModelBackend:
|
||||
def authenticate(
|
||||
self, request: Any, username: Optional[Union[int, str]] = ..., password: Optional[str] = ..., **kwargs: Any
|
||||
self, request: Any, username: Optional[str] = ..., password: Optional[str] = ..., **kwargs: Any
|
||||
) -> Optional[AbstractBaseUser]: ...
|
||||
def user_can_authenticate(self, user: Optional[AbstractBaseUser]) -> bool: ...
|
||||
def get_user_permissions(self, user_obj: AbstractBaseUser, obj: None = ...) -> Set[str]: ...
|
||||
def get_group_permissions(self, user_obj: AbstractBaseUser, obj: None = ...) -> Set[str]: ...
|
||||
def get_all_permissions(self, user_obj: AbstractBaseUser, obj: Optional[str] = ...) -> Set[str]: ...
|
||||
def has_perm(
|
||||
self, user_obj: Union[AbstractBaseUser, AnonymousUser], perm: str, obj: Optional[str] = ...
|
||||
) -> bool: ...
|
||||
def has_module_perms(self, user_obj: Union[AbstractBaseUser, AnonymousUser], app_label: str) -> bool: ...
|
||||
def user_can_authenticate(self, user: Optional[_AnyUser]) -> bool: ...
|
||||
def get_user_permissions(self, user_obj: _AnyUser, obj: Optional[Model] = ...) -> Set[str]: ...
|
||||
def get_group_permissions(self, user_obj: _AnyUser, obj: Optional[Model] = ...) -> Set[str]: ...
|
||||
def get_all_permissions(self, user_obj: _AnyUser, obj: Optional[Model] = ...) -> Set[str]: ...
|
||||
def has_perm(self, user_obj: _AnyUser, perm: str, obj: Optional[Model] = ...) -> bool: ...
|
||||
def has_module_perms(self, user_obj: _AnyUser, app_label: str) -> bool: ...
|
||||
def get_user(self, user_id: int) -> AbstractBaseUser: ...
|
||||
|
||||
class AllowAllUsersModelBackend(ModelBackend): ...
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import Any, Callable, List
|
||||
from typing import Any, Callable, List, Optional
|
||||
|
||||
from django import http
|
||||
from django.http.response import HttpResponse, HttpResponseRedirect
|
||||
@@ -23,6 +23,6 @@ class PermissionRequiredMixin(AccessMixin):
|
||||
def dispatch(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse: ...
|
||||
|
||||
class UserPassesTestMixin(AccessMixin):
|
||||
def test_func(self) -> None: ...
|
||||
def test_func(self) -> Optional[bool]: ...
|
||||
def get_test_func(self) -> Callable: ...
|
||||
def dispatch(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse: ...
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import Any, Collection, Optional, Set, Tuple, Type, TypeVar
|
||||
from typing import Any, Collection, Optional, Set, Tuple, Type, TypeVar, Union
|
||||
|
||||
from django.contrib.auth.base_user import AbstractBaseUser as AbstractBaseUser, BaseUserManager as BaseUserManager
|
||||
from django.contrib.auth.validators import UnicodeUsernameValidator
|
||||
@@ -8,22 +8,28 @@ from django.db.models.manager import EmptyManager
|
||||
|
||||
from django.db import models
|
||||
|
||||
_AnyUser = Union[Model, "AnonymousUser"]
|
||||
|
||||
def update_last_login(sender: Type[AbstractBaseUser], user: AbstractBaseUser, **kwargs: Any) -> None: ...
|
||||
|
||||
class PermissionManager(models.Manager):
|
||||
class PermissionManager(models.Manager["Permission"]):
|
||||
def get_by_natural_key(self, codename: str, app_label: str, model: str) -> Permission: ...
|
||||
|
||||
class Permission(models.Model):
|
||||
content_type_id: int
|
||||
objects: PermissionManager
|
||||
|
||||
name = models.CharField(max_length=255)
|
||||
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
|
||||
codename = models.CharField(max_length=100)
|
||||
def natural_key(self) -> Tuple[str, str, str]: ...
|
||||
|
||||
class GroupManager(models.Manager):
|
||||
class GroupManager(models.Manager["Group"]):
|
||||
def get_by_natural_key(self, name: str) -> Group: ...
|
||||
|
||||
class Group(models.Model):
|
||||
objects: GroupManager
|
||||
|
||||
name = models.CharField(max_length=150)
|
||||
permissions = models.ManyToManyField(Permission)
|
||||
def natural_key(self): ...
|
||||
@@ -40,12 +46,12 @@ class UserManager(BaseUserManager[_T]):
|
||||
|
||||
class PermissionsMixin(models.Model):
|
||||
is_superuser = models.BooleanField()
|
||||
groups: models.ManyToManyField = models.ManyToManyField(Group)
|
||||
user_permissions: models.ManyToManyField = models.ManyToManyField(Permission)
|
||||
def get_group_permissions(self, obj: None = ...) -> Set[str]: ...
|
||||
def get_all_permissions(self, obj: Optional[str] = ...) -> Set[str]: ...
|
||||
def has_perm(self, perm: str, obj: Optional[str] = ...) -> bool: ...
|
||||
def has_perms(self, perm_list: Collection[str], obj: None = ...) -> bool: ...
|
||||
groups = models.ManyToManyField(Group)
|
||||
user_permissions = models.ManyToManyField(Permission)
|
||||
def get_group_permissions(self, obj: Optional[_AnyUser] = ...) -> Set[str]: ...
|
||||
def get_all_permissions(self, obj: Optional[_AnyUser] = ...) -> Set[str]: ...
|
||||
def has_perm(self, perm: str, obj: Optional[_AnyUser] = ...) -> bool: ...
|
||||
def has_perms(self, perm_list: Collection[str], obj: Optional[_AnyUser] = ...) -> bool: ...
|
||||
def has_module_perms(self, app_label: str) -> bool: ...
|
||||
|
||||
class AbstractUser(AbstractBaseUser, PermissionsMixin): # type: ignore
|
||||
@@ -82,10 +88,10 @@ class AnonymousUser:
|
||||
def groups(self) -> EmptyManager: ...
|
||||
@property
|
||||
def user_permissions(self) -> EmptyManager: ...
|
||||
def get_group_permissions(self, obj: None = ...) -> Set[Any]: ...
|
||||
def get_all_permissions(self, obj: Any = ...) -> Set[str]: ...
|
||||
def has_perm(self, perm: str, obj: None = ...) -> bool: ...
|
||||
def has_perms(self, perm_list: Collection[str], obj: None = ...) -> bool: ...
|
||||
def get_group_permissions(self, obj: Optional[_AnyUser] = ...) -> Set[Any]: ...
|
||||
def get_all_permissions(self, obj: Optional[_AnyUser] = ...) -> Set[str]: ...
|
||||
def has_perm(self, perm: str, obj: Optional[_AnyUser] = ...) -> bool: ...
|
||||
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) -> bool: ...
|
||||
|
||||
@@ -6,15 +6,16 @@ from django.db import models
|
||||
|
||||
SITE_CACHE: Any
|
||||
|
||||
class SiteManager(models.Manager):
|
||||
class SiteManager(models.Manager["Site"]):
|
||||
def get_current(self, request: Optional[HttpRequest] = ...) -> Site: ...
|
||||
def clear_cache(self) -> None: ...
|
||||
def get_by_natural_key(self, domain: str) -> Site: ...
|
||||
|
||||
class Site(models.Model):
|
||||
domain: models.CharField = ...
|
||||
name: models.CharField = ...
|
||||
objects: SiteManager = ...
|
||||
objects: SiteManager
|
||||
|
||||
domain = models.CharField(max_length=100)
|
||||
name = models.CharField(max_length=50)
|
||||
def natural_key(self) -> Tuple[str]: ...
|
||||
|
||||
def clear_site_cache(sender: Type[Site], **kwargs: Any) -> None: ...
|
||||
|
||||
@@ -37,7 +37,7 @@ def get_finders() -> Iterator[BaseFinder]: ...
|
||||
def get_finder(import_path: Literal["django.contrib.staticfiles.finders.FileSystemFinder"]) -> FileSystemFinder: ...
|
||||
@overload
|
||||
def get_finder(
|
||||
import_path: Literal["django.contrib.staticfiles.finders.AppDirectoriesFinder"]
|
||||
import_path: Literal["django.contrib.staticfiles.finders.AppDirectoriesFinder"],
|
||||
) -> AppDirectoriesFinder: ...
|
||||
@overload
|
||||
def get_finder(import_path: str) -> BaseFinder: ...
|
||||
|
||||
@@ -12,6 +12,7 @@ class Tags:
|
||||
security: str = ...
|
||||
signals: str = ...
|
||||
templates: str = ...
|
||||
translation: str = ...
|
||||
urls: str = ...
|
||||
|
||||
class CheckRegistry:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import types
|
||||
from typing import Any, TypeVar, Type, Iterable
|
||||
from typing import Any, TypeVar, Type, Iterable, Optional
|
||||
|
||||
from django.core.mail.message import EmailMessage
|
||||
|
||||
@@ -7,7 +7,7 @@ _T = TypeVar("_T", bound="BaseEmailBackend")
|
||||
|
||||
class BaseEmailBackend:
|
||||
def __init__(self, fail_silently: bool = ..., **kwargs: Any) -> None: ...
|
||||
def open(self) -> bool: ...
|
||||
def open(self) -> Optional[bool]: ...
|
||||
def close(self) -> None: ...
|
||||
def __enter__(self: _T) -> _T: ...
|
||||
def __exit__(
|
||||
|
||||
3
django-stubs/core/mail/backends/console.pyi
Normal file
3
django-stubs/core/mail/backends/console.pyi
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.core.mail.backends.base import BaseEmailBackend
|
||||
|
||||
class EmailBackend(BaseEmailBackend): ...
|
||||
3
django-stubs/core/mail/backends/dummy.pyi
Normal file
3
django-stubs/core/mail/backends/dummy.pyi
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.core.mail.backends.base import BaseEmailBackend
|
||||
|
||||
class EmailBackend(BaseEmailBackend): ...
|
||||
3
django-stubs/core/mail/backends/filebased.pyi
Normal file
3
django-stubs/core/mail/backends/filebased.pyi
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.core.mail.backends.base import BaseEmailBackend
|
||||
|
||||
class EmailBackend(BaseEmailBackend): ...
|
||||
3
django-stubs/core/mail/backends/locmem.pyi
Normal file
3
django-stubs/core/mail/backends/locmem.pyi
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.core.mail.backends.base import BaseEmailBackend
|
||||
|
||||
class EmailBackend(BaseEmailBackend): ...
|
||||
18
django-stubs/core/mail/backends/smtp.pyi
Normal file
18
django-stubs/core/mail/backends/smtp.pyi
Normal file
@@ -0,0 +1,18 @@
|
||||
import smtplib
|
||||
import threading
|
||||
from typing import Optional, Union
|
||||
|
||||
from django.core.mail.backends.base import BaseEmailBackend
|
||||
|
||||
class EmailBackend(BaseEmailBackend):
|
||||
host: str = ...
|
||||
port: int = ...
|
||||
username: str = ...
|
||||
password: str = ...
|
||||
use_tls: bool = ...
|
||||
use_ssl: bool = ...
|
||||
timeout: Optional[int] = ...
|
||||
ssl_keyfile: Optional[str] = ...
|
||||
ssl_certfile: Optional[str] = ...
|
||||
connection: Union[smtplib.SMTP_SSL, smtplib.SMTP, None] = ...
|
||||
_lock: threading.RLock = ...
|
||||
@@ -1,8 +1,10 @@
|
||||
from email._policybase import Policy # type: ignore
|
||||
from email.message import Message
|
||||
from email.mime.base import MIMEBase
|
||||
from email.mime.message import MIMEMessage
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.text import MIMEText
|
||||
from typing import Any, Dict, List, Optional, Sequence, Tuple, Union
|
||||
from typing import Any, Dict, List, Optional, Sequence, Tuple, Union, overload
|
||||
|
||||
utf8_charset: Any
|
||||
utf8_charset_qp: Any
|
||||
@@ -43,6 +45,11 @@ class SafeMIMEMultipart(MIMEMixin, MIMEMultipart):
|
||||
self, _subtype: str = ..., boundary: None = ..., _subparts: None = ..., encoding: str = ..., **_params: Any
|
||||
) -> None: ...
|
||||
|
||||
_AttachmentContent = Union[bytes, EmailMessage, Message, SafeMIMEText, str]
|
||||
_AttachmentTuple = Union[
|
||||
Tuple[str, _AttachmentContent], Tuple[Optional[str], _AttachmentContent, str], Tuple[str, _AttachmentContent, None]
|
||||
]
|
||||
|
||||
class EmailMessage:
|
||||
content_subtype: str = ...
|
||||
mixed_subtype: str = ...
|
||||
@@ -62,42 +69,42 @@ class EmailMessage:
|
||||
subject: str = ...,
|
||||
body: Optional[str] = ...,
|
||||
from_email: Optional[str] = ...,
|
||||
to: Optional[Union[Sequence[str], str]] = ...,
|
||||
bcc: Optional[Union[Sequence[str], str]] = ...,
|
||||
to: Optional[Sequence[str]] = ...,
|
||||
bcc: Optional[Sequence[str]] = ...,
|
||||
connection: Optional[Any] = ...,
|
||||
attachments: Optional[Union[List[Tuple[str, Union[str, bytes], str]], List[MIMEText]]] = ...,
|
||||
attachments: Optional[Sequence[Union[MIMEBase, _AttachmentTuple]]] = ...,
|
||||
headers: Optional[Dict[str, str]] = ...,
|
||||
cc: Optional[Union[Sequence[str], str]] = ...,
|
||||
reply_to: Optional[Union[List[Optional[str]], str]] = ...,
|
||||
cc: Optional[Sequence[str]] = ...,
|
||||
reply_to: Optional[Sequence[str]] = ...,
|
||||
) -> None: ...
|
||||
def get_connection(self, fail_silently: bool = ...) -> Any: ...
|
||||
# TODO: when typeshed gets more types for email.Message, move it to MIMEMessage, now it has too many false-positives
|
||||
def message(self) -> Any: ...
|
||||
def recipients(self) -> List[str]: ...
|
||||
def send(self, fail_silently: bool = ...) -> int: ...
|
||||
def attach(
|
||||
self,
|
||||
filename: Optional[Union[MIMEText, str]] = ...,
|
||||
content: Optional[Union[bytes, EmailMessage, SafeMIMEText, str]] = ...,
|
||||
mimetype: Optional[str] = ...,
|
||||
) -> None: ...
|
||||
@overload
|
||||
def attach(self, filename: MIMEText = ...) -> None: ...
|
||||
@overload
|
||||
def attach(self, filename: None = ..., content: _AttachmentContent = ..., mimetype: str = ...) -> None: ...
|
||||
@overload
|
||||
def attach(self, filename: str = ..., content: _AttachmentContent = ..., mimetype: Optional[str] = ...) -> None: ...
|
||||
def attach_file(self, path: str, mimetype: Optional[str] = ...) -> None: ...
|
||||
|
||||
class EmailMultiAlternatives(EmailMessage):
|
||||
alternative_subtype: str = ...
|
||||
alternatives: Any = ...
|
||||
alternatives: Sequence[Tuple[_AttachmentContent, str]] = ...
|
||||
def __init__(
|
||||
self,
|
||||
subject: str = ...,
|
||||
body: str = ...,
|
||||
from_email: Optional[str] = ...,
|
||||
to: Optional[List[str]] = ...,
|
||||
bcc: Optional[List[str]] = ...,
|
||||
to: Optional[Sequence[str]] = ...,
|
||||
bcc: Optional[Sequence[str]] = ...,
|
||||
connection: Optional[Any] = ...,
|
||||
attachments: None = ...,
|
||||
attachments: Optional[Sequence[Union[MIMEBase, _AttachmentTuple]]] = ...,
|
||||
headers: Optional[Dict[str, str]] = ...,
|
||||
alternatives: Optional[List[Tuple[str, str]]] = ...,
|
||||
cc: None = ...,
|
||||
reply_to: None = ...,
|
||||
alternatives: Optional[Sequence[Tuple[_AttachmentContent, str]]] = ...,
|
||||
cc: Optional[Sequence[str]] = ...,
|
||||
reply_to: Optional[Sequence[str]] = ...,
|
||||
) -> None: ...
|
||||
def attach_alternative(self, content: str, mimetype: str) -> None: ...
|
||||
def attach_alternative(self, content: _AttachmentContent, mimetype: str) -> None: ...
|
||||
|
||||
@@ -20,7 +20,7 @@ class BaseDatabaseWrapper:
|
||||
ops: Any = ...
|
||||
vendor: str = ...
|
||||
display_name: str = ...
|
||||
SchemaEditorClass: Any = ...
|
||||
SchemaEditorClass: Optional[BaseDatabaseSchemaEditor] = ...
|
||||
client_class: Any = ...
|
||||
creation_class: Any = ...
|
||||
features_class: Any = ...
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from collections import namedtuple
|
||||
from typing import Any, Dict, List, Optional, Set, Tuple, Type, Union
|
||||
from typing import Any, Dict, List, Optional, Set, Type
|
||||
|
||||
from django.db.backends.base.base import BaseDatabaseWrapper
|
||||
from django.db.backends.utils import CursorWrapper
|
||||
@@ -13,7 +13,7 @@ class BaseDatabaseIntrospection:
|
||||
data_types_reverse: Any = ...
|
||||
connection: Any = ...
|
||||
def __init__(self, connection: BaseDatabaseWrapper) -> None: ...
|
||||
def get_field_type(self, data_type: str, description: FieldInfo) -> Union[Tuple[str, Dict[str, int]], str]: ...
|
||||
def get_field_type(self, data_type: str, description: FieldInfo) -> str: ...
|
||||
def table_name_converter(self, name: str) -> str: ...
|
||||
def column_name_converter(self, name: str) -> str: ...
|
||||
def table_names(self, cursor: Optional[CursorWrapper] = ..., include_views: bool = ...) -> List[str]: ...
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
from datetime import date, datetime, timedelta
|
||||
from datetime import date, datetime, timedelta, time
|
||||
from decimal import Decimal
|
||||
from typing import Any, List, Optional, Sequence, Tuple, Type, Union
|
||||
|
||||
from django.core.management.color import Style
|
||||
from django.db.backends.base.base import BaseDatabaseWrapper
|
||||
from django.db.backends.sqlite3.base import DatabaseWrapper
|
||||
from django.db.backends.utils import CursorWrapper
|
||||
from django.db.models.base import Model
|
||||
from django.db.models.expressions import Case, Expression
|
||||
@@ -13,6 +12,8 @@ from django.db.models.sql.compiler import SQLCompiler
|
||||
from django.db import DefaultConnectionProxy
|
||||
from django.db.models.fields import Field
|
||||
|
||||
_Connection = Union[DefaultConnectionProxy, BaseDatabaseWrapper]
|
||||
|
||||
class BaseDatabaseOperations:
|
||||
compiler_module: str = ...
|
||||
integer_field_ranges: Any = ...
|
||||
@@ -25,8 +26,8 @@ class BaseDatabaseOperations:
|
||||
UNBOUNDED_FOLLOWING: Any = ...
|
||||
CURRENT_ROW: str = ...
|
||||
explain_prefix: Any = ...
|
||||
connection: Any = ...
|
||||
def __init__(self, connection: Optional[Union[DefaultConnectionProxy, BaseDatabaseWrapper]]) -> None: ...
|
||||
connection: _Connection = ...
|
||||
def __init__(self, connection: Optional[_Connection]) -> None: ...
|
||||
def autoinc_sql(self, table: str, column: str) -> None: ...
|
||||
def bulk_batch_size(self, fields: Any, objs: Any): ...
|
||||
def cache_key_culling_sql(self) -> str: ...
|
||||
@@ -75,10 +76,10 @@ class BaseDatabaseOperations:
|
||||
def prep_for_like_query(self, x: str) -> str: ...
|
||||
prep_for_iexact_query: Any = ...
|
||||
def validate_autopk_value(self, value: int) -> int: ...
|
||||
def adapt_unknown_value(self, value: Union[datetime, Decimal, int, str]) -> Union[int, str]: ...
|
||||
def adapt_unknown_value(self, value: Any) -> Any: ...
|
||||
def adapt_datefield_value(self, value: Optional[date]) -> Optional[str]: ...
|
||||
def adapt_datetimefield_value(self, value: None) -> None: ...
|
||||
def adapt_timefield_value(self, value: Optional[datetime]) -> Optional[str]: ...
|
||||
def adapt_datetimefield_value(self, value: Optional[datetime]) -> Optional[str]: ...
|
||||
def adapt_timefield_value(self, value: Optional[Union[datetime, time]]) -> Optional[str]: ...
|
||||
def adapt_decimalfield_value(
|
||||
self, value: Optional[Decimal], max_digits: Optional[int] = ..., decimal_places: Optional[int] = ...
|
||||
) -> Optional[str]: ...
|
||||
@@ -87,19 +88,17 @@ class BaseDatabaseOperations:
|
||||
def year_lookup_bounds_for_datetime_field(self, value: int) -> List[str]: ...
|
||||
def get_db_converters(self, expression: Expression) -> List[Any]: ...
|
||||
def convert_durationfield_value(
|
||||
self, value: Optional[float], expression: Expression, connection: DatabaseWrapper
|
||||
self, value: Optional[float], expression: Expression, connection: _Connection
|
||||
) -> Optional[timedelta]: ...
|
||||
def check_expression_support(self, expression: Any) -> None: ...
|
||||
def combine_expression(self, connector: str, sub_expressions: List[str]) -> str: ...
|
||||
def combine_duration_expression(self, connector: Any, sub_expressions: Any): ...
|
||||
def binary_placeholder_sql(self, value: Optional[Case]) -> str: ...
|
||||
def modify_insert_params(
|
||||
self, placeholder: str, params: Union[List[None], List[bool], List[float], List[str]]
|
||||
) -> Union[List[None], List[bool], List[float], List[str]]: ...
|
||||
def modify_insert_params(self, placeholder: str, params: Any) -> Any: ...
|
||||
def integer_field_range(self, internal_type: Any): ...
|
||||
def subtract_temporals(self, internal_type: Any, lhs: Any, rhs: Any): ...
|
||||
def window_frame_start(self, start: Any): ...
|
||||
def window_frame_end(self, end: Any): ...
|
||||
def window_frame_rows_start_end(self, start: None = ..., end: None = ...) -> Any: ...
|
||||
def window_frame_range_start_end(self, start: Optional[Any] = ..., end: Optional[Any] = ...): ...
|
||||
def window_frame_rows_start_end(self, start: Optional[int] = ..., end: Optional[int] = ...) -> Any: ...
|
||||
def window_frame_range_start_end(self, start: Optional[int] = ..., end: Optional[int] = ...) -> Any: ...
|
||||
def explain_query_prefix(self, format: Optional[str] = ..., **options: Any) -> str: ...
|
||||
|
||||
@@ -48,7 +48,7 @@ class BaseDatabaseSchemaEditor(ContextManager[Any]):
|
||||
def quote_name(self, name: str) -> str: ...
|
||||
def column_sql(
|
||||
self, model: Type[Model], field: Field, include_default: bool = ...
|
||||
) -> Union[Tuple[None, None], Tuple[str, List[Any]]]: ...
|
||||
) -> Tuple[Optional[str], Optional[List[Any]]]: ...
|
||||
def skip_default(self, field: Any): ...
|
||||
def prepare_default(self, value: Any) -> None: ...
|
||||
def effective_default(self, field: Field) -> Optional[Union[int, str]]: ...
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import Any, Dict, Optional, Tuple, Union
|
||||
from typing import Any, Optional
|
||||
|
||||
from django.db.backends.base.introspection import BaseDatabaseIntrospection
|
||||
|
||||
@@ -8,6 +8,6 @@ def get_field_size(name: str) -> Optional[int]: ...
|
||||
|
||||
class FlexibleFieldLookupDict:
|
||||
base_data_types_reverse: Any = ...
|
||||
def __getitem__(self, key: str) -> Union[Tuple[str, Dict[str, int]], str]: ...
|
||||
def __getitem__(self, key: str) -> Any: ...
|
||||
|
||||
class DatabaseIntrospection(BaseDatabaseIntrospection): ...
|
||||
|
||||
@@ -40,6 +40,7 @@ from .fields import (
|
||||
DurationField as DurationField,
|
||||
BigAutoField as BigAutoField,
|
||||
CommaSeparatedIntegerField as CommaSeparatedIntegerField,
|
||||
NOT_PROVIDED as NOT_PROVIDED,
|
||||
)
|
||||
|
||||
from .fields.related import (
|
||||
@@ -128,3 +129,5 @@ from .constraints import (
|
||||
CheckConstraint as CheckConstraint,
|
||||
UniqueConstraint as UniqueConstraint,
|
||||
)
|
||||
|
||||
from .enums import Choices as Choices, IntegerChoices as IntegerChoices, TextChoices as TextChoices
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from typing import Any, Dict, List, Optional, Sequence, Set, Tuple, TypeVar, Union
|
||||
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
|
||||
from django.db.models.manager import Manager
|
||||
from django.db.models.options import Options
|
||||
|
||||
@@ -18,10 +19,13 @@ class Model(metaclass=ModelBase):
|
||||
pk: Any = ...
|
||||
def __init__(self: _Self, *args, **kwargs) -> None: ...
|
||||
def delete(self, using: Any = ..., keep_parents: bool = ...) -> Tuple[int, Dict[str, int]]: ...
|
||||
def full_clean(self, exclude: Optional[List[str]] = ..., validate_unique: bool = ...) -> None: ...
|
||||
def full_clean(self, exclude: Optional[Collection[str]] = ..., validate_unique: bool = ...) -> None: ...
|
||||
def clean(self) -> None: ...
|
||||
def clean_fields(self, exclude: List[str] = ...) -> None: ...
|
||||
def validate_unique(self, exclude: List[str] = ...) -> None: ...
|
||||
def clean_fields(self, exclude: Optional[Collection[str]] = ...) -> None: ...
|
||||
def validate_unique(self, exclude: Optional[Collection[str]] = ...) -> None: ...
|
||||
def unique_error_message(
|
||||
self, model_class: Type[_Self], unique_check: Collection[Union[Callable, str]]
|
||||
) -> ValidationError: ...
|
||||
def save(
|
||||
self,
|
||||
force_insert: bool = ...,
|
||||
|
||||
30
django-stubs/db/models/enums.pyi
Normal file
30
django-stubs/db/models/enums.pyi
Normal file
@@ -0,0 +1,30 @@
|
||||
import enum
|
||||
from typing import Any, List, Tuple
|
||||
|
||||
class ChoicesMeta(enum.EnumMeta):
|
||||
names: List[str] = ...
|
||||
choices: List[Tuple[Any, str]] = ...
|
||||
labels: List[str] = ...
|
||||
values: List[Any] = ...
|
||||
def __contains__(self, item: Any) -> bool: ...
|
||||
|
||||
class Choices(enum.Enum, metaclass=ChoicesMeta):
|
||||
def __str__(self): ...
|
||||
|
||||
# fake
|
||||
class _IntegerChoicesMeta(ChoicesMeta):
|
||||
names: List[str] = ...
|
||||
choices: List[Tuple[int, str]] = ...
|
||||
labels: List[str] = ...
|
||||
values: List[int] = ...
|
||||
|
||||
class IntegerChoices(int, Choices, metaclass=_IntegerChoicesMeta): ...
|
||||
|
||||
# fake
|
||||
class _TextChoicesMeta(ChoicesMeta):
|
||||
names: List[str] = ...
|
||||
choices: List[Tuple[str, str]] = ...
|
||||
labels: List[str] = ...
|
||||
values: List[str] = ...
|
||||
|
||||
class TextChoices(str, Choices, metaclass=_TextChoicesMeta): ...
|
||||
@@ -25,7 +25,7 @@ from django.db.models.expressions import Combinable, Col
|
||||
from django.db.models.query_utils import RegisterLookupMixin
|
||||
from django.forms import Field as FormField, Widget
|
||||
|
||||
from .mixins import NOT_PROVIDED as NOT_PROVIDED
|
||||
class NOT_PROVIDED: ...
|
||||
|
||||
_Choice = Tuple[Any, Any]
|
||||
_ChoiceNamedGroup = Tuple[str, Iterable[_Choice]]
|
||||
@@ -52,6 +52,8 @@ class Field(RegisterLookupMixin, Generic[_ST, _GT]):
|
||||
auto_created: bool
|
||||
primary_key: bool
|
||||
remote_field: Field
|
||||
is_relation: bool
|
||||
related_model: Optional[Type[Model]]
|
||||
max_length: int
|
||||
model: Type[Model]
|
||||
name: str
|
||||
@@ -64,6 +66,7 @@ class Field(RegisterLookupMixin, Generic[_ST, _GT]):
|
||||
choices: Optional[_FieldChoices] = ...
|
||||
db_column: Optional[str]
|
||||
column: str
|
||||
default: Any
|
||||
error_messages: _ErrorMessagesToOverride
|
||||
def __init__(
|
||||
self,
|
||||
@@ -105,7 +108,8 @@ class Field(RegisterLookupMixin, Generic[_ST, _GT]):
|
||||
def db_parameters(self, connection: Any) -> Dict[str, str]: ...
|
||||
def get_prep_value(self, value: Any) -> Any: ...
|
||||
def get_internal_type(self) -> str: ...
|
||||
def formfield(self, **kwargs) -> FormField: ...
|
||||
# TODO: plugin support
|
||||
def formfield(self, **kwargs) -> Any: ...
|
||||
def save_form_data(self, instance: Model, data: Any) -> None: ...
|
||||
def contribute_to_class(self, cls: Type[Model], name: str, private_only: bool = ...) -> None: ...
|
||||
def to_python(self, value: Any) -> Any: ...
|
||||
@@ -128,11 +132,12 @@ class Field(RegisterLookupMixin, Generic[_ST, _GT]):
|
||||
@property
|
||||
def cached_col(self) -> Col: ...
|
||||
def value_from_object(self, obj: Model) -> _GT: ...
|
||||
def get_attname(self) -> str: ...
|
||||
|
||||
class IntegerField(Field[_ST, _GT]):
|
||||
_pyi_private_set_type: Union[float, int, str, Combinable]
|
||||
_pyi_private_get_type: int
|
||||
_pyi_lookup_exact_type: int
|
||||
_pyi_lookup_exact_type: Union[str, int]
|
||||
|
||||
class PositiveIntegerRelDbTypeMixin:
|
||||
def rel_db_type(self, connection: Any): ...
|
||||
@@ -180,7 +185,7 @@ class DecimalField(Field[_ST, _GT]):
|
||||
class AutoField(Field[_ST, _GT]):
|
||||
_pyi_private_set_type: Union[Combinable, int, str]
|
||||
_pyi_private_get_type: int
|
||||
_pyi_lookup_exact_type: int
|
||||
_pyi_lookup_exact_type: Union[str, int]
|
||||
|
||||
class CharField(Field[_ST, _GT]):
|
||||
_pyi_private_set_type: Union[str, int, Combinable]
|
||||
@@ -357,20 +362,20 @@ class UUIDField(Field[_ST, _GT]):
|
||||
_pyi_private_get_type: uuid.UUID
|
||||
|
||||
class FilePathField(Field[_ST, _GT]):
|
||||
path: str = ...
|
||||
match: Optional[Any] = ...
|
||||
path: Any = ...
|
||||
match: Optional[str] = ...
|
||||
recursive: bool = ...
|
||||
allow_files: bool = ...
|
||||
allow_folders: bool = ...
|
||||
def __init__(
|
||||
self,
|
||||
verbose_name: Optional[Union[str, bytes]] = ...,
|
||||
name: Optional[str] = ...,
|
||||
path: str = ...,
|
||||
match: Optional[Any] = ...,
|
||||
path: Union[str, Callable[..., str]] = ...,
|
||||
match: Optional[str] = ...,
|
||||
recursive: bool = ...,
|
||||
allow_files: bool = ...,
|
||||
allow_folders: bool = ...,
|
||||
verbose_name: Optional[str] = ...,
|
||||
name: Optional[str] = ...,
|
||||
primary_key: bool = ...,
|
||||
max_length: int = ...,
|
||||
unique: bool = ...,
|
||||
@@ -389,7 +394,8 @@ class FilePathField(Field[_ST, _GT]):
|
||||
error_messages: Optional[_ErrorMessagesToOverride] = ...,
|
||||
): ...
|
||||
|
||||
class BinaryField(Field[_ST, _GT]): ...
|
||||
class BinaryField(Field[_ST, _GT]):
|
||||
_pyi_private_get_type: bytes
|
||||
|
||||
class DurationField(Field[_ST, _GT]):
|
||||
_pyi_private_get_type: timedelta
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from pathlib import Path
|
||||
from typing import Any, Callable, Iterable, List, Optional, Tuple, Type, TypeVar, Union, overload
|
||||
|
||||
from django.core.files.base import File
|
||||
@@ -39,11 +40,10 @@ class FileField(Field):
|
||||
upload_to: Union[str, Callable] = ...
|
||||
def __init__(
|
||||
self,
|
||||
upload_to: Union[str, Callable, Path] = ...,
|
||||
storage: Optional[Storage] = ...,
|
||||
verbose_name: Optional[Union[str, bytes]] = ...,
|
||||
name: Optional[str] = ...,
|
||||
upload_to: Union[str, Callable] = ...,
|
||||
storage: Optional[Storage] = ...,
|
||||
primary_key: bool = ...,
|
||||
max_length: Optional[int] = ...,
|
||||
unique: bool = ...,
|
||||
blank: bool = ...,
|
||||
|
||||
@@ -43,9 +43,8 @@ class RelatedField(FieldCacheMixin, Field[_ST, _GT]):
|
||||
one_to_one: bool = ...
|
||||
many_to_many: bool = ...
|
||||
many_to_one: bool = ...
|
||||
related_model: Type[Model]
|
||||
opts: Any = ...
|
||||
@property
|
||||
def related_model(self) -> Type[Model]: ...
|
||||
def get_forward_related_filter(self, obj: Model) -> Dict[str, Union[int, UUID]]: ...
|
||||
def get_reverse_related_filter(self, obj: Model) -> Q: ...
|
||||
@property
|
||||
|
||||
@@ -18,6 +18,7 @@ class ForeignObjectRel(FieldCacheMixin):
|
||||
concrete: bool = ...
|
||||
editable: bool = ...
|
||||
is_relation: bool = ...
|
||||
related_model: Type[Model]
|
||||
null: bool = ...
|
||||
field: RelatedField = ...
|
||||
model: Union[Type[Model], str] = ...
|
||||
@@ -44,8 +45,6 @@ class ForeignObjectRel(FieldCacheMixin):
|
||||
@property
|
||||
def name(self) -> str: ...
|
||||
@property
|
||||
def related_model(self) -> Type[Model]: ...
|
||||
@property
|
||||
def remote_field(self) -> RelatedField: ...
|
||||
@property
|
||||
def target_field(self) -> AutoField: ...
|
||||
|
||||
@@ -31,11 +31,9 @@ class Lookup(Generic[_T]):
|
||||
def process_lhs(
|
||||
self, compiler: SQLCompiler, connection: DatabaseWrapper, lhs: Optional[Expression] = ...
|
||||
) -> Tuple[str, List[Union[int, str]]]: ...
|
||||
def process_rhs(
|
||||
self, compiler: SQLCompiler, connection: DatabaseWrapper
|
||||
) -> Tuple[str, Union[List[Union[int, str]], Tuple[int, int]]]: ...
|
||||
def process_rhs(self, compiler: SQLCompiler, connection: DatabaseWrapper) -> Tuple[str, List[Union[int, str]]]: ...
|
||||
def rhs_is_direct_value(self) -> bool: ...
|
||||
def relabeled_clone(self, relabels: Mapping[str, str]) -> Union[BuiltinLookup, FieldGetDbPrepValueMixin]: ...
|
||||
def relabeled_clone(self: _T, relabels: Mapping[str, str]) -> _T: ...
|
||||
def get_group_by_cols(self) -> List[Expression]: ...
|
||||
def as_sql(self, compiler: Any, connection: Any) -> Any: ...
|
||||
def contains_aggregate(self) -> bool: ...
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
from typing import Any, Dict, List, Optional, Tuple, Type, TypeVar
|
||||
from typing import Any, Dict, List, Optional, Tuple, Type, TypeVar, Iterable, Union
|
||||
|
||||
from django.db.models.base import Model
|
||||
from django.db.models.query import QuerySet
|
||||
|
||||
_T = TypeVar("_T", bound=Model, covariant=True)
|
||||
_M = TypeVar("_M", bound="BaseManager")
|
||||
|
||||
class BaseManager(QuerySet[_T]):
|
||||
creation_counter: int = ...
|
||||
auto_created: bool = ...
|
||||
use_in_migrations: bool = ...
|
||||
name: str = ...
|
||||
model: Type[Model] = ...
|
||||
model: Type[_T] = ...
|
||||
db: str
|
||||
def __init__(self) -> None: ...
|
||||
def deconstruct(self) -> Tuple[bool, str, None, Tuple, Dict[str, int]]: ...
|
||||
@@ -20,13 +21,18 @@ class BaseManager(QuerySet[_T]):
|
||||
@classmethod
|
||||
def _get_queryset_methods(cls, queryset_class: type) -> Dict[str, Any]: ...
|
||||
def contribute_to_class(self, model: Type[Model], name: str) -> None: ...
|
||||
def db_manager(self, using: Optional[str] = ..., hints: Optional[Dict[str, Model]] = ...) -> Manager: ...
|
||||
def db_manager(self: _M, using: Optional[str] = ..., hints: Optional[Dict[str, Model]] = ...) -> _M: ...
|
||||
def get_queryset(self) -> QuerySet[_T]: ...
|
||||
|
||||
class Manager(BaseManager[_T]): ...
|
||||
|
||||
class RelatedManager(Manager[_T]):
|
||||
def add(self, *objs: Model, bulk: bool = ...) -> None: ...
|
||||
related_val: Tuple[int, ...]
|
||||
def add(self, *objs: Union[_T, int], bulk: bool = ...) -> None: ...
|
||||
def remove(self, *objs: Union[_T, int], bulk: bool = ...) -> None: ...
|
||||
def set(
|
||||
self, objs: Union[QuerySet[_T], Iterable[Union[_T, int]]], *, bulk: bool = ..., clear: bool = ...
|
||||
) -> None: ...
|
||||
def clear(self) -> None: ...
|
||||
|
||||
class ManagerDescriptor:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import collections
|
||||
from typing import Any, Callable, Dict, Generic, Iterator, List, Optional, Sequence, Set, Tuple, Type, TypeVar, Union
|
||||
from typing import Any, Callable, Dict, Generic, Iterator, List, Optional, Sequence, Tuple, Type, TypeVar, Union
|
||||
|
||||
from django.apps.config import AppConfig
|
||||
from django.apps.registry import Apps
|
||||
@@ -23,8 +23,8 @@ IMMUTABLE_WARNING: str
|
||||
DEFAULT_NAMES: Tuple[str, ...]
|
||||
|
||||
def normalize_together(
|
||||
option_together: Any
|
||||
) -> Union[List[Union[Tuple[str, str], int]], Set[Tuple[str, str]], Tuple, int, str]: ...
|
||||
option_together: Union[Sequence[Tuple[str, str]], Tuple[str, str]]
|
||||
) -> Tuple[Tuple[str, str], ...]: ...
|
||||
def make_immutable_fields_list(
|
||||
name: str, data: Union[Iterator[Any], List[Union[ArrayField, CIText]], List[Union[Field, FieldCacheMixin]]]
|
||||
) -> ImmutableList: ...
|
||||
@@ -108,6 +108,8 @@ class Options(Generic[_M]):
|
||||
def managers(self) -> List[Manager]: ...
|
||||
@property
|
||||
def managers_map(self) -> Dict[str, Manager]: ...
|
||||
@property
|
||||
def db_returning_fields(self) -> List[Field]: ...
|
||||
def get_field(self, field_name: Union[Callable, str]) -> Field: ...
|
||||
def get_base_chain(self, model: Type[Model]) -> List[Type[Model]]: ...
|
||||
def get_parent_list(self) -> List[Type[Model]]: ...
|
||||
|
||||
@@ -52,8 +52,9 @@ class _BaseQuerySet(Generic[_T], Sized):
|
||||
def get(self, *args: Any, **kwargs: Any) -> _T: ...
|
||||
def create(self, *args: Any, **kwargs: Any) -> _T: ...
|
||||
def bulk_create(
|
||||
self, objs: Iterable[Model], batch_size: Optional[int] = ..., ignore_conflicts: bool = ...
|
||||
self, objs: Iterable[_T], batch_size: Optional[int] = ..., ignore_conflicts: bool = ...
|
||||
) -> List[_T]: ...
|
||||
def bulk_update(self, objs: Iterable[_T], fields: Sequence[str], batch_size: Optional[int] = ...) -> None: ...
|
||||
def get_or_create(self, defaults: Optional[MutableMapping[str, Any]] = ..., **kwargs: Any) -> Tuple[_T, bool]: ...
|
||||
def update_or_create(
|
||||
self, defaults: Optional[MutableMapping[str, Any]] = ..., **kwargs: Any
|
||||
@@ -119,8 +120,6 @@ class _BaseQuerySet(Generic[_T], Sized):
|
||||
@property
|
||||
def db(self) -> str: ...
|
||||
def resolve_expression(self, *args: Any, **kwargs: Any) -> Any: ...
|
||||
# TODO: remove when django adds __class_getitem__ methods
|
||||
def __getattr__(self, item: str) -> Any: ...
|
||||
|
||||
class QuerySet(_BaseQuerySet[_T], Collection[_T], Sized):
|
||||
def __iter__(self) -> Iterator[_T]: ...
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
from collections import OrderedDict, namedtuple
|
||||
from typing import Any, Dict, Iterator, List, Optional, Set, Tuple, Type, Union
|
||||
from collections import namedtuple
|
||||
from typing import Any, Collection, Dict, Iterator, List, Mapping, Optional, Sequence, Set, Tuple, Type
|
||||
|
||||
from django.db.models.base import Model
|
||||
from django.db.models.expressions import Expression
|
||||
from django.db.models.fields import Field
|
||||
from django.db.models.fields.mixins import FieldCacheMixin
|
||||
from django.db.models.sql.compiler import SQLCompiler
|
||||
from django.db.models.sql.query import Query
|
||||
from django.db.models.sql.where import WhereNode
|
||||
|
||||
from django.db.models.fields import Field
|
||||
from django.utils import tree
|
||||
|
||||
PathInfo = namedtuple("PathInfo", "from_opts to_opts target_fields join_field m2m direct filtered_relation")
|
||||
@@ -23,12 +23,8 @@ class QueryWrapper:
|
||||
def as_sql(self, compiler: SQLCompiler = ..., connection: Any = ...) -> Any: ...
|
||||
|
||||
class Q(tree.Node):
|
||||
children: Union[List[Dict[str, str]], List[Tuple[str, Any]], List[Q]]
|
||||
connector: str
|
||||
negated: bool
|
||||
AND: str = ...
|
||||
OR: str = ...
|
||||
default: Any = ...
|
||||
conditional: bool = ...
|
||||
def __init__(self, *args: Any, **kwargs: Any) -> None: ...
|
||||
def __or__(self, other: Any) -> Q: ...
|
||||
@@ -46,8 +42,8 @@ class Q(tree.Node):
|
||||
|
||||
class DeferredAttribute:
|
||||
field_name: str = ...
|
||||
field: Field
|
||||
def __init__(self, field_name: str) -> None: ...
|
||||
def __get__(self, instance: Optional[Model], cls: Type[Model] = ...) -> Any: ...
|
||||
|
||||
class RegisterLookupMixin:
|
||||
lookup_name: str
|
||||
@@ -65,15 +61,11 @@ class RegisterLookupMixin:
|
||||
def select_related_descend(
|
||||
field: Field,
|
||||
restricted: bool,
|
||||
requested: Optional[
|
||||
Union[Dict[str, Dict[str, Dict[str, Dict[str, Dict[str, Dict[str, Dict[str, Dict[Any, Any]]]]]]]], bool]
|
||||
],
|
||||
load_fields: Optional[Set[str]],
|
||||
requested: Optional[Mapping[str, Any]],
|
||||
load_fields: Optional[Collection[str]],
|
||||
reverse: bool = ...,
|
||||
) -> bool: ...
|
||||
def refs_expression(
|
||||
lookup_parts: List[str], annotations: OrderedDict
|
||||
) -> Union[Tuple[bool, Tuple], Tuple[Expression, List[str]]]: ...
|
||||
def refs_expression(lookup_parts: Sequence[str], annotations: Mapping[str, bool]) -> Tuple[bool, Sequence[str]]: ...
|
||||
def check_rel_lookup_compatibility(model: Type[Model], target_opts: Any, field: FieldCacheMixin) -> bool: ...
|
||||
|
||||
class FilteredRelation:
|
||||
|
||||
@@ -25,7 +25,7 @@ class SQLCompiler:
|
||||
def setup_query(self) -> None: ...
|
||||
has_extra_select: Any = ...
|
||||
def pre_sql_setup(
|
||||
self
|
||||
self,
|
||||
) -> Tuple[
|
||||
List[Tuple[Expression, Tuple[str, Union[List[Any], Tuple[str, str]]], None]],
|
||||
List[Tuple[Expression, Tuple[str, List[Union[int, str]], bool]]],
|
||||
@@ -40,7 +40,7 @@ class SQLCompiler:
|
||||
self, expressions: List[Expression], having: Union[List[Expression], Tuple]
|
||||
) -> List[Expression]: ...
|
||||
def get_select(
|
||||
self
|
||||
self,
|
||||
) -> Tuple[
|
||||
List[Tuple[Expression, Tuple[str, List[Union[int, str]]], Optional[str]]],
|
||||
Optional[Dict[str, Any]],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import collections
|
||||
from collections import OrderedDict, namedtuple
|
||||
from typing import Any, Callable, Dict, Iterator, List, Optional, Sequence, Set, Tuple, Type, Union
|
||||
from typing import Any, Callable, Dict, Iterator, List, Optional, Sequence, Set, Tuple, Type, Union, Iterable
|
||||
|
||||
from django.db.models.lookups import Lookup, Transform
|
||||
from django.db.models.query_utils import PathInfo, RegisterLookupMixin
|
||||
@@ -155,19 +155,19 @@ class Query:
|
||||
def add_ordering(self, *ordering: Any) -> None: ...
|
||||
def clear_ordering(self, force_empty: bool) -> None: ...
|
||||
def set_group_by(self) -> None: ...
|
||||
def add_select_related(self, fields: Tuple[str]) -> None: ...
|
||||
def add_select_related(self, fields: Iterable[str]) -> None: ...
|
||||
def add_extra(
|
||||
self,
|
||||
select: Optional[Union[Dict[str, int], Dict[str, str], OrderedDict]],
|
||||
select_params: Optional[Union[List[int], List[str], Tuple[int]]],
|
||||
where: Optional[List[str]],
|
||||
params: Optional[List[str]],
|
||||
tables: Optional[List[str]],
|
||||
order_by: Optional[Union[List[str], Tuple[str]]],
|
||||
select: Optional[Dict[str, Any]],
|
||||
select_params: Optional[Iterable[Any]],
|
||||
where: Optional[Sequence[str]],
|
||||
params: Optional[Sequence[str]],
|
||||
tables: Optional[Sequence[str]],
|
||||
order_by: Optional[Sequence[str]],
|
||||
) -> None: ...
|
||||
def clear_deferred_loading(self) -> None: ...
|
||||
def add_deferred_loading(self, field_names: Tuple[str]) -> None: ...
|
||||
def add_immediate_loading(self, field_names: Tuple[str]) -> None: ...
|
||||
def add_deferred_loading(self, field_names: Iterable[str]) -> None: ...
|
||||
def add_immediate_loading(self, field_names: Iterable[str]) -> None: ...
|
||||
def get_loaded_field_names(self) -> Dict[Type[Model], Set[str]]: ...
|
||||
def get_loaded_field_names_cb(
|
||||
self, target: Dict[Type[Model], Set[str]], model: Type[Model], fields: Set[Field]
|
||||
|
||||
@@ -2,6 +2,8 @@ from django.core.exceptions import ValidationError as ValidationError
|
||||
|
||||
from .forms import Form as Form, BaseForm as BaseForm
|
||||
|
||||
from .formsets import BaseFormSet as BaseFormSet, all_valid as all_valid, formset_factory as formset_factory
|
||||
|
||||
from .models import (
|
||||
ModelForm as ModelForm,
|
||||
ModelChoiceField as ModelChoiceField,
|
||||
|
||||
@@ -68,4 +68,6 @@ class BaseForm:
|
||||
def visible_fields(self): ...
|
||||
def get_initial_for_field(self, field: Field, field_name: str) -> Any: ...
|
||||
|
||||
class Form(BaseForm): ...
|
||||
class Form(BaseForm):
|
||||
base_fields: Dict[str, Field]
|
||||
declared_fields: Dict[str, Field]
|
||||
|
||||
@@ -63,7 +63,7 @@ class HttpRequest(BytesIO):
|
||||
self, key: str, default: Any = ..., salt: str = ..., max_age: Optional[int] = ...
|
||||
) -> Optional[str]: ...
|
||||
def get_raw_uri(self) -> str: ...
|
||||
def build_absolute_uri(self, location: str = ...) -> str: ...
|
||||
def build_absolute_uri(self, location: Optional[str] = ...) -> str: ...
|
||||
@property
|
||||
def scheme(self) -> Optional[str]: ...
|
||||
def is_secure(self) -> bool: ...
|
||||
|
||||
@@ -63,7 +63,7 @@ class HttpResponseBase(Iterable[Any]):
|
||||
|
||||
class HttpResponse(HttpResponseBase):
|
||||
client: Client
|
||||
context: Optional[Context]
|
||||
context: Context
|
||||
csrf_cookie_set: bool
|
||||
redirect_chain: List[Tuple[str, int]]
|
||||
request: Dict[str, Any]
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
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 HttpResponse as HttpResponse, HttpResponseRedirect as HttpResponseRedirect
|
||||
from django.http.response import (
|
||||
HttpResponse as HttpResponse,
|
||||
HttpResponseRedirect as HttpResponseRedirect,
|
||||
HttpResponsePermanentRedirect as HttpResponsePermanentRedirect,
|
||||
)
|
||||
|
||||
from django.db.models import Manager, QuerySet
|
||||
from django.http import HttpRequest
|
||||
@@ -26,7 +30,7 @@ class SupportsGetAbsoluteUrl(Protocol): ...
|
||||
|
||||
def redirect(
|
||||
to: Union[Callable, str, SupportsGetAbsoluteUrl], *args: Any, permanent: bool = ..., **kwargs: Any
|
||||
) -> HttpResponseRedirect: ...
|
||||
) -> Union[HttpResponseRedirect, HttpResponsePermanentRedirect]: ...
|
||||
|
||||
_T = TypeVar("_T", bound=Model)
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from io import BytesIO
|
||||
from typing import Any, Dict, List, Optional, Pattern, Tuple, Type, Union
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
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
|
||||
@@ -9,6 +9,8 @@ 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
|
||||
|
||||
BOUNDARY: str = ...
|
||||
MULTIPART_CONTENT: str = ...
|
||||
CONTENT_TYPE_RE: Pattern = ...
|
||||
@@ -40,13 +42,13 @@ class RequestFactory:
|
||||
cookies: SimpleCookie = ...
|
||||
errors: BytesIO = ...
|
||||
def __init__(self, *, json_encoder: Any = ..., **defaults: Any) -> None: ...
|
||||
def request(self, **request: Any) -> HttpRequest: ...
|
||||
def get(self, path: str, data: Any = ..., secure: bool = ..., **extra: Any) -> HttpRequest: ...
|
||||
def request(self, **request: Any) -> WSGIRequest: ...
|
||||
def get(self, path: str, data: Any = ..., secure: bool = ..., **extra: Any) -> WSGIRequest: ...
|
||||
def post(
|
||||
self, path: str, data: Any = ..., content_type: str = ..., secure: bool = ..., **extra: Any
|
||||
) -> HttpRequest: ...
|
||||
def head(self, path: str, data: Any = ..., secure: bool = ..., **extra: Any) -> HttpRequest: ...
|
||||
def trace(self, path: str, secure: bool = ..., **extra: Any) -> HttpRequest: ...
|
||||
) -> WSGIRequest: ...
|
||||
def head(self, path: str, data: Any = ..., secure: bool = ..., **extra: Any) -> WSGIRequest: ...
|
||||
def trace(self, path: str, secure: bool = ..., **extra: Any) -> WSGIRequest: ...
|
||||
def options(
|
||||
self,
|
||||
path: str,
|
||||
@@ -54,16 +56,16 @@ class RequestFactory:
|
||||
content_type: str = ...,
|
||||
secure: bool = ...,
|
||||
**extra: Any
|
||||
) -> HttpRequest: ...
|
||||
) -> WSGIRequest: ...
|
||||
def put(
|
||||
self, path: str, data: Any = ..., content_type: str = ..., secure: bool = ..., **extra: Any
|
||||
) -> HttpRequest: ...
|
||||
) -> WSGIRequest: ...
|
||||
def patch(
|
||||
self, path: str, data: Any = ..., content_type: str = ..., secure: bool = ..., **extra: Any
|
||||
) -> HttpRequest: ...
|
||||
) -> WSGIRequest: ...
|
||||
def delete(
|
||||
self, path: str, data: Any = ..., content_type: str = ..., secure: bool = ..., **extra: Any
|
||||
) -> HttpRequest: ...
|
||||
) -> WSGIRequest: ...
|
||||
def generic(
|
||||
self,
|
||||
method: str,
|
||||
@@ -72,7 +74,7 @@ class RequestFactory:
|
||||
content_type: Optional[str] = ...,
|
||||
secure: bool = ...,
|
||||
**extra: Any
|
||||
) -> HttpRequest: ...
|
||||
) -> WSGIRequest: ...
|
||||
|
||||
class Client:
|
||||
json_encoder: Type[DjangoJSONEncoder] = ...
|
||||
@@ -82,13 +84,11 @@ class Client:
|
||||
handler: ClientHandler = ...
|
||||
exc_info: None = ...
|
||||
def __init__(self, enforce_csrf_checks: bool = ..., **defaults: Any) -> None: ...
|
||||
def request(self, **request: Any) -> HttpResponse: ...
|
||||
def get(self, path: str, data: Any = ..., secure: bool = ..., **extra: Any) -> HttpResponse: ...
|
||||
def post(
|
||||
self, path: str, data: Any = ..., content_type: str = ..., secure: bool = ..., **extra: Any
|
||||
) -> HttpResponse: ...
|
||||
def head(self, path: str, data: Any = ..., secure: bool = ..., **extra: Any) -> HttpResponse: ...
|
||||
def trace(self, path: str, secure: bool = ..., **extra: Any) -> HttpResponse: ...
|
||||
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,
|
||||
@@ -96,16 +96,10 @@ class Client:
|
||||
content_type: str = ...,
|
||||
secure: bool = ...,
|
||||
**extra: Any
|
||||
) -> HttpResponse: ...
|
||||
def put(
|
||||
self, path: str, data: Any = ..., content_type: str = ..., secure: bool = ..., **extra: Any
|
||||
) -> HttpResponse: ...
|
||||
def patch(
|
||||
self, path: str, data: Any = ..., content_type: str = ..., secure: bool = ..., **extra: Any
|
||||
) -> HttpResponse: ...
|
||||
def delete(
|
||||
self, path: str, data: Any = ..., content_type: str = ..., secure: bool = ..., **extra: Any
|
||||
) -> HttpResponse: ...
|
||||
) -> 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,
|
||||
@@ -114,12 +108,12 @@ class Client:
|
||||
content_type: Optional[str] = ...,
|
||||
secure: bool = ...,
|
||||
**extra: Any
|
||||
) -> HttpResponse: ...
|
||||
) -> Any: ...
|
||||
def store_exc_info(self, **kwargs: Any) -> None: ...
|
||||
@property
|
||||
def session(self) -> SessionBase: ...
|
||||
def login(self, **credentials: Any) -> bool: ...
|
||||
def force_login(self, user: User, backend: Optional[str] = ...) -> None: ...
|
||||
def force_login(self, user: AbstractUser, backend: Optional[str] = ...) -> None: ...
|
||||
def logout(self) -> None: ...
|
||||
|
||||
def conditional_content_removal(request: HttpRequest, response: HttpResponseBase) -> HttpResponse: ...
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import threading
|
||||
import unittest
|
||||
from datetime import date
|
||||
from typing import Any, Callable, Dict, Iterator, List, Optional, Set, Tuple, Type, Union
|
||||
from typing import Any, Callable, Dict, Iterator, List, Optional, Set, Tuple, Type, Union, ClassVar
|
||||
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.core.handlers.wsgi import WSGIHandler
|
||||
@@ -47,7 +47,8 @@ class SimpleTestCase(unittest.TestCase):
|
||||
client_class: Any = ...
|
||||
client: Client
|
||||
allow_database_queries: bool = ...
|
||||
databases: Set[str] = ...
|
||||
# TODO: str -> Literal['__all__']
|
||||
databases: Union[Set[str], str] = ...
|
||||
def __call__(self, result: Optional[unittest.TestResult] = ...) -> None: ...
|
||||
def settings(self, **kwargs: Any) -> Any: ...
|
||||
def modify_settings(self, **kwargs: Any) -> Any: ...
|
||||
@@ -198,13 +199,12 @@ class LiveServerThread(threading.Thread):
|
||||
def terminate(self) -> None: ...
|
||||
|
||||
class LiveServerTestCase(TransactionTestCase):
|
||||
live_server_url: ClassVar[str]
|
||||
host: str = ...
|
||||
port: int = ...
|
||||
server_thread_class: Type[Any] = ...
|
||||
server_thread: Any
|
||||
static_handler: Any = ...
|
||||
@classmethod
|
||||
def live_server_url(cls): ...
|
||||
|
||||
class SerializeMixin:
|
||||
lockfile: Any = ...
|
||||
|
||||
@@ -1,9 +1,22 @@
|
||||
import decimal
|
||||
import warnings
|
||||
from contextlib import contextmanager
|
||||
from decimal import Decimal
|
||||
from io import StringIO
|
||||
from typing import Any, Callable, Dict, Iterable, Iterator, List, Mapping, Optional, Set, Tuple, Type, Union
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
Dict,
|
||||
Iterable,
|
||||
Iterator,
|
||||
List,
|
||||
Mapping,
|
||||
Optional,
|
||||
Set,
|
||||
Tuple,
|
||||
Type,
|
||||
Union,
|
||||
ContextManager,
|
||||
)
|
||||
|
||||
from django.apps.registry import Apps
|
||||
from django.core.checks.registry import CheckRegistry
|
||||
@@ -86,7 +99,7 @@ class ignore_warnings(TestContextDecorator):
|
||||
ignore_kwargs: Dict[str, Any] = ...
|
||||
filter_func: Callable = ...
|
||||
def __init__(self, **kwargs: Any) -> None: ...
|
||||
catch_warnings: warnings.catch_warnings = ...
|
||||
catch_warnings: ContextManager[Optional[list]] = ...
|
||||
|
||||
requires_tz_support: Any
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from os.path import abspath
|
||||
from pathlib import Path
|
||||
from typing import Any, Union
|
||||
|
||||
abspathu = abspath
|
||||
@@ -7,3 +8,4 @@ def upath(path: Any): ...
|
||||
def npath(path: Any): ...
|
||||
def safe_join(base: Union[bytes, str], *paths: Any) -> str: ...
|
||||
def symlinks_supported() -> Any: ...
|
||||
def to_path(value: Union[Path, str]) -> Path: ...
|
||||
|
||||
@@ -5,6 +5,7 @@ from django.http.response import HttpResponse
|
||||
|
||||
class RemovedInDjango30Warning(PendingDeprecationWarning): ...
|
||||
class RemovedInDjango31Warning(PendingDeprecationWarning): ...
|
||||
class RemovedInDjango40Warning(PendingDeprecationWarning): ...
|
||||
class RemovedInNextVersionWarning(DeprecationWarning): ...
|
||||
|
||||
class warn_about_renamed_method:
|
||||
|
||||
@@ -25,6 +25,9 @@ def urlsafe_base64_decode(s: Union[bytes, str]) -> bytes: ...
|
||||
def parse_etags(etag_str: str) -> List[str]: ...
|
||||
def quote_etag(etag_str: str) -> str: ...
|
||||
def is_same_domain(host: str, pattern: str) -> bool: ...
|
||||
def url_has_allowed_host_and_scheme(
|
||||
url: Optional[str], allowed_hosts: Optional[Union[str, Iterable[str]]], require_https: bool = ...
|
||||
) -> bool: ...
|
||||
def is_safe_url(
|
||||
url: Optional[str], allowed_hosts: Optional[Union[str, Iterable[str]]], require_https: bool = ...
|
||||
) -> bool: ...
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import types
|
||||
from contextlib import ContextDecorator
|
||||
from datetime import date, datetime as datetime, time, timedelta as timedelta, tzinfo as tzinfo
|
||||
from datetime import date, datetime as datetime, time, timedelta as timedelta, tzinfo as tzinfo, timezone
|
||||
from typing import Optional, Union, Type
|
||||
|
||||
from pytz import BaseTzInfo
|
||||
|
||||
_AnyTime = Union[time, datetime]
|
||||
|
||||
class UTC(tzinfo):
|
||||
@@ -30,10 +32,14 @@ class LocalTimezone(ReferenceLocalTimezone):
|
||||
|
||||
utc: UTC = ...
|
||||
|
||||
def get_fixed_timezone(offset: Union[timedelta, int]) -> tzinfo: ...
|
||||
def get_default_timezone() -> tzinfo: ...
|
||||
def get_fixed_timezone(offset: Union[timedelta, int]) -> timezone: ...
|
||||
def get_default_timezone() -> BaseTzInfo: ...
|
||||
def get_default_timezone_name() -> str: ...
|
||||
def get_current_timezone() -> tzinfo: ...
|
||||
|
||||
# Strictly speaking, it is possible to activate() a non-pytz timezone,
|
||||
# in which case BaseTzInfo is incorrect. However, this is unlikely,
|
||||
# so we use it anyway, to keep things ergonomic for most users.
|
||||
def get_current_timezone() -> BaseTzInfo: ...
|
||||
def get_current_timezone_name() -> str: ...
|
||||
def activate(timezone: Union[tzinfo, str]) -> None: ...
|
||||
def deactivate() -> None: ...
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
from typing import Any, Dict, Iterable, Optional, Tuple, Union, Sequence
|
||||
from typing import Any, Dict, Iterable, Optional, Tuple, Union, Sequence, List
|
||||
|
||||
from django.db.models.sql.where import NothingNode
|
||||
|
||||
_NodeChildren = Iterable[Union["Node", NothingNode, Sequence[Any]]]
|
||||
|
||||
class Node:
|
||||
default: str = ...
|
||||
children: List[Any]
|
||||
default: Any = ...
|
||||
connector: str = ...
|
||||
negated: bool = ...
|
||||
def __init__(
|
||||
self,
|
||||
children: Optional[Iterable[Union[Node, NothingNode, Sequence[Any]]]] = ...,
|
||||
connector: Optional[str] = ...,
|
||||
negated: bool = ...,
|
||||
self, children: Optional[_NodeChildren] = ..., connector: Optional[str] = ..., negated: bool = ...,
|
||||
) -> None: ...
|
||||
def __deepcopy__(self, memodict: Dict[Any, Any]) -> Node: ...
|
||||
def __len__(self) -> int: ...
|
||||
|
||||
@@ -63,4 +63,4 @@ class ExceptionReporter:
|
||||
): ...
|
||||
|
||||
def technical_404_response(request: HttpRequest, exception: Http404) -> HttpResponse: ...
|
||||
def default_urlconf(request: HttpRequest) -> HttpResponse: ...
|
||||
def default_urlconf(request: Optional[HttpResponse]) -> HttpResponse: ...
|
||||
|
||||
@@ -13,6 +13,7 @@ class View:
|
||||
def __init__(self, **kwargs: Any) -> None: ...
|
||||
@classmethod
|
||||
def as_view(cls: Any, **initkwargs: Any) -> Callable[..., http.HttpResponse]: ...
|
||||
def setup(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> None: ...
|
||||
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: ...
|
||||
|
||||
@@ -367,7 +367,7 @@ class DjangoContext:
|
||||
lookup_type: MypyType = lookup_base.args[0]
|
||||
# if it's Field, consider lookup_type a __get__ of current field
|
||||
if (isinstance(lookup_type, Instance)
|
||||
and lookup_type.type.fullname() == fullnames.FIELD_FULLNAME):
|
||||
and lookup_type.type.fullname == fullnames.FIELD_FULLNAME):
|
||||
field_info = helpers.lookup_class_typeinfo(helpers.get_typechecker_api(ctx), field.__class__)
|
||||
if field_info is None:
|
||||
return AnyType(TypeOfAny.explicit)
|
||||
|
||||
@@ -179,26 +179,20 @@ def add_new_class_for_module(module: MypyFile, name: str, bases: List[Instance],
|
||||
|
||||
# make new class expression
|
||||
classdef = ClassDef(new_class_unique_name, Block([]))
|
||||
classdef.fullname = module.fullname() + '.' + new_class_unique_name
|
||||
classdef.fullname = module.fullname + '.' + new_class_unique_name
|
||||
|
||||
# make new TypeInfo
|
||||
new_typeinfo = TypeInfo(SymbolTable(), classdef, module.fullname())
|
||||
new_typeinfo = TypeInfo(SymbolTable(), classdef, module.fullname)
|
||||
new_typeinfo.bases = bases
|
||||
calculate_mro(new_typeinfo)
|
||||
new_typeinfo.calculate_metaclass_type()
|
||||
|
||||
def add_field_to_new_typeinfo(var: Var, is_initialized_in_class: bool = False,
|
||||
is_property: bool = False) -> None:
|
||||
var.info = new_typeinfo
|
||||
var.is_initialized_in_class = is_initialized_in_class
|
||||
var.is_property = is_property
|
||||
var._fullname = new_typeinfo.fullname() + '.' + var.name()
|
||||
new_typeinfo.names[var.name()] = SymbolTableNode(MDEF, var)
|
||||
|
||||
# add fields
|
||||
var_items = [Var(item, typ) for item, typ in fields.items()]
|
||||
for var_item in var_items:
|
||||
add_field_to_new_typeinfo(var_item, is_property=True)
|
||||
for field_name, field_type in fields.items():
|
||||
var = Var(field_name, type=field_type)
|
||||
var.info = new_typeinfo
|
||||
var._fullname = new_typeinfo.fullname + '.' + field_name
|
||||
new_typeinfo.names[field_name] = SymbolTableNode(MDEF, var, plugin_generated=True)
|
||||
|
||||
classdef.info = new_typeinfo
|
||||
module.names[new_class_unique_name] = SymbolTableNode(GDEF, new_typeinfo, plugin_generated=True)
|
||||
@@ -282,7 +276,7 @@ def get_typechecker_api(ctx: Union[AttributeContext, MethodContext, FunctionCont
|
||||
|
||||
|
||||
def is_model_subclass_info(info: TypeInfo, django_context: 'DjangoContext') -> bool:
|
||||
return (info.fullname() in django_context.all_registered_model_class_fullnames
|
||||
return (info.fullname in django_context.all_registered_model_class_fullnames
|
||||
or info.has_base(fullnames.MODEL_CLASS_FULLNAME))
|
||||
|
||||
|
||||
@@ -299,7 +293,7 @@ def add_new_sym_for_info(info: TypeInfo, *, name: str, sym_type: MypyType) -> No
|
||||
var = Var(name=name, type=sym_type)
|
||||
# var.info: type of the object variable is bound to
|
||||
var.info = info
|
||||
var._fullname = info.fullname() + '.' + name
|
||||
var._fullname = info.fullname + '.' + name
|
||||
var.is_initialized_in_class = True
|
||||
var.is_inferred = True
|
||||
info.names[name] = SymbolTableNode(MDEF, var,
|
||||
|
||||
@@ -121,17 +121,17 @@ class NewSemanalDjangoPlugin(Plugin):
|
||||
|
||||
def get_additional_deps(self, file: MypyFile) -> List[Tuple[int, str, int]]:
|
||||
# for settings
|
||||
if file.fullname() == 'django.conf' and self.django_context.django_settings_module:
|
||||
if file.fullname == 'django.conf' and self.django_context.django_settings_module:
|
||||
return [self._new_dependency(self.django_context.django_settings_module)]
|
||||
|
||||
# for values / values_list
|
||||
if file.fullname() == 'django.db.models':
|
||||
if file.fullname == 'django.db.models':
|
||||
return [self._new_dependency('mypy_extensions'), self._new_dependency('typing')]
|
||||
|
||||
# for `get_user_model()`
|
||||
if self.django_context.settings:
|
||||
if (file.fullname() == 'django.contrib.auth'
|
||||
or file.fullname() in {'django.http', 'django.http.request'}):
|
||||
if (file.fullname == 'django.contrib.auth'
|
||||
or file.fullname in {'django.http', 'django.http.request'}):
|
||||
auth_user_model_name = self.django_context.settings.AUTH_USER_MODEL
|
||||
try:
|
||||
auth_user_module = self.django_context.apps_registry.get_model(auth_user_model_name).__module__
|
||||
@@ -141,7 +141,7 @@ class NewSemanalDjangoPlugin(Plugin):
|
||||
return [self._new_dependency(auth_user_module)]
|
||||
|
||||
# ensure that all mentioned to='someapp.SomeModel' are loaded with corresponding related Fields
|
||||
defined_model_classes = self.django_context.model_modules.get(file.fullname())
|
||||
defined_model_classes = self.django_context.model_modules.get(file.fullname)
|
||||
if not defined_model_classes:
|
||||
return []
|
||||
deps = set()
|
||||
@@ -153,13 +153,13 @@ class NewSemanalDjangoPlugin(Plugin):
|
||||
if related_model_cls is None:
|
||||
continue
|
||||
related_model_module = related_model_cls.__module__
|
||||
if related_model_module != file.fullname():
|
||||
if related_model_module != file.fullname:
|
||||
deps.add(self._new_dependency(related_model_module))
|
||||
# reverse relations
|
||||
for relation in model_class._meta.related_objects:
|
||||
related_model_cls = self.django_context.get_field_related_model_cls(relation)
|
||||
related_model_module = related_model_cls.__module__
|
||||
if related_model_module != file.fullname():
|
||||
if related_model_module != file.fullname:
|
||||
deps.add(self._new_dependency(related_model_module))
|
||||
return list(deps)
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ def _get_current_field_from_assignment(ctx: FunctionContext, django_context: Dja
|
||||
if field_name is None:
|
||||
return None
|
||||
|
||||
model_cls = django_context.get_model_class_by_fullname(outer_model_info.fullname())
|
||||
model_cls = django_context.get_model_class_by_fullname(outer_model_info.fullname)
|
||||
if model_cls is None:
|
||||
return None
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ def typecheck_model_method(ctx: Union[FunctionContext, MethodContext], django_co
|
||||
def redefine_and_typecheck_model_init(ctx: FunctionContext, django_context: DjangoContext) -> MypyType:
|
||||
assert isinstance(ctx.default_return_type, Instance)
|
||||
|
||||
model_fullname = ctx.default_return_type.type.fullname()
|
||||
model_fullname = ctx.default_return_type.type.fullname
|
||||
model_cls = django_context.get_model_class_by_fullname(model_fullname)
|
||||
if model_cls is None:
|
||||
return ctx.default_return_type
|
||||
@@ -67,7 +67,7 @@ def redefine_and_typecheck_model_create(ctx: MethodContext, django_context: Djan
|
||||
# only work with ctx.default_return_type = model Instance
|
||||
return ctx.default_return_type
|
||||
|
||||
model_fullname = ctx.default_return_type.type.fullname()
|
||||
model_fullname = ctx.default_return_type.type.fullname
|
||||
model_cls = django_context.get_model_class_by_fullname(model_fullname)
|
||||
if model_cls is None:
|
||||
return ctx.default_return_type
|
||||
|
||||
@@ -28,7 +28,7 @@ def return_proper_field_type_from_get_field(ctx: MethodContext, django_context:
|
||||
if not isinstance(model_type, Instance):
|
||||
return ctx.default_return_type
|
||||
|
||||
model_cls = django_context.get_model_class_by_fullname(model_type.type.fullname())
|
||||
model_cls = django_context.get_model_class_by_fullname(model_type.type.fullname)
|
||||
if model_cls is None:
|
||||
return ctx.default_return_type
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from collections import OrderedDict
|
||||
from typing import Type
|
||||
from typing import List, Tuple, Type
|
||||
|
||||
from django.db.models.base import Model
|
||||
from django.db.models.fields import DateField, DateTimeField
|
||||
@@ -7,10 +7,11 @@ from django.db.models.fields.related import ForeignKey
|
||||
from django.db.models.fields.reverse_related import (
|
||||
ManyToManyRel, ManyToOneRel, OneToOneRel,
|
||||
)
|
||||
from mypy.nodes import ARG_STAR2, Argument, Context, TypeInfo, Var
|
||||
from mypy.nodes import ARG_STAR2, Argument, Context, FuncDef, TypeInfo, Var
|
||||
from mypy.plugin import ClassDefContext
|
||||
from mypy.plugins import common
|
||||
from mypy.types import AnyType, Instance
|
||||
from mypy.plugins.common import add_method
|
||||
from mypy.types import AnyType, CallableType, Instance
|
||||
from mypy.types import Type as MypyType
|
||||
from mypy.types import TypeOfAny
|
||||
|
||||
@@ -43,7 +44,7 @@ class ModelClassInitializer:
|
||||
var = Var(name=name, type=typ)
|
||||
# var.info: type of the object variable is bound to
|
||||
var.info = self.model_classdef.info
|
||||
var._fullname = self.model_classdef.info.fullname() + '.' + name
|
||||
var._fullname = self.model_classdef.info.fullname + '.' + name
|
||||
var.is_initialized_in_class = True
|
||||
var.is_inferred = True
|
||||
return var
|
||||
@@ -126,7 +127,7 @@ class AddRelatedModelsId(ModelClassInitializer):
|
||||
|
||||
class AddManagers(ModelClassInitializer):
|
||||
def _is_manager_any(self, typ: Instance) -> bool:
|
||||
return typ.type.fullname() == fullnames.MANAGER_CLASS_FULLNAME and type(typ.args[0]) == AnyType
|
||||
return typ.type.fullname == fullnames.MANAGER_CLASS_FULLNAME and type(typ.args[0]) == AnyType
|
||||
|
||||
def run_with_model_cls(self, model_cls: Type[Model]) -> None:
|
||||
for manager_name, manager in model_cls._meta.managers_map.items():
|
||||
@@ -141,6 +142,7 @@ class AddManagers(ModelClassInitializer):
|
||||
has_manager_any_base = any(self._is_manager_any(base) for base in manager_info.bases)
|
||||
if has_manager_any_base:
|
||||
custom_model_manager_name = manager.model.__name__ + '_' + manager.__class__.__name__
|
||||
|
||||
bases = []
|
||||
for original_base in manager_info.bases:
|
||||
if self._is_manager_any(original_base):
|
||||
@@ -150,14 +152,53 @@ class AddManagers(ModelClassInitializer):
|
||||
original_base = helpers.reparametrize_instance(original_base,
|
||||
[Instance(self.model_classdef.info, [])])
|
||||
bases.append(original_base)
|
||||
|
||||
current_module = self.api.modules[self.model_classdef.info.module_name]
|
||||
custom_manager_info = helpers.add_new_class_for_module(current_module,
|
||||
custom_model_manager_name,
|
||||
bases=bases,
|
||||
fields=OrderedDict())
|
||||
# copy fields to a new manager
|
||||
new_cls_def_context = ClassDefContext(cls=custom_manager_info.defn,
|
||||
reason=self.ctx.reason,
|
||||
api=self.api)
|
||||
custom_manager_type = Instance(custom_manager_info, [Instance(self.model_classdef.info, [])])
|
||||
|
||||
for name, sym in manager_info.names.items():
|
||||
# replace self type with new class, if copying method
|
||||
if isinstance(sym.node, FuncDef):
|
||||
arguments, return_type = self.prepare_new_method_arguments(sym.node)
|
||||
add_method(new_cls_def_context,
|
||||
name,
|
||||
args=arguments,
|
||||
return_type=return_type,
|
||||
self_type=custom_manager_type)
|
||||
continue
|
||||
|
||||
new_sym = sym.copy()
|
||||
if isinstance(new_sym.node, Var):
|
||||
new_var = Var(name, type=sym.type)
|
||||
new_var.info = custom_manager_info
|
||||
new_var._fullname = custom_manager_info.fullname + '.' + name
|
||||
new_sym.node = new_var
|
||||
custom_manager_info.names[name] = new_sym
|
||||
|
||||
self.add_new_node_to_model_class(manager_name, custom_manager_type)
|
||||
|
||||
def prepare_new_method_arguments(self, node: FuncDef) -> Tuple[List[Argument], MypyType]:
|
||||
arguments = []
|
||||
for argument in node.arguments[1:]:
|
||||
if argument.type_annotation is None:
|
||||
argument.type_annotation = AnyType(TypeOfAny.unannotated)
|
||||
arguments.append(argument)
|
||||
|
||||
if isinstance(node.type, CallableType):
|
||||
return_type = node.type.ret_type
|
||||
else:
|
||||
return_type = AnyType(TypeOfAny.unannotated)
|
||||
|
||||
return arguments, return_type
|
||||
|
||||
|
||||
class AddDefaultManagerAttribute(ModelClassInitializer):
|
||||
def run_with_model_cls(self, model_cls: Type[Model]) -> None:
|
||||
|
||||
@@ -16,7 +16,7 @@ def typecheck_queryset_filter(ctx: MethodContext, django_context: DjangoContext)
|
||||
if not ctx.type.args or not isinstance(ctx.type.args[0], Instance):
|
||||
return ctx.default_return_type
|
||||
|
||||
model_cls_fullname = ctx.type.args[0].type.fullname()
|
||||
model_cls_fullname = ctx.type.args[0].type.fullname
|
||||
model_cls = django_context.get_model_class_by_fullname(model_cls_fullname)
|
||||
if model_cls is None:
|
||||
return ctx.default_return_type
|
||||
@@ -44,7 +44,7 @@ def typecheck_queryset_filter(ctx: MethodContext, django_context: DjangoContext)
|
||||
|
||||
|
||||
def resolve_combinable_type(combinable_type: Instance, django_context: DjangoContext) -> MypyType:
|
||||
if combinable_type.type.fullname() != fullnames.F_EXPRESSION_FULLNAME:
|
||||
if combinable_type.type.fullname != fullnames.F_EXPRESSION_FULLNAME:
|
||||
# Combinables aside from F expressions are unsupported
|
||||
return AnyType(TypeOfAny.explicit)
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ def extract_proper_type_queryset_values_list(ctx: MethodContext, django_context:
|
||||
if model_type is None:
|
||||
return AnyType(TypeOfAny.from_omitted_generics)
|
||||
|
||||
model_cls = django_context.get_model_class_by_fullname(model_type.type.fullname())
|
||||
model_cls = django_context.get_model_class_by_fullname(model_type.type.fullname)
|
||||
if model_cls is None:
|
||||
return ctx.default_return_type
|
||||
|
||||
@@ -166,7 +166,7 @@ def extract_proper_type_queryset_values(ctx: MethodContext, django_context: Djan
|
||||
if model_type is None:
|
||||
return AnyType(TypeOfAny.from_omitted_generics)
|
||||
|
||||
model_cls = django_context.get_model_class_by_fullname(model_type.type.fullname())
|
||||
model_cls = django_context.get_model_class_by_fullname(model_type.type.fullname)
|
||||
if model_cls is None:
|
||||
return ctx.default_return_type
|
||||
|
||||
|
||||
88
scripts/catch_non_abstract_annotation.py
Normal file
88
scripts/catch_non_abstract_annotation.py
Normal file
@@ -0,0 +1,88 @@
|
||||
import os
|
||||
from typing import Optional
|
||||
|
||||
import libcst
|
||||
from libcst import Annotation, BaseExpression, FunctionDef, Name, Subscript
|
||||
from libcst.metadata import SyntacticPositionProvider
|
||||
|
||||
BASE_DIR = 'django-stubs'
|
||||
|
||||
fpath = os.path.join(BASE_DIR, 'core', 'checks', 'model_checks.pyi')
|
||||
with open(fpath, 'r') as f:
|
||||
contents = f.read()
|
||||
|
||||
|
||||
tree = libcst.parse_module(contents)
|
||||
|
||||
|
||||
class TypeAnnotationsAnalyzer(libcst.CSTVisitor):
|
||||
METADATA_DEPENDENCIES = (SyntacticPositionProvider,)
|
||||
|
||||
def __init__(self, fpath: str):
|
||||
super().__init__()
|
||||
self.fpath = fpath
|
||||
|
||||
def get_node_location(self, node: FunctionDef) -> str:
|
||||
start_line = self.get_metadata(SyntacticPositionProvider, node).start.line
|
||||
return f'{self.fpath}:{start_line}'
|
||||
|
||||
def show_error_for_node(self, node: FunctionDef, error_message: str):
|
||||
print(self.get_node_location(node), error_message)
|
||||
|
||||
def check_subscripted_annotation(self, annotation: BaseExpression) -> Optional[str]:
|
||||
if isinstance(annotation, Subscript):
|
||||
if isinstance(annotation.value, Name):
|
||||
error_message = self.check_concrete_class_usage(annotation.value)
|
||||
if error_message:
|
||||
return error_message
|
||||
|
||||
if annotation.value.value == 'Union':
|
||||
for slice_param in annotation.slice:
|
||||
if isinstance(slice_param.slice.value, Name):
|
||||
error_message = self.check_concrete_class_usage(annotation.value)
|
||||
if error_message:
|
||||
return error_message
|
||||
|
||||
def check_concrete_class_usage(self, name_node: Name) -> Optional[str]:
|
||||
if name_node.value == 'List':
|
||||
return (f'Concrete class {name_node.value!r} used for an iterable annotation. '
|
||||
f'Use abstract collection (Iterable, Collection, Sequence) instead')
|
||||
|
||||
def visit_FunctionDef(self, node: FunctionDef) -> Optional[bool]:
|
||||
params_node = node.params
|
||||
for param_node in [*params_node.params, *params_node.default_params]:
|
||||
param_name = param_node.name.value
|
||||
annotation_node = param_node.annotation # type: Annotation
|
||||
if annotation_node is not None:
|
||||
annotation = annotation_node.annotation
|
||||
if annotation.value == 'None':
|
||||
self.show_error_for_node(node, f'"None" type annotation used for parameter {param_name!r}')
|
||||
continue
|
||||
|
||||
error_message = self.check_subscripted_annotation(annotation)
|
||||
if error_message is not None:
|
||||
self.show_error_for_node(node, error_message)
|
||||
continue
|
||||
|
||||
if node.returns is not None:
|
||||
return_annotation = node.returns.annotation
|
||||
if isinstance(return_annotation, Subscript) and return_annotation.value.value == 'Union':
|
||||
self.show_error_for_node(node, 'Union is return type annotation')
|
||||
|
||||
return False
|
||||
|
||||
|
||||
for dirpath, dirnames, filenames in os.walk(BASE_DIR):
|
||||
for filename in filenames:
|
||||
fpath = os.path.join(dirpath, filename)
|
||||
# skip all other checks for now, low priority
|
||||
if not fpath.startswith(('django-stubs/db', 'django-stubs/views', 'django-stubs/apps',
|
||||
'django-stubs/http', 'django-stubs/contrib/postgres')):
|
||||
continue
|
||||
|
||||
with open(fpath, 'r') as f:
|
||||
contents = f.read()
|
||||
|
||||
tree = libcst.MetadataWrapper(libcst.parse_module(contents))
|
||||
analyzer = TypeAnnotationsAnalyzer(fpath)
|
||||
tree.visit(analyzer)
|
||||
@@ -1,3 +1,5 @@
|
||||
import django
|
||||
|
||||
SECRET_KEY = '1'
|
||||
SITE_ID = 1
|
||||
|
||||
@@ -41,7 +43,6 @@ test_modules = [
|
||||
'bulk_create',
|
||||
'cache',
|
||||
'check_framework',
|
||||
'choices',
|
||||
'conditional_processing',
|
||||
'constraints',
|
||||
'contenttypes_tests',
|
||||
@@ -219,6 +220,9 @@ test_modules = [
|
||||
'wsgi',
|
||||
]
|
||||
|
||||
if django.VERSION[0] == 2:
|
||||
test_modules += ['choices']
|
||||
|
||||
invalid_apps = {
|
||||
'import_error_package',
|
||||
}
|
||||
|
||||
@@ -5,13 +5,13 @@ import re
|
||||
IGNORED_MODULES = {'schema', 'gis_tests', 'admin_widgets', 'admin_filters', 'migrations',
|
||||
'sitemaps_tests', 'staticfiles_tests', 'modeladmin', 'model_forms',
|
||||
'generic_views', 'forms_tests', 'flatpages_tests', 'admin_utils',
|
||||
'admin_ordering', 'admin_changelist', 'admin_views', 'mail', 'redirects_tests',
|
||||
'admin_ordering', 'admin_changelist', 'admin_views', 'redirects_tests',
|
||||
'invalid_models_tests', 'i18n', 'migrate_signals', 'model_formsets',
|
||||
'template_tests', 'template_backends', 'test_runner', 'admin_scripts',
|
||||
'sites_tests', 'inline_formsets', 'foreign_object', 'cache', 'test_client', 'test_client_regress'}
|
||||
|
||||
MOCK_OBJECTS = ['MockRequest', 'MockCompiler', 'modelz', 'call_count', 'call_args_list',
|
||||
'call_args', 'MockUser', 'Xtemplate', 'DummyRequest', 'DummyUser', 'MinimalUser']
|
||||
'call_args', 'MockUser', 'Xtemplate', 'DummyRequest', 'DummyUser', 'MinimalUser', 'DummyNode']
|
||||
EXTERNAL_MODULES = ['psycopg2', 'PIL', 'selenium', 'oracle', 'mysql', 'sqlite3', 'sqlparse', 'tblib', 'numpy',
|
||||
'bcrypt', 'argon2', 'xml.dom']
|
||||
IGNORED_ERRORS = {
|
||||
@@ -25,8 +25,7 @@ IGNORED_ERRORS = {
|
||||
'Cannot assign to a type',
|
||||
'"HttpResponseBase" has no attribute',
|
||||
'"object" has no attribute',
|
||||
re.compile(r'"Callable\[(\[(Any(, )?)*((, )?VarArg\(Any\))?((, )?KwArg\(Any\))?\]|\.\.\.), Any\]" '
|
||||
r'has no attribute'),
|
||||
re.compile(r'"Callable\[.+, Any\]" has no attribute'),
|
||||
'has no attribute "deconstruct"',
|
||||
# private members
|
||||
re.compile(r'has no attribute ("|\')_[a-zA-Z_]+("|\')'),
|
||||
@@ -35,10 +34,9 @@ IGNORED_ERRORS = {
|
||||
re.compile(r"Expression of type '.*' is not supported"),
|
||||
'has incompatible type "object"',
|
||||
'undefined in superclass',
|
||||
'Argument after ** must be a mapping, not "object"',
|
||||
'Argument after ** must be a mapping',
|
||||
'note:',
|
||||
re.compile(r'Item "None" of "[a-zA-Z_ ,\[\]]+" has no attribute'),
|
||||
'"Optional[List[_Record]]"',
|
||||
'"Callable[..., None]" has no attribute',
|
||||
'does not return a value',
|
||||
'has no attribute "alternatives"',
|
||||
@@ -61,7 +59,12 @@ IGNORED_ERRORS = {
|
||||
# TODO: multitable inheritance
|
||||
'ptr',
|
||||
'Incompatible types in assignment (expression has type "Callable[',
|
||||
'SimpleLazyObject'
|
||||
'SimpleLazyObject',
|
||||
'Test',
|
||||
'Mixin" has no attribute',
|
||||
'Incompatible types in string interpolation',
|
||||
'"None" has no attribute',
|
||||
'has no attribute "assert',
|
||||
],
|
||||
'aggregation': [
|
||||
re.compile(r'got "Optional\[(Author|Publisher)\]", expected "Union\[(Author|Publisher), Combinable\]"'),
|
||||
@@ -83,52 +86,69 @@ IGNORED_ERRORS = {
|
||||
'Unsupported left operand type for + ("Sequence[str]")',
|
||||
'AuthenticationFormWithInactiveUsersOkay',
|
||||
'Incompatible types in assignment (expression has type "Dict[str, Any]", variable has type "QueryDict")',
|
||||
'No overload variant of "int" matches argument type "AnonymousUser"',
|
||||
'expression has type "AnonymousUser", variable has type "User"',
|
||||
],
|
||||
'basic': [
|
||||
'Unexpected keyword argument "unknown_kwarg" for "refresh_from_db" of "Model"',
|
||||
'Unexpected attribute "foo" for model "Article"',
|
||||
'has no attribute "touched"',
|
||||
'Incompatible types in assignment (expression has type "Type[CustomQuerySet]"'
|
||||
'Incompatible types in assignment (expression has type "Type[CustomQuerySet]"',
|
||||
'"Manager[Article]" has no attribute "do_something"',
|
||||
],
|
||||
'backends': [
|
||||
'"DatabaseError" has no attribute "pgcode"'
|
||||
],
|
||||
'builtin_server': [
|
||||
'"ServerHandler" has no attribute',
|
||||
],
|
||||
'bulk_create': [
|
||||
'has incompatible type "List[Country]"; expected "Iterable[TwoFields]"',
|
||||
'List item 1 has incompatible type "Country"; expected "ProxyCountry"',
|
||||
],
|
||||
'check_framework': [
|
||||
'base class "Model" defined the type as "Callable',
|
||||
'Value of type "Collection[str]" is not indexable',
|
||||
'Unsupported target for indexed assignment',
|
||||
],
|
||||
'constraints': [
|
||||
'Argument "condition" to "UniqueConstraint" has incompatible type "str"; expected "Optional[Q]"'
|
||||
],
|
||||
'contenttypes_tests': [
|
||||
# 'Item "Model" of "Union[GenericForeignKey, Model, None]" has no attribute'
|
||||
'base class "BaseOrderWithRespectToTests" defined the type as "None"'
|
||||
'"FooWithBrokenAbsoluteUrl" has no attribute "unknown_field"',
|
||||
'contenttypes_tests.models.Site',
|
||||
'Argument 1 to "set" of "RelatedManager" has incompatible type "SiteManager[Site]"',
|
||||
],
|
||||
'custom_lookups': [
|
||||
'in base class "SQLFuncMixin"'
|
||||
'in base class "SQLFuncMixin"',
|
||||
'has no attribute "name"',
|
||||
],
|
||||
'custom_columns': [
|
||||
"Cannot resolve keyword 'firstname' into field",
|
||||
],
|
||||
'custom_pk': [
|
||||
'"Employee" has no attribute "id"'
|
||||
'"Employee" has no attribute "id"',
|
||||
],
|
||||
'custom_managers': [
|
||||
'Unsupported dynamic base class',
|
||||
'Incompatible types in assignment (expression has type "CharField',
|
||||
'Item "Book" of "Optional[Book]" has no attribute "favorite_avg"'
|
||||
'Item "Book" of "Optional[Book]" has no attribute "favorite_avg"',
|
||||
],
|
||||
'csrf_tests': [
|
||||
'Incompatible types in assignment (expression has type "property", ' +
|
||||
'base class "HttpRequest" defined the type as "QueryDict")'
|
||||
'expression has type "property", base class "HttpRequest" defined the type as "QueryDict"',
|
||||
'expression has type "Dict[<nothing>, <nothing>]", variable has type "SessionBase"',
|
||||
],
|
||||
'dates': [
|
||||
'Too few arguments for "dates" of',
|
||||
],
|
||||
'dbshell': [
|
||||
'Incompatible types in assignment (expression has type "None"',
|
||||
],
|
||||
'defer': [
|
||||
'Too many arguments for "refresh_from_db" of "Model"'
|
||||
],
|
||||
'delete': [
|
||||
'Incompatible type for lookup \'pk\': (got "Optional[int]", expected "int")',
|
||||
'Incompatible type for lookup \'pk\': (got "Optional[int]", expected "Union[str, int]")',
|
||||
],
|
||||
'dispatch': [
|
||||
'Item "str" of "Union[ValueError, str]" has no attribute "args"'
|
||||
@@ -144,45 +164,54 @@ IGNORED_ERRORS = {
|
||||
'decorators': [
|
||||
'"Type[object]" has no attribute "method"'
|
||||
],
|
||||
'expressions_case': [
|
||||
'Item "Field[Any, Any]" of "Union[Field[Any, Any], ForeignObjectRel]" has no attribute "is_relation"'
|
||||
],
|
||||
'expressions_window': [
|
||||
'has incompatible type "str"'
|
||||
],
|
||||
'file_uploads': [
|
||||
'has no attribute "content_type"',
|
||||
],
|
||||
'file_storage': [
|
||||
'Incompatible types in assignment (expression has type "None", variable has type "str")',
|
||||
'Property "base_url" defined in "FileSystemStorage" is read-only',
|
||||
],
|
||||
'files': [
|
||||
'Incompatible types in assignment (expression has type "IOBase", variable has type "File")',
|
||||
'Argument 1 to "write" of "SpooledTemporaryFile"',
|
||||
],
|
||||
'filtered_relation': [
|
||||
'has no attribute "name"',
|
||||
],
|
||||
'fixtures': [
|
||||
'Incompatible types in assignment (expression has type "int", target has type "Iterable[str]")',
|
||||
'Incompatible types in assignment (expression has type "SpyManager[Spy]"'
|
||||
'Incompatible types in assignment (expression has type "SpyManager[Spy]"',
|
||||
],
|
||||
'fixtures_regress': [
|
||||
'Unsupported left operand type for + ("None")',
|
||||
],
|
||||
'from_db_value': [
|
||||
'"Cash" has no attribute'
|
||||
'"Cash" has no attribute',
|
||||
'Argument 1 to "__str__" of "Decimal"',
|
||||
],
|
||||
'get_object_or_404': [
|
||||
'Argument 1 to "get_object_or_404" has incompatible type "str"; '
|
||||
+ 'expected "Union[Type[<nothing>], QuerySet[<nothing>]]"',
|
||||
'Argument 1 to "get_list_or_404" has incompatible type "List[Type[Article]]"; '
|
||||
+ 'expected "Union[Type[<nothing>], QuerySet[<nothing>]]"',
|
||||
'CustomClass'
|
||||
'CustomClass',
|
||||
],
|
||||
'generic_relations': [
|
||||
"Cannot resolve keyword 'vegetable' into field"
|
||||
"Cannot resolve keyword 'vegetable' into field",
|
||||
],
|
||||
'generic_relations_regress': [
|
||||
'"Link" has no attribute'
|
||||
'"Link" has no attribute',
|
||||
],
|
||||
'httpwrappers': [
|
||||
'Argument 2 to "appendlist" of "QueryDict"',
|
||||
'Incompatible types in assignment (expression has type "int", target has type "Union[str, List[str]]")',
|
||||
'Argument 1 to "fromkeys" of "QueryDict" has incompatible type "int"'
|
||||
'Argument 1 to "fromkeys" of "QueryDict" has incompatible type "int"',
|
||||
],
|
||||
'humanize_tests': [
|
||||
'Argument 1 to "append" of "list" has incompatible type "None"; expected "str"'
|
||||
'Argument 1 to "append" of "list" has incompatible type "None"; expected "str"',
|
||||
],
|
||||
'lookup': [
|
||||
'Unexpected keyword argument "headline__startswith" for "in_bulk" of',
|
||||
@@ -190,18 +219,42 @@ IGNORED_ERRORS = {
|
||||
"Cannot resolve keyword 'pub_date_year' into field",
|
||||
"Cannot resolve keyword 'blahblah' into field",
|
||||
],
|
||||
'logging_tests': [
|
||||
re.compile(r'Argument [0-9] to "makeRecord" of "Logger"'),
|
||||
'"LogRecord" has no attribute "request"',
|
||||
],
|
||||
'm2m_regress': [
|
||||
"Cannot resolve keyword 'porcupine' into field",
|
||||
'Argument 1 to "set" of "RelatedManager" has incompatible type "int"',
|
||||
],
|
||||
'mail': [
|
||||
'List item 1 has incompatible type "None"; expected "str"',
|
||||
'Argument 1 to "push" of "SMTPChannel" has incompatible type "str"; expected "bytes"',
|
||||
'Value of type "Union[List[Message], str, bytes, None]" is not indexable',
|
||||
'Incompatible types in assignment '
|
||||
+ '(expression has type "bool", variable has type "Union[SMTP_SSL, SMTP, None]")',
|
||||
re.compile(
|
||||
r'Item "(int|str)" of "Union\[Message, str, int, Any\]" has no attribute "(get_content_type|get_filename)"'
|
||||
)
|
||||
],
|
||||
'messages_tests': [
|
||||
'List item 0 has incompatible type "Dict[str, Message]"; expected "Message"'
|
||||
'List item 0 has incompatible type "Dict[str, Message]"; expected "Message"',
|
||||
'Too many arguments',
|
||||
'CustomRequest',
|
||||
],
|
||||
'middleware': [
|
||||
'"HttpRequest" has no attribute'
|
||||
re.compile(r'"(HttpRequest|WSGIRequest)" has no attribute'),
|
||||
],
|
||||
'many_to_many': [
|
||||
'(expression has type "List[Article]", variable has type "RelatedManager[Article]"',
|
||||
'"add" of "RelatedManager" has incompatible type "Article"; expected "Union[Publication, int]"',
|
||||
],
|
||||
'many_to_one': [
|
||||
'Incompatible type for "parent" of "Child" (got "None", expected "Union[Parent, Combinable]")',
|
||||
'Incompatible type for "parent" of "Child" (got "Child", expected "Union[Parent, Combinable]")'
|
||||
'Incompatible type for "parent" of "Child" (got "Child", expected "Union[Parent, Combinable]")',
|
||||
'expression has type "List[<nothing>]", variable has type "RelatedManager[Article]"',
|
||||
'"Reporter" has no attribute "cached_query"',
|
||||
'to "add" of "RelatedManager" has incompatible type "Reporter"; expected "Union[Article, int]"',
|
||||
],
|
||||
'middleware_exceptions': [
|
||||
'Argument 1 to "append" of "list" has incompatible type "Tuple[Any, Any]"; expected "str"'
|
||||
@@ -209,9 +262,10 @@ IGNORED_ERRORS = {
|
||||
'model_fields': [
|
||||
'Item "Field[Any, Any]" of "Union[Field[Any, Any], ForeignObjectRel]" has no attribute',
|
||||
'Incompatible types in assignment (expression has type "Type[Person',
|
||||
'base class "IntegerFieldTests"',
|
||||
'ImageFieldTestMixin',
|
||||
'Incompatible types in assignment (expression has type "FloatModel", variable has type',
|
||||
'"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',
|
||||
],
|
||||
'model_indexes': [
|
||||
'Argument "condition" to "Index" has incompatible type "str"; expected "Optional[Q]"'
|
||||
@@ -231,6 +285,16 @@ IGNORED_ERRORS = {
|
||||
'Incompatible type for "department" of "Worker"',
|
||||
'"PickledModel" has no attribute',
|
||||
'"Department" has no attribute "evaluate"',
|
||||
'Unsupported target for indexed assignment',
|
||||
],
|
||||
'model_formsets_regress': [
|
||||
'Incompatible types in assignment (expression has type "int", target has type "str")',
|
||||
],
|
||||
'model_options': [
|
||||
'expression has type "Dict[str, Type[Model]]", target has type "OrderedDict',
|
||||
],
|
||||
'model_enums': [
|
||||
"'bool' is not a valid base class",
|
||||
],
|
||||
'multiple_database': [
|
||||
'Unexpected attribute "extra_arg" for model "Book"'
|
||||
@@ -239,7 +303,6 @@ IGNORED_ERRORS = {
|
||||
"Cannot resolve keyword 'foo' into field"
|
||||
],
|
||||
'order_with_respect_to': [
|
||||
'BaseOrderWithRespectToTests',
|
||||
'"Dimension" has no attribute "set_component_order"',
|
||||
],
|
||||
'one_to_one': [
|
||||
@@ -255,8 +318,6 @@ IGNORED_ERRORS = {
|
||||
'DummyJSONField',
|
||||
'Incompatible types in assignment (expression has type "Type[Field[Any, Any]]',
|
||||
'Argument "encoder" to "JSONField" has incompatible type "DjangoJSONEncoder";',
|
||||
re.compile(r'Incompatible types in assignment \(expression has type "Type\[.+?\]", '
|
||||
r'base class "(UnaccentTest|TrigramTest)" defined the type as "Type\[CharFieldModel\]"\)'),
|
||||
'("None" and "SearchQuery")',
|
||||
],
|
||||
'properties': [
|
||||
@@ -285,12 +346,14 @@ IGNORED_ERRORS = {
|
||||
'"Collection[Any]" has no attribute "explain"',
|
||||
"Cannot resolve keyword 'unknown_field' into field",
|
||||
'Incompatible type for lookup \'tag\': (got "str", expected "Union[Tag, int, None]")',
|
||||
'No overload variant of "__getitem__" of "QuerySet" matches argument type "str"',
|
||||
],
|
||||
'requests': [
|
||||
'Incompatible types in assignment (expression has type "Dict[str, str]", variable has type "QueryDict")'
|
||||
],
|
||||
'responses': [
|
||||
'Argument 1 to "TextIOWrapper" has incompatible type "HttpResponse"; expected "IO[bytes]"'
|
||||
'Argument 1 to "TextIOWrapper" has incompatible type "HttpResponse"; expected "IO[bytes]"',
|
||||
'"FileLike" has no attribute "closed"',
|
||||
],
|
||||
'reverse_lookup': [
|
||||
"Cannot resolve keyword 'choice' into field"
|
||||
@@ -302,15 +365,16 @@ IGNORED_ERRORS = {
|
||||
'Argument 1 to "append" of "list" has incompatible type "Tuple[Any, Any, Optional[Any], Any]";'
|
||||
],
|
||||
'sites_framework': [
|
||||
'expression has type "CurrentSiteManager[CustomArticle]", base class "AbstractArticle"'
|
||||
'expression has type "CurrentSiteManager[CustomArticle]", base class "AbstractArticle"',
|
||||
"Name 'Optional' is not defined",
|
||||
],
|
||||
'syndication_tests': [
|
||||
'List or tuple expected as variable arguments'
|
||||
],
|
||||
'sessions_tests': [
|
||||
'base class "SessionTestsMixin" defined the type as "None")',
|
||||
'Incompatible types in assignment (expression has type "None", variable has type "int")',
|
||||
'"AbstractBaseSession" has no attribute'
|
||||
'"AbstractBaseSession" has no attribute',
|
||||
'"None" not callable',
|
||||
],
|
||||
'select_related': [
|
||||
'Item "ForeignKey[Union[Genus, Combinable], Genus]" '
|
||||
@@ -324,18 +388,28 @@ IGNORED_ERRORS = {
|
||||
re.compile('Argument [0-9] to "WSGIRequestHandler"'),
|
||||
'"HTTPResponse" has no attribute',
|
||||
'"type" has no attribute',
|
||||
'"WSGIRequest" has no attribute "makefile"'
|
||||
'"WSGIRequest" has no attribute "makefile"',
|
||||
'LiveServerAddress',
|
||||
'"Stub" has no attribute "makefile"',
|
||||
],
|
||||
'serializers': [
|
||||
'"SerializersTestBase" defined the type as "None"',
|
||||
'"Model" has no attribute "data"',
|
||||
'"Iterable[Any]" has no attribute "content"',
|
||||
re.compile(r'Argument 1 to "(serialize|deserialize)" has incompatible type "None"; expected "str"')
|
||||
],
|
||||
'string_lookup': [
|
||||
'"Bar" has no attribute "place"',
|
||||
],
|
||||
'test_utils': [
|
||||
'"PossessedCar" has no attribute "color"',
|
||||
'expression has type "None", variable has type "List[str]"',
|
||||
],
|
||||
'transactions': [
|
||||
'Incompatible types in assignment (expression has type "Thread", variable has type "Callable[[], Any]")'
|
||||
],
|
||||
'urlpatterns': [
|
||||
'"object" not callable'
|
||||
'"object" not callable',
|
||||
'"None" not callable',
|
||||
],
|
||||
'urlpatterns_reverse': [
|
||||
'List or tuple expected as variable arguments',
|
||||
@@ -344,6 +418,7 @@ IGNORED_ERRORS = {
|
||||
],
|
||||
'utils_tests': [
|
||||
'Argument 1 to "activate" has incompatible type "None"; expected "Union[tzinfo, str]"',
|
||||
'Argument 1 to "activate" has incompatible type "Optional[str]"; expected "str"',
|
||||
'Incompatible types in assignment (expression has type "None", base class "object" defined the type as',
|
||||
'Class',
|
||||
'has no attribute "cp"',
|
||||
@@ -351,11 +426,28 @@ IGNORED_ERRORS = {
|
||||
'has no attribute "sort"',
|
||||
'Unsupported target for indexed assignment',
|
||||
'defined the type as "None"',
|
||||
'Argument 1 to "Path" has incompatible type "Optional[str]"'
|
||||
'Argument 1 to "Path" has incompatible type "Optional[str]"',
|
||||
'"None" not callable',
|
||||
'"WSGIRequest" has no attribute "process_response_content"',
|
||||
'No overload variant of "join" matches argument types "str", "None"',
|
||||
'Argument 1 to "Archive" has incompatible type "None"; expected "str"',
|
||||
'Argument 1 to "to_path" has incompatible type "int"; expected "Union[Path, str]"',
|
||||
|
||||
],
|
||||
'view_tests': [
|
||||
"Module 'django.views.debug' has no attribute 'Path'"
|
||||
]
|
||||
"Module 'django.views.debug' has no attribute 'Path'",
|
||||
'Value of type "Optional[List[str]]" is not indexable',
|
||||
'ExceptionUser',
|
||||
'view_tests.tests.test_debug.User',
|
||||
'Exception must be derived from BaseException',
|
||||
"No binding for nonlocal 'tb_frames' found",
|
||||
],
|
||||
'validation': [
|
||||
'has no attribute "name"',
|
||||
],
|
||||
'wsgi': [
|
||||
'"HttpResponse" has no attribute "block_size"',
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -2,15 +2,23 @@ import itertools
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from argparse import ArgumentParser
|
||||
from collections import defaultdict
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Pattern, Union
|
||||
|
||||
from git import Repo
|
||||
|
||||
from scripts.enabled_test_modules import (
|
||||
EXTERNAL_MODULES, IGNORED_ERRORS, IGNORED_MODULES, MOCK_OBJECTS,
|
||||
)
|
||||
|
||||
DJANGO_COMMIT_REFS = {
|
||||
'2.2': 'e8b0903976077b951795938b260211214ed7fe41',
|
||||
'3.0': '7ec5962638144cbf4c2e47ea7d8dc02d1ce44394'
|
||||
}
|
||||
PROJECT_DIRECTORY = Path(__file__).parent.parent
|
||||
DJANGO_SOURCE_DIRECTORY = PROJECT_DIRECTORY / 'django-sources' # type: Path
|
||||
|
||||
|
||||
def get_unused_ignores(ignored_message_freq: Dict[str, Dict[Union[str, Pattern], int]]) -> List[str]:
|
||||
@@ -67,11 +75,29 @@ def replace_with_clickable_location(error: str, abs_test_folder: Path) -> str:
|
||||
return error.replace(raw_path, clickable_location)
|
||||
|
||||
|
||||
def get_django_repo_object() -> Repo:
|
||||
if not DJANGO_SOURCE_DIRECTORY.exists():
|
||||
DJANGO_SOURCE_DIRECTORY.mkdir(exist_ok=True, parents=False)
|
||||
return Repo.clone_from('https://github.com/django/django.git', DJANGO_SOURCE_DIRECTORY)
|
||||
else:
|
||||
repo = Repo(DJANGO_SOURCE_DIRECTORY)
|
||||
return repo
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument('--django_version', choices=['2.2', '3.0'], required=True)
|
||||
args = parser.parse_args()
|
||||
|
||||
commit_sha = DJANGO_COMMIT_REFS[args.django_version]
|
||||
repo = get_django_repo_object()
|
||||
if repo.head.commit.hexsha != commit_sha:
|
||||
repo.git.fetch('origin')
|
||||
repo.git.checkout(commit_sha)
|
||||
|
||||
mypy_config_file = (PROJECT_DIRECTORY / 'scripts' / 'mypy.ini').absolute()
|
||||
repo_directory = PROJECT_DIRECTORY / 'django-sources'
|
||||
mypy_cache_dir = Path(__file__).parent / '.mypy_cache'
|
||||
tests_root = repo_directory / 'tests'
|
||||
tests_root = DJANGO_SOURCE_DIRECTORY / 'tests'
|
||||
global_rc = 0
|
||||
|
||||
try:
|
||||
|
||||
4
setup.py
4
setup.py
@@ -21,14 +21,14 @@ with open('README.md', 'r') as f:
|
||||
readme = f.read()
|
||||
|
||||
dependencies = [
|
||||
'mypy>=0.730',
|
||||
'mypy>=0.750,<0.760',
|
||||
'typing-extensions',
|
||||
'django',
|
||||
]
|
||||
|
||||
setup(
|
||||
name="django-stubs",
|
||||
version="1.2.0",
|
||||
version="1.3.0",
|
||||
description='Mypy stubs for Django',
|
||||
long_description=readme,
|
||||
long_description_content_type='text/markdown',
|
||||
|
||||
@@ -99,7 +99,7 @@
|
||||
class User(models.Model):
|
||||
pass
|
||||
class Profile(models.Model):
|
||||
user = models.OneToOneField(to=User, on_delete=models)
|
||||
user = models.OneToOneField(to=User, on_delete=models.CASCADE)
|
||||
|
||||
- case: test_circular_dependency_in_imports_with_foreign_key
|
||||
main: |
|
||||
|
||||
@@ -29,3 +29,19 @@
|
||||
|
||||
class Blog(models.Model):
|
||||
created_at = models.DateTimeField()
|
||||
|
||||
|
||||
- case: queryset_missing_method
|
||||
main: |
|
||||
from myapp.models import User
|
||||
reveal_type(User.objects) # N: Revealed type is 'django.db.models.manager.Manager[myapp.models.User]'
|
||||
User.objects.not_existing_method() # E: "Manager[User]" has no attribute "not_existing_method"
|
||||
installed_apps:
|
||||
- myapp
|
||||
files:
|
||||
- path: myapp/__init__.py
|
||||
- path: myapp/models.py
|
||||
content: |
|
||||
from django.db import models
|
||||
class User(models.Model):
|
||||
pass
|
||||
@@ -33,7 +33,7 @@
|
||||
- case: filter_with_invalid_type
|
||||
main: |
|
||||
from myapp.models import User
|
||||
User.objects.filter(age='hello') # E: Incompatible type for lookup 'age': (got "str", expected "int")
|
||||
User.objects.filter(age=User()) # E: Incompatible type for lookup 'age': (got "User", expected "Union[str, int]")
|
||||
installed_apps:
|
||||
- myapp
|
||||
files:
|
||||
@@ -49,12 +49,12 @@
|
||||
- case: filter_with_multiple_fields
|
||||
main: |
|
||||
from myapp.models import User
|
||||
User.objects.filter(age='hello', gender='world')
|
||||
User.objects.filter(age=User(), gender=User())
|
||||
installed_apps:
|
||||
- myapp
|
||||
out: |
|
||||
main:2: error: Incompatible type for lookup 'age': (got "str", expected "int")
|
||||
main:2: error: Incompatible type for lookup 'gender': (got "str", expected "int")
|
||||
main:2: error: Incompatible type for lookup 'age': (got "User", expected "Union[str, int]")
|
||||
main:2: error: Incompatible type for lookup 'gender': (got "User", expected "Union[str, int]")
|
||||
files:
|
||||
- path: myapp/__init__.py
|
||||
- path: myapp/models.py
|
||||
@@ -128,7 +128,7 @@
|
||||
Blog.objects.filter(publisher__id=1)
|
||||
|
||||
Blog.objects.filter(publisher=blog) # E: Incompatible type for lookup 'publisher': (got "Blog", expected "Union[Publisher, int, None]")
|
||||
Blog.objects.filter(publisher_id='hello') # E: Incompatible type for lookup 'publisher_id': (got "str", expected "int")
|
||||
Blog.objects.filter(publisher_id=blog) # E: Incompatible type for lookup 'publisher_id': (got "Blog", expected "Union[str, int]")
|
||||
installed_apps:
|
||||
- myapp
|
||||
files:
|
||||
@@ -151,7 +151,7 @@
|
||||
Publisher.objects.filter(blogs__id=1)
|
||||
|
||||
Publisher.objects.filter(blogs=publisher) # E: Incompatible type for lookup 'blogs': (got "Publisher", expected "Union[Blog, int, None]")
|
||||
Publisher.objects.filter(blogs__id=publisher) # E: Incompatible type for lookup 'blogs__id': (got "Publisher", expected "int")
|
||||
Publisher.objects.filter(blogs__id=publisher) # E: Incompatible type for lookup 'blogs__id': (got "Publisher", expected "Union[str, int]")
|
||||
installed_apps:
|
||||
- myapp
|
||||
files:
|
||||
|
||||
@@ -307,8 +307,18 @@
|
||||
- case: custom_manager_returns_proper_model_types
|
||||
main: |
|
||||
from myapp.models import User
|
||||
reveal_type(User.objects.get()) # N: Revealed type is 'myapp.models.User*'
|
||||
reveal_type(User.objects) # N: Revealed type is 'myapp.models.User_MyManager[myapp.models.User]'
|
||||
reveal_type(User.objects.select_related()) # N: Revealed type is 'myapp.models.User_MyManager[myapp.models.User]'
|
||||
reveal_type(User.objects.get()) # N: Revealed type is 'myapp.models.User*'
|
||||
reveal_type(User.objects.get_instance()) # N: Revealed type is 'builtins.int'
|
||||
reveal_type(User.objects.get_instance_untyped('hello')) # N: Revealed type is 'Any'
|
||||
|
||||
from myapp.models import ChildUser
|
||||
reveal_type(ChildUser.objects) # N: Revealed type is 'myapp.models.ChildUser_MyManager[myapp.models.ChildUser]'
|
||||
reveal_type(ChildUser.objects.select_related()) # N: Revealed type is 'myapp.models.ChildUser_MyManager[myapp.models.ChildUser]'
|
||||
reveal_type(ChildUser.objects.get()) # N: Revealed type is 'myapp.models.ChildUser*'
|
||||
reveal_type(ChildUser.objects.get_instance()) # N: Revealed type is 'builtins.int'
|
||||
reveal_type(ChildUser.objects.get_instance_untyped('hello')) # N: Revealed type is 'Any'
|
||||
installed_apps:
|
||||
- myapp
|
||||
files:
|
||||
@@ -317,6 +327,11 @@
|
||||
content: |
|
||||
from django.db import models
|
||||
class MyManager(models.Manager):
|
||||
pass
|
||||
def get_instance(self) -> int:
|
||||
pass
|
||||
def get_instance_untyped(self, name):
|
||||
pass
|
||||
class User(models.Model):
|
||||
objects = MyManager()
|
||||
class ChildUser(models.Model):
|
||||
objects = MyManager()
|
||||
|
||||
Reference in New Issue
Block a user