From 69d4ccaf5443a8124b870d1d685ae4ce9d5d547f Mon Sep 17 00:00:00 2001 From: Maxim Kurnikov Date: Mon, 4 Feb 2019 19:31:37 +0300 Subject: [PATCH] fixes, add some testing folders --- django-stubs/apps/registry.pyi | 3 +- django-stubs/contrib/auth/middleware.pyi | 4 +- .../contrib/auth/password_validation.pyi | 42 ++++----- django-stubs/contrib/auth/validators.pyi | 4 + django-stubs/contrib/messages/middleware.pyi | 7 +- .../contrib/sessions/backends/base.pyi | 10 +-- django-stubs/contrib/sessions/middleware.pyi | 6 +- django-stubs/contrib/staticfiles/finders.pyi | 24 ++++-- django-stubs/core/management/base.pyi | 13 +-- django-stubs/db/__init__.pyi | 2 + django-stubs/db/models/base.pyi | 2 +- django-stubs/db/models/query.pyi | 1 + django-stubs/db/transaction.pyi | 11 ++- django-stubs/forms/forms.pyi | 15 ++-- django-stubs/http/__init__.pyi | 7 +- django-stubs/http/request.pyi | 47 ++++------ django-stubs/http/response.pyi | 31 ++----- django-stubs/middleware/common.pyi | 17 ++-- django-stubs/shortcuts.pyi | 12 ++- django-stubs/template/backends/base.pyi | 6 +- django-stubs/template/loader.pyi | 1 + django-stubs/test/client.pyi | 73 ++++++++++++---- django-stubs/test/html.pyi | 14 +-- django-stubs/test/testcases.pyi | 9 +- django-stubs/test/utils.pyi | 14 ++- django-stubs/urls/resolvers.pyi | 4 + django-stubs/utils/datastructures.pyi | 80 +++++++---------- django-stubs/utils/translation/__init__.pyi | 2 + django-stubs/utils/translation/trans_real.pyi | 2 +- mypy_django_plugin/plugins/migrations.py | 7 +- scripts/typecheck_tests.py | 85 +++++++++++++++---- 31 files changed, 319 insertions(+), 236 deletions(-) create mode 100644 django-stubs/contrib/auth/validators.pyi diff --git a/django-stubs/apps/registry.pyi b/django-stubs/apps/registry.pyi index 5518fa8..b8ccef3 100644 --- a/django-stubs/apps/registry.pyi +++ b/django-stubs/apps/registry.pyi @@ -1,5 +1,5 @@ import collections -from typing import Any, Callable, List, Optional, Tuple, Type, Union, Iterable +from typing import Any, Callable, List, Optional, Tuple, Type, Union, Iterable, DefaultDict from django.db.migrations.state import AppConfigStub from django.db.models.base import Model @@ -12,6 +12,7 @@ class Apps: stored_app_configs: List[Any] = ... apps_ready: bool = ... loading: bool = ... + _pending_operations: DefaultDict[str, List] def __init__(self, installed_apps: Optional[Union[List[AppConfigStub], List[str], Tuple]] = ...) -> None: ... models_ready: bool = ... ready: bool = ... diff --git a/django-stubs/contrib/auth/middleware.pyi b/django-stubs/contrib/auth/middleware.pyi index d5c2c4f..aae3981 100644 --- a/django-stubs/contrib/auth/middleware.pyi +++ b/django-stubs/contrib/auth/middleware.pyi @@ -13,8 +13,8 @@ class AuthenticationMiddleware(MiddlewareMixin): class RemoteUserMiddleware(MiddlewareMixin): header: str = ... force_logout_if_no_header: bool = ... - def process_request(self, request: WSGIRequest) -> None: ... - def clean_username(self, username: str, request: WSGIRequest) -> str: ... + def process_request(self, request: HttpRequest) -> None: ... + def clean_username(self, username: str, request: HttpRequest) -> str: ... class PersistentRemoteUserMiddleware(RemoteUserMiddleware): force_logout_if_no_header: bool = ... diff --git a/django-stubs/contrib/auth/password_validation.pyi b/django-stubs/contrib/auth/password_validation.pyi index aafec41..c37ae11 100644 --- a/django-stubs/contrib/auth/password_validation.pyi +++ b/django-stubs/contrib/auth/password_validation.pyi @@ -1,46 +1,46 @@ -from pathlib import PosixPath -from typing import Any, Dict, List, Optional, Tuple, Union +from pathlib import Path, PosixPath +from typing import Any, List, Mapping, Optional, Protocol, Sequence, Set, Union from django.contrib.auth.base_user import AbstractBaseUser -from django.contrib.auth.models import User -def get_default_password_validators() -> Union[ - List[NumericPasswordValidator], List[UserAttributeSimilarityValidator] -]: ... -def get_password_validators( - validator_config: List[Dict[str, Union[Dict[str, int], str]]] -) -> Union[List[NumericPasswordValidator], List[UserAttributeSimilarityValidator]]: ... +class PasswordValidator(Protocol): + def validate(self, password: str, user: Optional[AbstractBaseUser] = ...): ... + +def get_default_password_validators() -> List[PasswordValidator]: ... +def get_password_validators(validator_config: Sequence[Mapping[str, Any]]) -> List[PasswordValidator]: ... def validate_password( - password: str, user: Optional[AbstractBaseUser] = ..., password_validators: Optional[List[Any]] = ... + password: str, + user: Optional[AbstractBaseUser] = ..., + password_validators: Optional[Sequence[PasswordValidator]] = ..., ) -> None: ... def password_changed( - password: str, user: Optional[AbstractBaseUser] = ..., password_validators: None = ... + password: str, + user: Optional[AbstractBaseUser] = ..., + password_validators: Optional[Sequence[PasswordValidator]] = ..., ) -> None: ... -def password_validators_help_texts(password_validators: Optional[List[Any]] = ...) -> List[str]: ... +def password_validators_help_texts(password_validators: Optional[Sequence[PasswordValidator]] = ...) -> List[str]: ... password_validators_help_text_html: Any class MinimumLengthValidator: min_length: int = ... def __init__(self, min_length: int = ...) -> None: ... - def validate(self, password: str, user: Optional[User] = ...) -> None: ... + def validate(self, password: str, user: Optional[AbstractBaseUser] = ...) -> None: ... def get_help_text(self) -> str: ... class UserAttributeSimilarityValidator: - DEFAULT_USER_ATTRIBUTES: Any = ... - user_attributes: Tuple[str, str, str, str] = ... + DEFAULT_USER_ATTRIBUTES: Sequence[str] = ... + user_attributes: Sequence[str] = ... max_similarity: float = ... - def __init__( - self, user_attributes: Union[List[str], Tuple[str, str, str, str]] = ..., max_similarity: float = ... - ) -> None: ... - def validate(self, password: str, user: Optional[User] = ...) -> None: ... + def __init__(self, user_attributes: Sequence[str] = ..., max_similarity: float = ...) -> None: ... + def validate(self, password: str, user: Optional[AbstractBaseUser] = ...) -> None: ... def get_help_text(self) -> str: ... class CommonPasswordValidator: - DEFAULT_PASSWORD_LIST_PATH: Any = ... + DEFAULT_PASSWORD_LIST_PATH: Path = ... passwords: Set[str] = ... def __init__(self, password_list_path: Union[PosixPath, str] = ...) -> None: ... - def validate(self, password: str, user: None = ...) -> None: ... + def validate(self, password: str, user: Optional[AbstractBaseUser] = ...) -> None: ... def get_help_text(self) -> str: ... class NumericPasswordValidator: diff --git a/django-stubs/contrib/auth/validators.pyi b/django-stubs/contrib/auth/validators.pyi new file mode 100644 index 0000000..bbb530f --- /dev/null +++ b/django-stubs/contrib/auth/validators.pyi @@ -0,0 +1,4 @@ +from django.core import validators + +class ASCIIUsernameValidator(validators.RegexValidator): ... +class UnicodeUsernameValidator(validators.RegexValidator): ... diff --git a/django-stubs/contrib/messages/middleware.pyi b/django-stubs/contrib/messages/middleware.pyi index df6a76e..427f8d0 100644 --- a/django-stubs/contrib/messages/middleware.pyi +++ b/django-stubs/contrib/messages/middleware.pyi @@ -1,8 +1,7 @@ -from django.core.handlers.wsgi import WSGIRequest from django.http.request import HttpRequest -from django.http.response import HttpResponseBase +from django.http.response import HttpResponse from django.utils.deprecation import MiddlewareMixin class MessageMiddleware(MiddlewareMixin): - def process_request(self, request: WSGIRequest) -> None: ... - def process_response(self, request: HttpRequest, response: HttpResponseBase) -> HttpResponseBase: ... + def process_request(self, request: HttpRequest) -> None: ... + def process_response(self, request: HttpRequest, response: HttpResponse) -> HttpResponse: ... diff --git a/django-stubs/contrib/sessions/backends/base.pyi b/django-stubs/contrib/sessions/backends/base.pyi index fc22726..58deb72 100644 --- a/django-stubs/contrib/sessions/backends/base.pyi +++ b/django-stubs/contrib/sessions/backends/base.pyi @@ -8,26 +8,18 @@ VALID_KEY_CHARS: Any class CreateError(Exception): ... class UpdateError(Exception): ... -class SessionBase: +class SessionBase(Dict[str, Any]): TEST_COOKIE_NAME: str = ... TEST_COOKIE_VALUE: str = ... accessed: bool = ... modified: bool = ... serializer: Any = ... def __init__(self, session_key: Optional[str] = ...) -> None: ... - def __contains__(self, key: str) -> bool: ... - def __getitem__(self, key: str) -> Any: ... - def __setitem__(self, key: str, value: Any) -> None: ... - def __delitem__(self, key: str) -> None: ... - def get(self, key: str, default: Optional[str] = ...) -> Any: ... - def pop(self, key: str, default: Any = ...) -> Any: ... - def setdefault(self, key: str, value: str) -> str: ... def set_test_cookie(self) -> None: ... def test_cookie_worked(self) -> bool: ... def delete_test_cookie(self) -> None: ... def encode(self, session_dict: Dict[str, Model]) -> str: ... def decode(self, session_data: Union[bytes, str]) -> Dict[str, Model]: ... - def update(self, dict_: Dict[str, int]) -> None: ... def has_key(self, key: Any): ... def keys(self): ... def values(self): ... diff --git a/django-stubs/contrib/sessions/middleware.pyi b/django-stubs/contrib/sessions/middleware.pyi index dbbd4e1..0e29b8d 100644 --- a/django-stubs/contrib/sessions/middleware.pyi +++ b/django-stubs/contrib/sessions/middleware.pyi @@ -1,11 +1,11 @@ from typing import Type from django.contrib.sessions.backends.base import SessionBase -from django.core.handlers.wsgi import WSGIRequest +from django.http.request import HttpRequest from django.http.response import HttpResponse from django.utils.deprecation import MiddlewareMixin class SessionMiddleware(MiddlewareMixin): SessionStore: Type[SessionBase] = ... - def process_request(self, request: WSGIRequest) -> None: ... - def process_response(self, request: WSGIRequest, response: HttpResponse) -> HttpResponse: ... + def process_request(self, request: HttpRequest) -> None: ... + def process_response(self, request: HttpRequest, response: HttpResponse) -> HttpResponse: ... diff --git a/django-stubs/contrib/staticfiles/finders.pyi b/django-stubs/contrib/staticfiles/finders.pyi index 7f61be6..aa5842a 100644 --- a/django-stubs/contrib/staticfiles/finders.pyi +++ b/django-stubs/contrib/staticfiles/finders.pyi @@ -1,8 +1,9 @@ -from typing import Any, Iterator, List, Optional, Tuple, Union +from typing import Any, Iterator, List, Optional, Tuple, Union, Mapping, overload from django.contrib.staticfiles.storage import StaticFilesStorage from django.core.checks.messages import Error -from django.core.files.storage import DefaultStorage, FileSystemStorage +from django.core.files.storage import DefaultStorage, FileSystemStorage, Storage +from typing_extensions import Literal searched_locations: Any @@ -13,7 +14,7 @@ class BaseFinder: class FileSystemFinder(BaseFinder): locations: List[Any] = ... - storages: collections.OrderedDict = ... + storages: Mapping[str, Any] = ... def __init__(self, app_names: None = ..., *args: Any, **kwargs: Any) -> None: ... def check(self, **kwargs: Any) -> List[Error]: ... def find(self, path: str, all: bool = ...) -> Union[List[str], str]: ... @@ -24,22 +25,27 @@ class AppDirectoriesFinder(BaseFinder): storage_class: Any = ... source_dir: str = ... apps: List[str] = ... - storages: collections.OrderedDict = ... + storages: Mapping[str, Any] = ... def __init__(self, app_names: None = ..., *args: Any, **kwargs: Any) -> None: ... def list(self, ignore_patterns: List[str]) -> Iterator[Tuple[str, FileSystemStorage]]: ... def find(self, path: str, all: bool = ...) -> Union[List[str], str]: ... def find_in_app(self, app: str, path: str) -> Optional[str]: ... class BaseStorageFinder(BaseFinder): - storage: Any = ... - def __init__(self, storage: Optional[StaticFilesStorage] = ..., *args: Any, **kwargs: Any) -> None: ... + storage: Storage = ... + def __init__(self, storage: Optional[Storage] = ..., *args: Any, **kwargs: Any) -> None: ... def find(self, path: str, all: bool = ...) -> Union[List[str], str]: ... def list(self, ignore_patterns: List[str]) -> Iterator[Tuple[str, DefaultStorage]]: ... -class DefaultStorageFinder(BaseStorageFinder): - storage: django.contrib.staticfiles.storage.StaticFilesStorage = ... - def __init__(self, *args: Any, **kwargs: Any) -> None: ... +class DefaultStorageFinder(BaseStorageFinder): ... def find(path: str, all: bool = ...) -> Optional[Union[List[str], str]]: ... def get_finders() -> Iterator[BaseFinder]: ... +@overload +def get_finder(import_path: Literal["django.contrib.staticfiles.finders.FileSystemFinder"]) -> FileSystemFinder: ... +@overload +def get_finder( + import_path: Literal["django.contrib.staticfiles.finders.AppDirectoriesFinder"] +) -> AppDirectoriesFinder: ... +@overload def get_finder(import_path: str) -> BaseFinder: ... diff --git a/django-stubs/core/management/base.pyi b/django-stubs/core/management/base.pyi index 866f895..6765fa6 100644 --- a/django-stubs/core/management/base.pyi +++ b/django-stubs/core/management/base.pyi @@ -1,6 +1,6 @@ from argparse import ArgumentParser, HelpFormatter, Namespace from io import StringIO, TextIOBase, TextIOWrapper -from typing import Any, Callable, List, Optional, Tuple, Union, Type +from typing import Any, Callable, List, Optional, Union from django.apps.config import AppConfig from django.core.management.color import Style @@ -9,17 +9,6 @@ class CommandError(Exception): ... class SystemCheckError(CommandError): ... class CommandParser(ArgumentParser): - add_help: bool - allow_abbrev: bool - argument_default: None - conflict_handler: str - description: str - epilog: None - formatter_class: Type[DjangoHelpFormatter] - fromfile_prefix_chars: None - prefix_chars: str - prog: str - usage: None missing_args_message: None = ... called_from_command_line: bool = ... def __init__(self, **kwargs: Any) -> None: ... diff --git a/django-stubs/db/__init__.pyi b/django-stubs/db/__init__.pyi index 3e5bec8..da5bdfb 100644 --- a/django-stubs/db/__init__.pyi +++ b/django-stubs/db/__init__.pyi @@ -12,6 +12,8 @@ from .utils import ( InternalError as InternalError, InterfaceError as InterfaceError, ConnectionHandler as ConnectionHandler, + Error as Error, + ConnectionDoesNotExist as ConnectionDoesNotExist, ) from . import migrations diff --git a/django-stubs/db/models/base.pyi b/django-stubs/db/models/base.pyi index 98b17d1..2cf36ab 100644 --- a/django-stubs/db/models/base.pyi +++ b/django-stubs/db/models/base.pyi @@ -11,7 +11,7 @@ class Model(metaclass=ModelBase): pass _meta: Any pk: Any = ... - objects: Manager[Model] + # objects: Manager[Model] def __init__(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: ... diff --git a/django-stubs/db/models/query.pyi b/django-stubs/db/models/query.pyi index eb2fbe8..655441d 100644 --- a/django-stubs/db/models/query.pyi +++ b/django-stubs/db/models/query.pyi @@ -21,6 +21,7 @@ from django.db.models.sql.query import Query, RawQuery from django.db import models from django.db.models import Manager +from django.db.models.query_utils import Q as Q _T = TypeVar("_T", bound=models.Model, covariant=True) diff --git a/django-stubs/db/transaction.pyi b/django-stubs/db/transaction.pyi index 2d65870..65172cb 100644 --- a/django-stubs/db/transaction.pyi +++ b/django-stubs/db/transaction.pyi @@ -1,5 +1,5 @@ from contextlib import ContextDecorator -from typing import Any, Callable, Optional, Union, ContextManager +from typing import Any, Callable, Optional, Union, Iterator, overload, ContextManager from django.db import ProgrammingError @@ -25,5 +25,12 @@ class Atomic(ContextDecorator): def __enter__(self) -> None: ... def __exit__(self, exc_type: None, exc_value: None, traceback: None) -> None: ... -def atomic(using: Optional[Union[Callable, str]] = ..., savepoint: bool = ...) -> ContextManager[Atomic]: ... +@overload +def atomic() -> Atomic: ... +@overload +def atomic(using: Optional[str] = ...,) -> ContextManager[Atomic]: ... +@overload +def atomic(using: Callable = ...) -> Callable: ... +@overload +def atomic(using: Optional[str] = ..., savepoint: bool = ...) -> ContextManager[Atomic]: ... def non_atomic_requests(using: Callable = ...) -> Callable: ... diff --git a/django-stubs/forms/forms.pyi b/django-stubs/forms/forms.pyi index 8a8723e..6b18a13 100644 --- a/django-stubs/forms/forms.pyi +++ b/django-stubs/forms/forms.pyi @@ -1,16 +1,15 @@ from collections import OrderedDict -from datetime import date, datetime -from typing import Any, Dict, Iterator, List, Optional, Tuple, Type, Union +from datetime import datetime +from typing import Any, Dict, Iterator, List, Mapping, Optional, Tuple, Type, Union -from django.core.exceptions import ValidationError +from django.core.exceptions import ValidationError as ValidationError +from django.core.files.base import File from django.core.files.uploadedfile import SimpleUploadedFile from django.db.models.query import QuerySet from django.forms.boundfield import BoundField from django.forms.fields import Field from django.forms.utils import ErrorDict, ErrorList from django.forms.widgets import Media, MediaDefiningClass -from django.http.request import QueryDict -from django.utils.datastructures import MultiValueDict from django.utils.safestring import SafeText class DeclarativeFieldsMetaclass(MediaDefiningClass): @@ -35,11 +34,11 @@ class BaseForm: renderer: Any = ... def __init__( self, - data: Optional[Union[Dict[str, Union[List[int], int, str]], Dict[str, Union[List[str], str]], QueryDict]] = ..., - files: Optional[Union[Dict[str, SimpleUploadedFile], MultiValueDict]] = ..., + data: Optional[Mapping[str, Any]] = ..., + files: Optional[Mapping[str, File]] = ..., auto_id: Optional[Union[bool, str]] = ..., prefix: Optional[str] = ..., - initial: Optional[Union[Dict[str, List[int]], Dict[str, date], Dict[str, str]]] = ..., + initial: Optional[Mapping[str, Any]] = ..., error_class: Type[ErrorList] = ..., label_suffix: None = ..., empty_permitted: bool = ..., diff --git a/django-stubs/http/__init__.pyi b/django-stubs/http/__init__.pyi index e870f5c..a8e4c86 100644 --- a/django-stubs/http/__init__.pyi +++ b/django-stubs/http/__init__.pyi @@ -1,4 +1,9 @@ -from .request import HttpRequest as HttpRequest, QueryDict as QueryDict +from .request import ( + HttpRequest as HttpRequest, + QueryDict as QueryDict, + RawPostDataException as RawPostDataException, + UnreadablePostError as UnreadablePostError, +) from .response import ( BadHeaderError as BadHeaderError, diff --git a/django-stubs/http/request.pyi b/django-stubs/http/request.pyi index 7b2c668..6a153f8 100644 --- a/django-stubs/http/request.pyi +++ b/django-stubs/http/request.pyi @@ -1,16 +1,14 @@ -# Stubs for django.http.request (Python 3.5) -# -# NOTE: This dynamically typed stub was automatically generated by stubgen. from io import BytesIO -from typing import Any, BinaryIO, Dict, Iterable, Iterator, List, Optional, overload, Pattern, Tuple, Union +from typing import Any, BinaryIO, Dict, Iterable, List, Mapping, Optional, Pattern, Tuple, Union, overload from django.contrib.sessions.backends.base import SessionBase -from django.core.files import uploadhandler, uploadedfile -from django.utils.datastructures import MultiValueDict, ImmutableList +from django.utils.datastructures import ImmutableList, MultiValueDict + +from django.core.files import uploadedfile, uploadhandler from django.urls import ResolverMatch -RAISE_ERROR = ... # type: object -host_validation_re = ... # type: Pattern +RAISE_ERROR: object = ... +host_validation_re: Pattern = ... class UnreadablePostError(IOError): ... class RawPostDataException(Exception): ... @@ -21,7 +19,7 @@ class HttpRequest(BytesIO): GET: QueryDict = ... POST: QueryDict = ... COOKIES: Dict[str, str] = ... - META: Dict[str, str] = ... + META: Dict[str, Any] = ... FILES: MultiValueDict[str, uploadedfile.UploadedFile] = ... path: str = ... path_info: str = ... @@ -30,6 +28,8 @@ class HttpRequest(BytesIO): content_type: Optional[str] = ... content_params: Optional[Dict[str, str]] = ... session: SessionBase + encoding: Optional[str] = ... + upload_handlers: UploadHandlerList = ... def __init__(self) -> None: ... def get_host(self) -> str: ... def get_port(self) -> str: ... @@ -44,38 +44,23 @@ class HttpRequest(BytesIO): def scheme(self) -> Optional[str]: ... def is_secure(self) -> bool: ... def is_ajax(self) -> bool: ... - encoding = ... # type: Optional[str] - upload_handlers = ... # type: UploadHandlerList def parse_file_upload( - self, META: Dict[str, str], post_data: BinaryIO - ) -> Tuple["QueryDict", MultiValueDict[str, uploadedfile.UploadedFile]]: ... + self, META: Mapping[str, Any], post_data: BinaryIO + ) -> Tuple[QueryDict, MultiValueDict[str, uploadedfile.UploadedFile]]: ... @property def body(self) -> bytes: ... - def close(self) -> None: ... - def read(self, *args: Any, **kwargs: Any) -> bytes: ... - def readline(self, *args: Any, **kwargs: Any) -> bytes: ... - def xreadlines(self) -> Iterator[bytes]: ... - def __iter__(self) -> Iterator[bytes]: ... - def readlines(self) -> List[bytes]: ... # type: ignore + def _load_post_and_files(self) -> None: ... class QueryDict(MultiValueDict[str, str]): - encoding = str # type: Any + encoding: Any = ... _mutable: bool = ... def __init__( - self, query_string: Union[str, bytes, None] = None, mutable: bool = False, encoding: Optional[str] = None + self, query_string: Optional[Union[str, bytes]] = ..., mutable: bool = ..., encoding: Optional[str] = ... ) -> None: ... - def __setitem__(self, key: str, value: str) -> None: ... - def __delitem__(self, key: str) -> None: ... - def __copy__(self) -> "QueryDict": ... - def __deepcopy__(self, memo: Dict[int, object]) -> "QueryDict": ... def setlist(self, key: str, list_: List[str]) -> None: ... - def setlistdefault(self, key: str, default_list: List[str] = None) -> List[str]: ... + def setlistdefault(self, key: str, default_list: Optional[List[str]] = ...) -> List[str]: ... def appendlist(self, key: str, value: str) -> None: ... - def popitem(self) -> Tuple[str, str]: ... - def clear(self) -> None: ... - def setdefault(self, key: str, default: Optional[str] = None) -> str: ... - def copy(self) -> "QueryDict": ... - def urlencode(self, safe: Optional[str] = None) -> str: ... + def urlencode(self, safe: Optional[str] = ...) -> str: ... @overload def bytes_to_text(s: bytes, encoding: str) -> str: ... diff --git a/django-stubs/http/response.pyi b/django-stubs/http/response.pyi index 4cf22a7..85afa0b 100644 --- a/django-stubs/http/response.pyi +++ b/django-stubs/http/response.pyi @@ -1,7 +1,7 @@ import datetime from io import BytesIO from json import JSONEncoder -from typing import Any, Dict, Iterable, Iterator, List, Optional, Tuple, Type, Union, overload +from typing import Any, Dict, Iterable, Iterator, List, Optional, Tuple, Type, Union, overload, AnyStr, IO import six from django.core.handlers.wsgi import WSGIRequest @@ -13,10 +13,9 @@ from django.urls import ResolverMatch class BadHeaderError(ValueError): ... -class HttpResponseBase(six.Iterator): +class HttpResponseBase(BytesIO): status_code: int = ... cookies: SimpleCookie = ... - closed: bool = ... reason_phrase: str = ... charset: str = ... def __init__( @@ -52,18 +51,9 @@ class HttpResponseBase(six.Iterator): def set_signed_cookie(self, key: str, value: str, salt: str = "", **kwargs: Any) -> None: ... def delete_cookie(self, key: str, path: str = "", domain: str = None) -> None: ... def make_bytes(self, value: object) -> bytes: ... - def close(self) -> None: ... - def write(self, content: object) -> None: ... - def flush(self) -> None: ... - def tell(self) -> int: ... - def readable(self) -> bool: ... - def seekable(self) -> bool: ... - def writable(self) -> bool: ... - def writelines(self, lines: Iterable[object]) -> None: ... class HttpResponse(HttpResponseBase): client: Client - closed: bool context: Optional[Context] csrf_cookie_set: bool redirect_chain: List[Tuple[str, int]] @@ -83,26 +73,23 @@ class HttpResponse(HttpResponseBase): def content(self) -> bytes: ... @content.setter def content(self, value: Any) -> None: ... - def __iter__(self) -> Iterator[bytes]: ... def tell(self) -> int: ... - def getvalue(self) -> bytes: ... - def writable(self) -> bool: ... @property def url(self) -> str: ... def json(self) -> Dict[str, Any]: ... class StreamingHttpResponse(HttpResponseBase): - def __init__(self, streaming_content: Iterable[bytes] = ..., *args: Any, **kwargs: Any) -> None: ... + def __init__(self, streaming_content: Iterable[AnyStr] = ..., *args: Any, **kwargs: Any) -> None: ... @property - def content(self) -> bytes: ... + def content(self) -> AnyStr: ... @content.setter - def content(self, value: Any) -> None: ... + def content(self, value: AnyStr) -> None: ... @property - def streaming_content(self) -> Iterator[bytes]: ... + def streaming_content(self) -> Iterator[AnyStr]: ... @streaming_content.setter - def streaming_content(self, value: Iterable[bytes]) -> None: ... - def __iter__(self) -> Iterator[bytes]: ... - def getvalue(self) -> bytes: ... + def streaming_content(self, value: Iterable[AnyStr]) -> None: ... + def __iter__(self) -> Iterator[AnyStr]: ... + def getvalue(self) -> AnyStr: ... class FileResponse(StreamingHttpResponse): client: Client diff --git a/django-stubs/middleware/common.pyi b/django-stubs/middleware/common.pyi index 0c09436..026433f 100644 --- a/django-stubs/middleware/common.pyi +++ b/django-stubs/middleware/common.pyi @@ -1,18 +1,17 @@ -from typing import Any, Optional, Callable +from typing import Any, Optional -from django.core.handlers.wsgi import WSGIRequest from django.http.request import HttpRequest -from django.http.response import HttpResponseBase, HttpResponseNotFound, HttpResponsePermanentRedirect +from django.http.response import HttpResponse, HttpResponseNotFound, HttpResponsePermanentRedirect from django.utils.deprecation import MiddlewareMixin class CommonMiddleware(MiddlewareMixin): response_redirect_class: Any = ... - def process_request(self, request: WSGIRequest) -> Optional[HttpResponsePermanentRedirect]: ... - def should_redirect_with_slash(self, request: WSGIRequest) -> bool: ... - def get_full_path_with_slash(self, request: WSGIRequest) -> str: ... - def process_response(self, request: HttpRequest, response: HttpResponseBase) -> HttpResponseBase: ... + def process_request(self, request: HttpRequest) -> Optional[HttpResponsePermanentRedirect]: ... + def should_redirect_with_slash(self, request: HttpRequest) -> bool: ... + def get_full_path_with_slash(self, request: HttpRequest) -> str: ... + def process_response(self, request: HttpRequest, response: HttpResponse) -> HttpResponse: ... class BrokenLinkEmailsMiddleware(MiddlewareMixin): - def process_response(self, request: WSGIRequest, response: HttpResponseNotFound) -> HttpResponseNotFound: ... + def process_response(self, request: HttpRequest, response: HttpResponseNotFound) -> HttpResponseNotFound: ... def is_internal_request(self, domain: str, referer: str) -> bool: ... - def is_ignorable_request(self, request: WSGIRequest, uri: str, domain: str, referer: str) -> bool: ... + def is_ignorable_request(self, request: HttpRequest, uri: str, domain: str, referer: str) -> bool: ... diff --git a/django-stubs/shortcuts.pyi b/django-stubs/shortcuts.pyi index 71b7b13..8892583 100644 --- a/django-stubs/shortcuts.pyi +++ b/django-stubs/shortcuts.pyi @@ -1,4 +1,4 @@ -from typing import Any, Callable, Dict, List, Optional, Type, Union, Sequence +from typing import Any, Callable, Dict, List, Optional, Type, Union, Sequence, Protocol from django.db.models import Manager, QuerySet from django.db.models.base import Model @@ -7,7 +7,7 @@ from django.http.response import HttpResponse, HttpResponseRedirect from django.http import HttpRequest def render_to_response( - template_name: str, + template_name: Union[str, Sequence[str]], context: Optional[Dict[str, Any]] = ..., content_type: Optional[str] = ..., status: Optional[int] = ..., @@ -21,7 +21,13 @@ def render( status: Optional[int] = ..., using: Optional[str] = ..., ) -> HttpResponse: ... -def redirect(to: Union[Callable, str], *args: Any, permanent: bool = ..., **kwargs: Any) -> HttpResponseRedirect: ... + +class SupportsGetAbsoluteUrl(Protocol): + pass + +def redirect( + to: Union[Callable, str, SupportsGetAbsoluteUrl], *args: Any, permanent: bool = ..., **kwargs: Any +) -> HttpResponseRedirect: ... def get_object_or_404(klass: Union[Type[Model], Manager, QuerySet], *args: Any, **kwargs: Any) -> Model: ... def get_list_or_404(klass: Union[Type[Model], Manager, QuerySet], *args: Any, **kwargs: Any) -> List[Model]: ... def resolve_url(to: Union[Callable, Model, str], *args: Any, **kwargs: Any) -> str: ... diff --git a/django-stubs/template/backends/base.pyi b/django-stubs/template/backends/base.pyi index f69d96f..bb12a08 100644 --- a/django-stubs/template/backends/base.pyi +++ b/django-stubs/template/backends/base.pyi @@ -1,4 +1,6 @@ -from typing import Any, Dict, Iterator, List, Optional, Tuple, Union +from typing import Any, Dict, Iterator, List, Tuple, Union + +from django.template.base import Template class BaseEngine: name: Any = ... @@ -7,7 +9,7 @@ class BaseEngine: def __init__(self, params: Dict[str, Union[List[str], bool, str]]) -> None: ... @property def app_dirname(self) -> None: ... - def from_string(self, template_code: Any) -> None: ... + def from_string(self, template_code: Any) -> Template: ... def get_template(self, template_name: Any) -> None: ... def template_dirs(self) -> Tuple[str]: ... def iter_template_filenames(self, template_name: str) -> Iterator[str]: ... diff --git a/django-stubs/template/loader.pyi b/django-stubs/template/loader.pyi index 9f51ce4..4d83c33 100644 --- a/django-stubs/template/loader.pyi +++ b/django-stubs/template/loader.pyi @@ -1,6 +1,7 @@ from typing import Dict, List, Optional, Union, Any from django.core.handlers.wsgi import WSGIRequest +from . import engines as engines def get_template(template_name: str, using: Optional[str] = ...) -> Any: ... def select_template(template_name_list: Union[List[str], str], using: Optional[str] = ...) -> Any: ... diff --git a/django-stubs/test/client.pyi b/django-stubs/test/client.pyi index 19995ab..c06bfbe 100644 --- a/django-stubs/test/client.pyi +++ b/django-stubs/test/client.pyi @@ -1,13 +1,15 @@ from io import BytesIO -from typing import Any, Dict, List, Optional, Union, Tuple, Type +from typing import Any, Dict, List, Optional, Pattern, Tuple, Type, Union from django.contrib.auth.models import User from django.contrib.sessions.backends.base import SessionBase from django.core.handlers.base import BaseHandler -from django.core.handlers.wsgi import WSGIRequest from django.core.serializers.json import DjangoJSONEncoder from django.http.cookie import SimpleCookie -from django.http.response import HttpResponseBase +from django.http.request import HttpRequest +from django.http.response import HttpResponse, HttpResponseBase + +CONTENT_TYPE_RE: Pattern class RedirectCycleError(Exception): last_response: HttpResponseBase = ... @@ -35,13 +37,13 @@ class RequestFactory: cookies: SimpleCookie = ... errors: BytesIO = ... def __init__(self, *, json_encoder: Any = ..., **defaults: Any) -> None: ... - def request(self, **request: Any) -> WSGIRequest: ... - def get(self, path: str, data: Any = ..., secure: bool = ..., **extra: Any) -> WSGIRequest: ... + def request(self, **request: Any) -> HttpRequest: ... + def get(self, path: str, data: Any = ..., secure: bool = ..., **extra: Any) -> HttpRequest: ... def post( self, path: str, data: Any = ..., content_type: str = ..., secure: bool = ..., **extra: Any - ) -> WSGIRequest: ... - def head(self, path: str, data: Any = ..., secure: bool = ..., **extra: Any) -> WSGIRequest: ... - def trace(self, path: str, secure: bool = ..., **extra: Any) -> WSGIRequest: ... + ) -> HttpRequest: ... + def head(self, path: str, data: Any = ..., secure: bool = ..., **extra: Any) -> HttpRequest: ... + def trace(self, path: str, secure: bool = ..., **extra: Any) -> HttpRequest: ... def options( self, path: str, @@ -49,16 +51,16 @@ class RequestFactory: content_type: str = ..., secure: bool = ..., **extra: Any - ) -> WSGIRequest: ... + ) -> HttpRequest: ... def put( self, path: str, data: Any = ..., content_type: str = ..., secure: bool = ..., **extra: Any - ) -> WSGIRequest: ... + ) -> HttpRequest: ... def patch( self, path: str, data: Any = ..., content_type: str = ..., secure: bool = ..., **extra: Any - ) -> WSGIRequest: ... + ) -> HttpRequest: ... def delete( self, path: str, data: Any = ..., content_type: str = ..., secure: bool = ..., **extra: Any - ) -> WSGIRequest: ... + ) -> HttpRequest: ... def generic( self, method: str, @@ -67,19 +69,54 @@ class RequestFactory: content_type: Optional[str] = ..., secure: bool = ..., **extra: Any - ) -> WSGIRequest: ... + ) -> HttpRequest: ... -class Client(RequestFactory): - defaults: Dict[str, str] - errors: BytesIO - json_encoder: Type[DjangoJSONEncoder] +class Client: + json_encoder: Type[DjangoJSONEncoder] = ... + defaults: Dict[str, str] = ... + cookies: SimpleCookie = ... + errors: BytesIO = ... handler: ClientHandler = ... exc_info: None = ... def __init__(self, enforce_csrf_checks: bool = ..., **defaults: Any) -> None: ... + def request(self, **request: Any) -> 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 options( + self, + path: str, + data: Union[Dict[str, str], str] = ..., + 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: ... + def generic( + self, + method: str, + path: str, + data: Any = ..., + content_type: Optional[str] = ..., + secure: bool = ..., + **extra: Any + ) -> HttpResponse: ... 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: ... - cookies: SimpleCookie = ... def logout(self) -> None: ... + +def conditional_content_removal(request: HttpRequest, response: HttpResponse) -> HttpResponse: ... diff --git a/django-stubs/test/html.pyi b/django-stubs/test/html.pyi index 1bc7bcd..c14f92c 100644 --- a/django-stubs/test/html.pyi +++ b/django-stubs/test/html.pyi @@ -1,5 +1,5 @@ from html.parser import HTMLParser -from typing import Any, List, Optional, Tuple, Union, TypeVar +from typing import Any, List, Optional, Tuple, Union, TypeVar, Iterable, Sequence _Self = TypeVar("_Self") @@ -7,20 +7,20 @@ WHITESPACE: Any def normalize_whitespace(string: str) -> str: ... +_ElementAttribute = Tuple[str, Optional[str]] + class Element: name: Optional[str] = ... - attributes: List[Tuple[str, Optional[str]]] = ... - children: List[Union[Element, str]] = ... - def __init__(self, name: Optional[str], attributes: Union[List[Tuple[str, Optional[str]]], Tuple]) -> None: ... + attributes: List[_ElementAttribute] = ... + children: List[Any] = ... + def __init__(self, name: Optional[str], attributes: Sequence[_ElementAttribute]) -> None: ... def append(self, element: Union[Element, str]) -> None: ... def finalize(self) -> None: ... def __contains__(self, element: Union[Element, str]) -> bool: ... def count(self, element: Union[Element, str]) -> int: ... - def __getitem__(self, key: int) -> Union[Element, str]: ... + def __getitem__(self, key: int) -> Any: ... class RootElement(Element): - attributes: List[Any] - children: List[Union[Element, str]] def __init__(self) -> None: ... class HTMLParseError(Exception): ... diff --git a/django-stubs/test/testcases.pyi b/django-stubs/test/testcases.pyi index 2488e0b..5a88add 100644 --- a/django-stubs/test/testcases.pyi +++ b/django-stubs/test/testcases.pyi @@ -5,15 +5,17 @@ from typing import Any, Callable, Dict, Iterator, List, Optional, Set, Tuple, Ty from django.core.exceptions import ImproperlyConfigured from django.core.handlers.wsgi import WSGIHandler -from django.core.servers.basehttp import WSGIRequestHandler, ThreadedWSGIServer +from django.core.servers.basehttp import ThreadedWSGIServer, WSGIRequestHandler from django.db.backends.sqlite3.base import DatabaseWrapper from django.db.models.base import Model from django.db.models.query import QuerySet, RawQuerySet from django.forms.fields import EmailField from django.http.response import HttpResponse, HttpResponseBase from django.template.base import Template -from django.test.utils import CaptureQueriesContext, modify_settings, override_settings, ContextList +from django.test.client import Client +from django.test.utils import CaptureQueriesContext, ContextList from django.utils.safestring import SafeText +from django.db import connections as connections class _AssertNumQueriesContext(CaptureQueriesContext): connection: Any @@ -55,6 +57,7 @@ class _CursorFailure: class SimpleTestCase(unittest.TestCase): client_class: Any = ... + client: Client allow_database_queries: bool = ... @classmethod def setUpClass(cls) -> None: ... @@ -233,3 +236,5 @@ class SerializeMixin: def setUpClass(cls) -> None: ... @classmethod def tearDownClass(cls) -> None: ... + +def connections_support_transactions() -> bool: ... diff --git a/django-stubs/test/utils.pyi b/django-stubs/test/utils.pyi index ca6130d..4a441a2 100644 --- a/django-stubs/test/utils.pyi +++ b/django-stubs/test/utils.pyi @@ -3,7 +3,7 @@ import warnings from io import StringIO from contextlib import contextmanager from decimal import Decimal -from typing import Any, Callable, Dict, Iterator, List, Optional, Set, Tuple, Type, Union, IO +from typing import Any, Callable, Dict, Iterator, List, Optional, Set, Tuple, Type, Union, IO, Iterable, Mapping from django.apps.registry import Apps from django.core.checks.registry import CheckRegistry @@ -118,3 +118,15 @@ def captured_stderr() -> Iterator[StringIO]: ... @contextmanager def freeze_time(t: float) -> Iterator[None]: ... def tag(*tags: str): ... + +_Signature = str +_TestDatabase = Tuple[str, List[str]] + +def dependency_ordered( + test_databases: Iterable[Tuple[_Signature, _TestDatabase]], dependencies: Mapping[str, List[str]] +) -> List[Tuple[_Signature, _TestDatabase]]: ... +def get_unique_databases_and_mirrors() -> Tuple[Dict[_Signature, _TestDatabase], Dict[str, Any]]: ... +def teardown_databases( + old_config: Iterable[Tuple[Any, str, bool]], verbosity: int, parallel: int = ..., keepdb: bool = ... +) -> None: ... +def require_jinja2(test_func: Callable) -> Callable: ... diff --git a/django-stubs/urls/resolvers.pyi b/django-stubs/urls/resolvers.pyi index 480fa21..6a8798b 100644 --- a/django-stubs/urls/resolvers.pyi +++ b/django-stubs/urls/resolvers.pyi @@ -84,6 +84,8 @@ class URLResolver: default_kwargs: Dict[str, Any] = ... namespace: Optional[str] = ... app_name: Optional[str] = ... + _local: Any + _reverse_dict: MultiValueDict def __init__( self, pattern: Any, @@ -101,3 +103,5 @@ class URLResolver: def resolve(self, path: str) -> ResolverMatch: ... def resolve_error_handler(self, view_type: int) -> Tuple[Callable, Dict[str, Any]]: ... def reverse(self, lookup_view: str, *args: Any, **kwargs: Any) -> str: ... + def _is_callback(self, name: str) -> bool: ... + def _populate(self) -> None: ... diff --git a/django-stubs/utils/datastructures.pyi b/django-stubs/utils/datastructures.pyi index f8656b5..b99239b 100644 --- a/django-stubs/utils/datastructures.pyi +++ b/django-stubs/utils/datastructures.pyi @@ -1,74 +1,58 @@ -# Stubs for django.utils.datastructures (Python 3.5) - -from collections import OrderedDict - from typing import ( Any, Callable, Dict, - Generic, Iterable, - Iterator, List, Mapping, MutableMapping, MutableSet, - Optional, - overload, Tuple, TypeVar, Union, + overload, + Iterator, ) -KT = TypeVar("KT") -VT = TypeVar("VT") +_K = TypeVar("_K") +_V = TypeVar("_V") -class OrderedSet(MutableSet[KT], Generic[KT]): - dict = ... # type: OrderedDict[KT, None] - def __init__(self, iterable: Iterable[KT] = None) -> None: ... - def add(self, item: KT) -> None: ... - def remove(self, item: KT) -> None: ... - def discard(self, item: KT) -> None: ... - def __contains__(self, item): ... - def __iter__(self): ... - def __len__(self): ... +class OrderedSet(MutableSet[_K]): + dict: Dict[_K, None] = ... + def __contains__(self, item: object) -> bool: ... + def __iter__(self) -> Iterator[_K]: ... + def __len__(self) -> int: ... + def add(self, x: _K) -> None: ... + def discard(self, item: _K) -> None: ... class MultiValueDictKeyError(KeyError): ... -class MultiValueDict(MutableMapping[KT, VT], Generic[KT, VT]): - def __init__(self, key_to_list_mapping: Iterable[Tuple[KT, List[VT]]] = ...) -> None: ... - def __copy__(self) -> "MultiValueDict[KT, VT]": ... - def __deepcopy__(self, memo: Dict[int, object]) -> "MultiValueDict[KT, VT]": ... - def __getitem__(self, key: KT) -> Union[VT, List[VT]]: ... # type: ignore - def pop(self, key: KT, default: List[VT] = None) -> List[VT]: ... # type: ignore - def __getstate__(self) -> Dict[str, Any]: ... - def __setstate__(self, obj_dict: Dict[str, Any]) -> None: ... - def get(self, key: KT, default: VT = None) -> Union[Optional[VT], List[VT]]: ... # type: ignore - def getlist(self, key: KT, default: List[VT] = None) -> List[VT]: ... - def setlist(self, key: KT, list_: List[VT]) -> None: ... - def setlistdefault(self, key: KT, default_list: List[VT] = None) -> List[VT]: ... - def appendlist(self, key: KT, value: VT) -> None: ... - def lists(self) -> Iterable[Tuple[KT, List[VT]]]: ... - def copy(self) -> "MultiValueDict[KT, VT]": ... - @overload # type: ignore - def update(self, args: Mapping[KT, VT]) -> None: ... +class MultiValueDict(MutableMapping[_K, List[_V]]): @overload - def update(self, *args: Mapping[KT, VT], **kwargs: Iterable[Tuple[KT, VT]]) -> None: ... # type: ignore - def dict(self) -> Dict[KT, Union[VT, List[VT]]]: ... + def __init__(self, key_to_list_mapping: Iterable[Tuple[_K, List[_V]]] = ...) -> None: ... + @overload + def __init__(self, key_to_list_mapping: Mapping[_K, List[_V]] = ...) -> None: ... + def getlist(self, key: _K, default: List[_V] = None) -> List[_V]: ... + def setlist(self, key: _K, list_: List[_V]) -> None: ... + def setlistdefault(self, key: _K, default_list: List[_V] = None) -> List[_V]: ... + def appendlist(self, key: _K, value: _V) -> None: ... + def lists(self) -> Iterable[Tuple[_K, List[_V]]]: ... + def dict(self) -> Dict[_K, List[_V]]: ... # These overrides are needed to convince mypy that this isn't an abstract class - def __delitem__(self, k: KT) -> None: ... - def __setitem__(self, k: KT, v: VT) -> None: ... + def __delitem__(self, item: _K) -> None: ... + def __getitem__(self, item: _K) -> List[_V]: ... + def __setitem__(self, k: _K, v: List[_V]) -> None: ... def __len__(self) -> int: ... - def __iter__(self) -> Iterator[KT]: ... + def __iter__(self) -> Iterator[_K]: ... -class ImmutableList(Tuple[VT, ...], Generic[VT]): - warning = ... # type: str +class ImmutableList(Tuple[_V, ...]): + warning: str = ... def complain(self, *wargs: Any, **kwargs: Any) -> None: ... -class DictWrapper(Dict[str, VT], Generic[VT]): - func = ... # type: Callable[[VT], VT] - prefix = ... # type: str +class DictWrapper(Dict[str, _V]): + func: Callable[[_V], _V] = ... + prefix: str = ... @overload - def __init__(self, data: Mapping[str, VT], func: Callable[[VT], VT], prefix: str) -> None: ... + def __init__(self, data: Mapping[str, _V], func: Callable[[_V], _V], prefix: str) -> None: ... @overload - def __init__(self, data: Iterable[Tuple[str, VT]], func: Callable[[VT], VT], prefix: str) -> None: ... + def __init__(self, data: Iterable[Tuple[str, _V]], func: Callable[[_V], _V], prefix: str) -> None: ... diff --git a/django-stubs/utils/translation/__init__.pyi b/django-stubs/utils/translation/__init__.pyi index 755b526..564935d 100644 --- a/django-stubs/utils/translation/__init__.pyi +++ b/django-stubs/utils/translation/__init__.pyi @@ -69,3 +69,5 @@ def get_language_from_request(request: WSGIRequest, check_path: bool = ...) -> s def templatize(src: str, **kwargs: Any) -> str: ... def deactivate_all() -> None: ... def get_language_info(lang_code: str) -> Any: ... + +from . import trans_real as trans_real diff --git a/django-stubs/utils/translation/trans_real.pyi b/django-stubs/utils/translation/trans_real.pyi index 533b795..f07e1b2 100644 --- a/django-stubs/utils/translation/trans_real.pyi +++ b/django-stubs/utils/translation/trans_real.pyi @@ -1,7 +1,7 @@ import gettext as gettext_module from collections import OrderedDict from gettext import NullTranslations -from typing import Any, List, Optional, Tuple +from typing import Any, List, Optional, Tuple, Callable from django.core.handlers.wsgi import WSGIRequest diff --git a/mypy_django_plugin/plugins/migrations.py b/mypy_django_plugin/plugins/migrations.py index 4be98bd..65500d4 100644 --- a/mypy_django_plugin/plugins/migrations.py +++ b/mypy_django_plugin/plugins/migrations.py @@ -24,8 +24,11 @@ def determine_model_cls_from_string_for_migrations(ctx: MethodContext) -> Type: if 'model_name' not in ctx.callee_arg_names: return ctx.default_return_type - model_name_expr = ctx.args[ctx.callee_arg_names.index('model_name')][0] - model_name = get_string_value_from_expr(model_name_expr) + model_name_expr_tuple = ctx.args[ctx.callee_arg_names.index('model_name')] + if not model_name_expr_tuple: + return ctx.default_return_type + + model_name = get_string_value_from_expr(model_name_expr_tuple[0]) if model_name is None: return ctx.default_return_type diff --git a/scripts/typecheck_tests.py b/scripts/typecheck_tests.py index d46ee79..7cef6b6 100644 --- a/scripts/typecheck_tests.py +++ b/scripts/typecheck_tests.py @@ -19,7 +19,7 @@ DJANGO_COMMIT_SHA = '03219b5f709dcd5b0bfacd963508625557ec1ef0' # Some errors occur for the test suite itself, and cannot be addressed via django-stubs. They should be ignored # using this constant. -MOCK_OBJECTS = ['MockRequest', 'MockCompiler', 'modelz'] +MOCK_OBJECTS = ['MockRequest', 'MockCompiler', 'modelz', 'call_count', 'call_args_list', 'call_args'] IGNORED_ERRORS = { '__common__': [ *MOCK_OBJECTS, @@ -35,7 +35,7 @@ IGNORED_ERRORS = { # settings re.compile(r'Module has no attribute "[A-Z_]+"'), # attributes assigned to test functions - re.compile(r'"Callable\[\[(Any(, )?)+(, VarArg\(Any\))?(, KwArg\(Any\))?\], Any\]" has no attribute'), + re.compile(r'"Callable\[\[(Any(, )?)*((, )?VarArg\(Any\))?((, )?KwArg\(Any\))?\], Any\]" has no attribute'), # assign empty tuple re.compile(r'Incompatible types in assignment \(expression has type "Tuple\[\]", ' r'variable has type "Tuple\[[A-Za-z, ]+\]"'), @@ -44,6 +44,9 @@ IGNORED_ERRORS = { 'Cannot infer type of lambda', re.compile(r'Incompatible types in assignment \(expression has type "Callable\[\[(Any(, )?)+\], Any\]", ' r'variable has type "Callable\['), + # cookies private attribute + 'has no attribute "_reserved"', + 'full_clean" of "Model" does not return a value' ], 'admin_changelist': [ 'Incompatible types in assignment (expression has type "FilteredChildAdmin", variable has type "ChildAdmin")' @@ -63,6 +66,17 @@ IGNORED_ERRORS = { 'aggregation_regress': [ 'Incompatible types in assignment (expression has type "List[str]", variable has type "QuerySet[Author]")' ], + 'apps': [ + 'Incompatible types in assignment (expression has type "str", target has type "type")', + '"Callable[[bool, bool], List[Type[Model]]]" has no attribute "cache_clear"' + ], + 'auth_tests': [ + '"PasswordValidator" has no attribute "min_length"', + '"validate_password" does not return a value', + '"password_changed" does not return a value', + re.compile(r'"validate" of "([A-Za-z]+)" does not return a value'), + 'Module has no attribute "SessionStore"' + ], 'basic': [ 'Unexpected keyword argument "unknown_kwarg" for "refresh_from_db" of "Model"', '"refresh_from_db" of "Model" defined here' @@ -120,11 +134,32 @@ IGNORED_ERRORS = { 'queryset_pickle': [ '"None" has no attribute "somefield"' ], + 'requests': [ + 'Incompatible types in assignment (expression has type "Dict[str, str]", variable has type "QueryDict")' + ], 'prefetch_related': [ 'Incompatible types in assignment (expression has type "List[Room]", variable has type "QuerySet[Room]")', '"None" has no attribute "__iter__"', 'has no attribute "read_by"' ], + 'signals': [ + 'Argument 1 to "append" of "list" has incompatible type "Tuple[Any, Any, Any, Any]"; expected "Tuple[Any, Any, Any]"' + ], + 'transactions': [ + 'Incompatible types in assignment (expression has type "Thread", variable has type "Callable[[], Any]")' + ], + 'test_client': [ + 'Incompatible types in assignment (expression has type "StreamingHttpResponse", variable has type "HttpResponse")', + ], + 'test_client_regress': [ + 'Incompatible types in assignment (expression has type "Dict[, ]", variable has type "SessionBase")' + ], + 'timezones': [ + 'Too few arguments for "render" of "Template"' + ], + 'test_runner': [ + 'Value of type "TestSuite" is not indexable' + ], 'urlpatterns': [ '"object" has no attribute "__iter__"; maybe "__str__" or "__dir__"? (not iterable)', '"object" not callable' @@ -132,9 +167,16 @@ IGNORED_ERRORS = { 'user_commands': [ 'Incompatible types in assignment (expression has type "Callable[[Any, KwArg(Any)], Any]", variable has type' ], + 'utils_tests': [ + re.compile(r'Argument ([1-9]) to "__get__" of "classproperty" has incompatible type') + ], + 'urlpatterns_reverse': [ + 'to "reverse" has incompatible type "object"', + 'Module has no attribute "_translations"', + "'django.urls.resolvers.ResolverMatch' object is not iterable" + ], 'sessions_tests': [ - 'base class "SessionTestsMixin" defined the type as "None")', - 'has no attribute "_reserved"' + 'base class "SessionTestsMixin" defined the type as "None")' ], 'select_related_onetoone': [ '"None" has no attribute' @@ -165,8 +207,8 @@ TESTS_DIRS = [ 'aggregation_regress', 'annotations', 'app_loading', - # TODO: 'apps', - # TODO: 'auth_tests' + 'apps', + # TODO: 'auth_tests', 'base', 'bash_completion', 'basic', @@ -297,10 +339,10 @@ TESTS_DIRS = [ 'queryset_pickle', 'raw_query', 'redirects_tests', - # TODO: 'requests', + 'requests', 'reserved_names', 'resolve_url', - # TODO: 'responses', + 'responses', 'reverse_lookup', 'save_delete_hooks', 'schema', @@ -313,8 +355,8 @@ TESTS_DIRS = [ 'sessions_tests', 'settings_tests', 'shell', - # TODO: 'shortcuts', - # TODO: 'signals', + 'shortcuts', + 'signals', 'signed_cookies_tests', # TODO: 'signing', # TODO: 'sitemaps_tests', @@ -328,23 +370,32 @@ TESTS_DIRS = [ # TODO: 'template_backends', 'template_loader', # TODO: 'template_tests', - # TODO: 'test_client', - # TODO: 'test_client_regress', + 'test_client', + 'test_client_regress', 'test_exceptions', # TODO: 'test_runner', 'test_runner_apps', - # TODO: 'test_utils', - # TODO: 'timezones', + 'test_utils', + 'timezones', 'transaction_hooks', - # TODO: 'transactions', + 'transactions', 'unmanaged_models', + + # wait for "allow redefinitions" here # TODO: 'update', + 'update_only_fields', 'urlpatterns', + + # not annotatable without annotation in test # TODO: 'urlpatterns_reverse', + 'user_commands', # TODO: 'utils_tests', + + # not annotatable without annotation in test # TODO: 'validation', + 'validators', 'version', 'view_tests', @@ -380,9 +431,9 @@ def is_ignored(line: str, test_folder_name: str) -> bool: def replace_with_clickable_location(error: str, abs_test_folder: Path) -> str: raw_path, _, error_line = error.partition(': ') - fname, line_number = raw_path.split(':') + fname, _,line_number = raw_path.partition(':') path = abs_test_folder.joinpath(fname).relative_to(PROJECT_DIRECTORY) - clickable_location = f'./{path}:{line_number}' + clickable_location = f'./{path}:{line_number or 1}' return error.replace(raw_path, clickable_location)