From 628c1224d63f6a3fa7905af7fd1219f2a0da3680 Mon Sep 17 00:00:00 2001 From: Maxim Kurnikov Date: Wed, 30 Jan 2019 15:56:59 +0300 Subject: [PATCH] enable test typechecking for a bunch of django test suite folders --- django-stubs/apps/config.pyi | 6 +- django-stubs/contrib/admin/checks.pyi | 14 +-- django-stubs/contrib/admin/options.pyi | 12 +-- django-stubs/contrib/admin/sites.pyi | 2 +- django-stubs/contrib/auth/base_user.pyi | 4 +- django-stubs/contrib/auth/hashers.pyi | 99 ++++--------------- django-stubs/contrib/contenttypes/admin.pyi | 14 ++- django-stubs/core/checks/__init__.pyi | 2 + django-stubs/db/models/__init__.pyi | 11 +++ django-stubs/db/models/base.pyi | 2 +- django-stubs/db/models/expressions.pyi | 37 ++++--- django-stubs/db/models/fields/__init__.pyi | 4 + django-stubs/db/models/fields/related.pyi | 1 + django-stubs/db/models/functions/__init__.pyi | 21 +++- django-stubs/db/models/manager.pyi | 2 + django-stubs/db/models/query.pyi | 12 ++- django-stubs/forms/__init__.pyi | 26 ++++- django-stubs/forms/widgets.pyi | 1 + django-stubs/http/request.pyi | 24 ++--- django-stubs/test/__init__.pyi | 10 +- django-stubs/test/testcases.pyi | 1 + django-stubs/test/utils.pyi | 12 ++- mypy_django_plugin/plugins/migrations.py | 3 + scripts/typecheck_tests.py | 50 ++++++++++ 24 files changed, 229 insertions(+), 141 deletions(-) diff --git a/django-stubs/apps/config.pyi b/django-stubs/apps/config.pyi index 9b2711b..fe3ec40 100644 --- a/django-stubs/apps/config.pyi +++ b/django-stubs/apps/config.pyi @@ -1,4 +1,4 @@ -from typing import Any, Iterator, Type +from typing import Any, Iterator, Type, Optional from django.db.models.base import Model @@ -6,14 +6,14 @@ MODELS_MODULE_NAME: str class AppConfig: name: str = ... - module: Any = ... + module: Optional[Any] = ... apps: None = ... label: str = ... verbose_name: str = ... path: str = ... models_module: None = ... models: None = ... - def __init__(self, app_name: str, app_module: None) -> None: ... + def __init__(self, app_name: str, app_module: Optional[Any]) -> None: ... @classmethod def create(cls, entry: str) -> AppConfig: ... def get_model(self, model_name: str, require_ready: bool = ...) -> Type[Model]: ... diff --git a/django-stubs/contrib/admin/checks.pyi b/django-stubs/contrib/admin/checks.pyi index bcadc94..9143c36 100644 --- a/django-stubs/contrib/admin/checks.pyi +++ b/django-stubs/contrib/admin/checks.pyi @@ -1,19 +1,21 @@ -from typing import Any, List +from typing import Any, List, Union from django.contrib.admin.options import BaseModelAdmin, InlineModelAdmin, ModelAdmin from django.core.checks.messages import Error -def check_admin_app(app_configs: None, **kwargs: Any) -> List[str]: ... -def check_dependencies(**kwargs: Any) -> List[Error]: ... +_CheckError = Union[str, Error] + +def check_admin_app(app_configs: None, **kwargs: Any) -> List[_CheckError]: ... +def check_dependencies(**kwargs: Any) -> List[_CheckError]: ... class BaseModelAdminChecks: - def check(self, admin_obj: BaseModelAdmin, **kwargs: Any) -> List[Error]: ... + def check(self, admin_obj: BaseModelAdmin, **kwargs: Any) -> List[_CheckError]: ... class ModelAdminChecks(BaseModelAdminChecks): - def check(self, admin_obj: ModelAdmin, **kwargs: Any) -> List[Error]: ... + def check(self, admin_obj: ModelAdmin, **kwargs: Any) -> List[_CheckError]: ... class InlineModelAdminChecks(BaseModelAdminChecks): - def check(self, inline_obj: InlineModelAdmin, **kwargs: Any) -> List[Any]: ... + def check(self, inline_obj: InlineModelAdmin, **kwargs: Any) -> List[_CheckError]: ... def must_be(type: Any, option: Any, obj: Any, id: Any): ... def must_inherit_from(parent: Any, option: Any, obj: Any, id: Any): ... diff --git a/django-stubs/contrib/admin/options.pyi b/django-stubs/contrib/admin/options.pyi index 16bd3ab..e012b05 100644 --- a/django-stubs/contrib/admin/options.pyi +++ b/django-stubs/contrib/admin/options.pyi @@ -1,7 +1,6 @@ from collections import OrderedDict -from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Type, Union, Sequence +from typing import Any, Callable, Dict, List, Optional, Sequence, Set, Tuple, Type, Union -from django.contrib.admin.filters import SimpleListFilter from django.contrib.admin.models import LogEntry from django.contrib.admin.sites import AdminSite from django.contrib.admin.views.main import ChangeList @@ -11,18 +10,17 @@ from django.core.checks.messages import Error from django.core.handlers.wsgi import WSGIRequest from django.core.paginator import Paginator from django.db.models.base import Model -from django.forms.fields import TypedChoiceField - -from django.db.models.fields import Field from django.db.models.fields.related import ForeignKey, ManyToManyField, RelatedField +from django.db.models.options import Options from django.db.models.query import QuerySet +from django.forms.fields import TypedChoiceField from django.forms.models import ModelChoiceField, ModelMultipleChoiceField from django.forms.widgets import Media from django.http.response import HttpResponse, HttpResponseBase, HttpResponseRedirect, JsonResponse from django.urls.resolvers import URLPattern from django.utils.safestring import SafeText -from django.db.models.options import Options +from django.db.models.fields import Field IS_POPUP_VAR: str TO_FIELD_VAR: str @@ -55,7 +53,7 @@ class BaseModelAdmin: view_on_site: bool = ... show_full_result_count: bool = ... checks_class: Any = ... - def check(self, **kwargs: Any) -> List[Error]: ... + def check(self, **kwargs: Any) -> List[Union[str, Error]]: ... def __init__(self) -> None: ... def formfield_for_dbfield(self, db_field: Field, request: WSGIRequest, **kwargs: Any) -> Optional[Field]: ... def formfield_for_choice_field(self, db_field: Field, request: WSGIRequest, **kwargs: Any) -> TypedChoiceField: ... diff --git a/django-stubs/contrib/admin/sites.pyi b/django-stubs/contrib/admin/sites.pyi index fc9e450..868fe57 100644 --- a/django-stubs/contrib/admin/sites.pyi +++ b/django-stubs/contrib/admin/sites.pyi @@ -28,7 +28,7 @@ class AdminSite: name: str = ... _registry: Dict[Type[Model], ModelAdmin] def __init__(self, name: str = ...) -> None: ... - def check(self, app_configs: None) -> List[str]: ... + def check(self, app_configs: None) -> List[Any]: ... def register( self, model_or_iterable: Union[List[Type[Model]], Tuple[Type[Model]], Type[Model]], diff --git a/django-stubs/contrib/auth/base_user.pyi b/django-stubs/contrib/auth/base_user.pyi index 287d93b..268e992 100644 --- a/django-stubs/contrib/auth/base_user.pyi +++ b/django-stubs/contrib/auth/base_user.pyi @@ -11,10 +11,8 @@ class BaseUserManager(models.Manager): class AbstractBaseUser(models.Model): password: models.CharField = ... last_login: Optional[models.DateTimeField] = ... - is_active: bool = ... + is_active: models.BooleanField = ... REQUIRED_FIELDS: List[str] = ... - class Meta: - abstract: bool = ... def get_username(self) -> str: ... def clean(self) -> None: ... def save(self, *args: Any, **kwargs: Any) -> None: ... diff --git a/django-stubs/contrib/auth/hashers.pyi b/django-stubs/contrib/auth/hashers.pyi index 2fc0f3c..d5bd9ad 100644 --- a/django-stubs/contrib/auth/hashers.pyi +++ b/django-stubs/contrib/auth/hashers.pyi @@ -1,4 +1,3 @@ -from collections import OrderedDict from typing import Any, Callable, Dict, List, Optional UNUSABLE_PASSWORD_PREFIX: str @@ -17,92 +16,30 @@ def identify_hasher(encoded: str) -> BasePasswordHasher: ... def mask_hash(hash: str, show: int = ..., char: str = ...) -> str: ... class BasePasswordHasher: - algorithm: Any = ... - library: Any = ... + algorithm: str = ... + library: str = ... + rounds: int = ... + time_cost: int = ... + memory_cost: int = ... + parallelism: int = ... + digest: Any = ... + iterations: Optional[int] = ... def salt(self) -> str: ... - def verify(self, password: str, encoded: str) -> Any: ... + def verify(self, password: str, encoded: str) -> bool: ... def encode(self, password: str, salt: str) -> Any: ... def safe_summary(self, encoded: str) -> Any: ... def must_update(self, encoded: str) -> bool: ... def harden_runtime(self, password: str, encoded: str) -> None: ... class PBKDF2PasswordHasher(BasePasswordHasher): - algorithm: str = ... - iterations: int = ... - digest: Any = ... def encode(self, password: str, salt: str, iterations: Optional[int] = ...) -> str: ... - def verify(self, password: str, encoded: str) -> bool: ... - def safe_summary(self, encoded: str) -> OrderedDict: ... - def must_update(self, encoded: str) -> bool: ... - def harden_runtime(self, password: str, encoded: str) -> None: ... -class PBKDF2SHA1PasswordHasher(PBKDF2PasswordHasher): - algorithm: str = ... - digest: Any = ... - -class Argon2PasswordHasher(BasePasswordHasher): - algorithm: str = ... - library: str = ... - time_cost: int = ... - memory_cost: int = ... - parallelism: int = ... - def encode(self, password: Any, salt: Any): ... - def verify(self, password: Any, encoded: Any): ... - def safe_summary(self, encoded: Any): ... - def must_update(self, encoded: Any): ... - def harden_runtime(self, password: Any, encoded: Any) -> None: ... - -class BCryptSHA256PasswordHasher(BasePasswordHasher): - algorithm: str = ... - digest: Any = ... - library: Any = ... - rounds: int = ... - def salt(self): ... - def encode(self, password: Any, salt: Any): ... - def verify(self, password: Any, encoded: Any): ... - def safe_summary(self, encoded: Any): ... - def must_update(self, encoded: Any): ... - def harden_runtime(self, password: Any, encoded: Any) -> None: ... - -class BCryptPasswordHasher(BCryptSHA256PasswordHasher): - algorithm: str = ... - digest: Any = ... - -class SHA1PasswordHasher(BasePasswordHasher): - algorithm: str = ... - def encode(self, password: str, salt: str) -> str: ... - def verify(self, password: str, encoded: str) -> bool: ... - def safe_summary(self, encoded: Any): ... - def harden_runtime(self, password: Any, encoded: Any) -> None: ... - -class MD5PasswordHasher(BasePasswordHasher): - algorithm: str = ... - def encode(self, password: str, salt: str) -> str: ... - def verify(self, password: str, encoded: str) -> bool: ... - def safe_summary(self, encoded: str) -> OrderedDict: ... - def harden_runtime(self, password: Any, encoded: Any) -> None: ... - -class UnsaltedSHA1PasswordHasher(BasePasswordHasher): - algorithm: str = ... - def salt(self) -> str: ... - def encode(self, password: str, salt: str) -> str: ... - def verify(self, password: str, encoded: str) -> bool: ... - def safe_summary(self, encoded: Any): ... - def harden_runtime(self, password: Any, encoded: Any) -> None: ... - -class UnsaltedMD5PasswordHasher(BasePasswordHasher): - algorithm: str = ... - def salt(self) -> str: ... - def encode(self, password: str, salt: str) -> str: ... - def verify(self, password: str, encoded: str) -> bool: ... - def safe_summary(self, encoded: Any): ... - def harden_runtime(self, password: Any, encoded: Any) -> None: ... - -class CryptPasswordHasher(BasePasswordHasher): - algorithm: str = ... - library: str = ... - def salt(self): ... - def encode(self, password: Any, salt: Any): ... - def verify(self, password: Any, encoded: Any): ... - def safe_summary(self, encoded: Any): ... - def harden_runtime(self, password: Any, encoded: Any) -> None: ... +class PBKDF2SHA1PasswordHasher(PBKDF2PasswordHasher): ... +class Argon2PasswordHasher(BasePasswordHasher): ... +class BCryptSHA256PasswordHasher(BasePasswordHasher): ... +class BCryptPasswordHasher(BCryptSHA256PasswordHasher): ... +class SHA1PasswordHasher(BasePasswordHasher): ... +class MD5PasswordHasher(BasePasswordHasher): ... +class UnsaltedSHA1PasswordHasher(BasePasswordHasher): ... +class UnsaltedMD5PasswordHasher(BasePasswordHasher): ... +class CryptPasswordHasher(BasePasswordHasher): ... diff --git a/django-stubs/contrib/contenttypes/admin.pyi b/django-stubs/contrib/contenttypes/admin.pyi index 72ce1a8..b4ca562 100644 --- a/django-stubs/contrib/contenttypes/admin.pyi +++ b/django-stubs/contrib/contenttypes/admin.pyi @@ -1,6 +1,14 @@ -from django.db.models.base import Model from typing import Any, List, Type +from django.contrib.admin.options import InlineModelAdmin +from django.db.models.base import Model + class GenericInlineModelAdminChecks: - def _check_exclude_of_parent_model(self, obj: GenericTabularInline, parent_model: Type[Model]) -> List[Any]: ... - def _check_relation(self, obj: GenericTabularInline, parent_model: Type[Model]) -> List[Any]: ... + def _check_exclude_of_parent_model(self, obj: GenericInlineModelAdmin, parent_model: Type[Model]) -> List[Any]: ... + def _check_relation(self, obj: GenericInlineModelAdmin, parent_model: Type[Model]) -> List[Any]: ... + +class GenericInlineModelAdmin(InlineModelAdmin): + template: str = ... + +class GenericStackedInline(GenericInlineModelAdmin): ... +class GenericTabularInline(GenericInlineModelAdmin): ... diff --git a/django-stubs/core/checks/__init__.pyi b/django-stubs/core/checks/__init__.pyi index 33a4a0d..c1ac36c 100644 --- a/django-stubs/core/checks/__init__.pyi +++ b/django-stubs/core/checks/__init__.pyi @@ -1 +1,3 @@ from .messages import Warning as Warning, Info as Info, Debug as Debug, Error as Error, Critical as Critical + +from .registry import run_checks as run_checks diff --git a/django-stubs/db/models/__init__.pyi b/django-stubs/db/models/__init__.pyi index 691d945..df8b5aa 100644 --- a/django-stubs/db/models/__init__.pyi +++ b/django-stubs/db/models/__init__.pyi @@ -64,8 +64,19 @@ from .expressions import ( RawSQL as RawSQL, Value as Value, Func as Func, + ExpressionWrapper as ExpressionWrapper, ) from .manager import BaseManager as BaseManager, Manager as Manager from . import lookups as lookups + +from .aggregates import ( + Avg as Avg, + Min as Min, + Max as Max, + Variance as Variance, + StdDev as StdDev, + Sum as Sum, + Aggregate as Aggregate, +) diff --git a/django-stubs/db/models/base.pyi b/django-stubs/db/models/base.pyi index d8b4787..1a64205 100644 --- a/django-stubs/db/models/base.pyi +++ b/django-stubs/db/models/base.pyi @@ -9,7 +9,7 @@ class Model(metaclass=ModelBase): pass pk: Any = ... objects: Manager[Model] - def __init__(self, **kwargs) -> None: ... + 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: ... def clean_fields(self, exclude: List[str] = ...) -> None: ... diff --git a/django-stubs/db/models/expressions.pyi b/django-stubs/db/models/expressions.pyi index 04d18c5..76b2ad9 100644 --- a/django-stubs/db/models/expressions.pyi +++ b/django-stubs/db/models/expressions.pyi @@ -1,13 +1,15 @@ from collections import OrderedDict from datetime import datetime, timedelta -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, Sequence -from django.db.models import QuerySet +from django.db.models import QuerySet, Q from django.db.models.fields import Field from django.db.models.lookups import Lookup from django.db.models.sql import Query from django.db.models.sql.compiler import SQLCompiler +_OutputField = Union[Field, str] + class SQLiteNumericMixin: def as_sqlite(self, compiler: SQLCompiler, connection: Any, **extra_context: Any) -> Tuple[str, List[float]]: ... @@ -26,9 +28,10 @@ class Combinable: def __add__(self, other: Optional[Union[timedelta, Combinable, float, str]]) -> CombinedExpression: ... def __sub__(self, other: Union[timedelta, Combinable, float]) -> CombinedExpression: ... def __mul__(self, other: Union[timedelta, Combinable, float]) -> CombinedExpression: ... - def __truediv__(self, other: float) -> CombinedExpression: ... - def __mod__(self, other: int) -> CombinedExpression: ... - def __pow__(self, other: float) -> CombinedExpression: ... + def __truediv__(self, other: Union[Combinable, float]) -> Combinable: ... + def __itruediv__(self, other: Union[Combinable, float]) -> Combinable: ... + def __mod__(self, other: int) -> Combinable: ... + def __pow__(self, other: float) -> Combinable: ... def __and__(self, other: Combinable) -> Any: ... def bitand(self, other: int) -> CombinedExpression: ... def bitleftshift(self, other: int) -> CombinedExpression: ... @@ -48,11 +51,11 @@ class BaseExpression: is_summary: bool = ... filterable: bool = ... window_compatible: bool = ... - def __init__(self, output_field: Optional[Union[Field, str]] = ...) -> None: ... + def __init__(self, output_field: Optional[_OutputField] = ...) -> None: ... def get_db_converters(self, connection: Any) -> List[Callable]: ... def get_source_expressions(self) -> List[Any]: ... def set_source_expressions(self, exprs: List[Any]) -> None: ... - def as_sql(self, compiler: Any, connection: Any) -> None: ... + def as_sql(self, compiler: Any, connection: Any, **kwargs) -> None: ... def contains_aggregate(self) -> bool: ... def contains_over_clause(self) -> bool: ... def contains_column_references(self) -> bool: ... @@ -87,10 +90,11 @@ class CombinedExpression(SQLiteNumericMixin, Expression): connector: Any = ... lhs: Any = ... rhs: Any = ... - def __init__(self, lhs: Combinable, connector: str, rhs: Combinable, output_field: None = ...) -> None: ... + def __init__( + self, lhs: Combinable, connector: str, rhs: Combinable, output_field: Optional[_OutputField] = ... + ) -> None: ... def get_source_expressions(self) -> Union[List[Combinable], List[SQLiteNumericMixin]]: ... def set_source_expressions(self, exprs: List[Combinable]) -> None: ... - def as_sql(self, compiler: SQLCompiler, connection: Any) -> Any: ... def resolve_expression( self, query: Any = ..., @@ -120,7 +124,7 @@ class Subquery(Expression): template: str = ... queryset: QuerySet = ... extra: Dict[Any, Any] = ... - def __init__(self, queryset: QuerySet, output_field: Optional[Field] = ..., **extra: Any) -> None: ... + def __init__(self, queryset: QuerySet, output_field: Optional[_OutputField] = ..., **extra: Any) -> None: ... class Exists(Subquery): extra: Dict[Any, Any] @@ -141,13 +145,13 @@ class OrderBy(BaseExpression): class Value(Expression): value: Any = ... - def __init__(self, value: Any, output_field: Optional[Field] = ...) -> None: ... + def __init__(self, value: Any, output_field: Optional[_OutputField] = ...) -> None: ... class RawSQL(Expression): output_field: Field params: List[Any] sql: str - def __init__(self, sql: str, params: Union[List[int], List[str], Tuple], output_field: None = ...) -> None: ... + def __init__(self, sql: str, params: Sequence[Any], output_field: Optional[_OutputField] = ...) -> None: ... class Func(SQLiteNumericMixin, Expression): function: Any = ... @@ -156,7 +160,7 @@ class Func(SQLiteNumericMixin, Expression): arity: Any = ... source_expressions: List[Expression] = ... extra: Any = ... - def __init__(self, *expressions: Any, output_field: Optional[Any] = ..., **extra: Any) -> None: ... + def __init__(self, *expressions: Any, output_field: Optional[_OutputField] = ..., **extra: Any) -> None: ... def get_source_expressions(self) -> List[Combinable]: ... def set_source_expressions(self, exprs: List[Expression]) -> None: ... def resolve_expression( @@ -182,5 +186,10 @@ class Case(Expression): default: Any = ... extra: Any = ... def __init__( - self, *cases: Any, default: Optional[Any] = ..., output_field: Optional[Any] = ..., **extra: Any + self, *cases: Any, default: Optional[Any] = ..., output_field: Optional[_OutputField] = ..., **extra: Any ) -> None: ... + +class ExpressionWrapper(Expression): + def __init__(self, expression: Union[Expression, Q], output_field: _OutputField): ... + def set_source_expressions(self, exprs: Sequence[Expression]) -> None: ... + def get_source_expressions(self) -> List[Expression]: ... diff --git a/django-stubs/db/models/fields/__init__.pyi b/django-stubs/db/models/fields/__init__.pyi index a2d64c9..941e341 100644 --- a/django-stubs/db/models/fields/__init__.pyi +++ b/django-stubs/db/models/fields/__init__.pyi @@ -2,6 +2,8 @@ from typing import Any, Optional, Tuple, Iterable, Callable, Dict, Union from django.db.models.query_utils import RegisterLookupMixin +from django.forms.widgets import Widget + _Choice = Tuple[Any, str] _ChoiceNamedGroup = Tuple[str, Iterable[_Choice]] _FieldChoices = Iterable[Union[_Choice, _ChoiceNamedGroup]] @@ -10,6 +12,8 @@ _ValidatorCallable = Callable[..., None] _ErrorMessagesToOverride = Dict[str, Any] class Field(RegisterLookupMixin): + widget: Widget + help_text: str def __init__( self, verbose_name: Optional[str] = ..., diff --git a/django-stubs/db/models/fields/related.pyi b/django-stubs/db/models/fields/related.pyi index 79c2181..f656648 100644 --- a/django-stubs/db/models/fields/related.pyi +++ b/django-stubs/db/models/fields/related.pyi @@ -81,6 +81,7 @@ class ManyToManyField(RelatedField, Generic[_T]): m2m_reverse_field_name: Any = ... m2m_target_field_name: Any = ... m2m_reverse_target_field_name: Any = ... + def contribute_to_class(self, cls: Type[Model], name: str, **kwargs) -> None: ... def contribute_to_related_class(self, cls: Type[Model], related: RelatedField) -> None: ... def set_attributes_from_rel(self) -> None: ... def value_from_object(self, obj: Model) -> List[Model]: ... diff --git a/django-stubs/db/models/functions/__init__.pyi b/django-stubs/db/models/functions/__init__.pyi index 88f42c8..4d1d7eb 100644 --- a/django-stubs/db/models/functions/__init__.pyi +++ b/django-stubs/db/models/functions/__init__.pyi @@ -1 +1,20 @@ -from .text import Lower as Lower, Upper as Upper +from .text import ( + Lower as Lower, + Upper as Upper, + Length as Length, + Chr as Chr, + Concat as Concat, + ConcatPair as ConcatPair, + Left as Left, + Right as Right, + LPad as LPad, + RPad as RPad, + LTrim as LTrim, + RTrim as RTrim, + Trim as Trim, + Ord as Ord, + Repeat as Repeat, + StrIndex as StrIndex, + Replace as Replace, + Substr as Substr, +) diff --git a/django-stubs/db/models/manager.pyi b/django-stubs/db/models/manager.pyi index 28bdaab..f0ba75b 100644 --- a/django-stubs/db/models/manager.pyi +++ b/django-stubs/db/models/manager.pyi @@ -17,6 +17,8 @@ class BaseManager(QuerySet[_T]): def check(self, **kwargs: Any) -> List[Any]: ... @classmethod def from_queryset(cls, queryset_class: Any, class_name: Optional[Any] = ...): ... + @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 get_queryset(self) -> QuerySet[_T]: ... diff --git a/django-stubs/db/models/query.pyi b/django-stubs/db/models/query.pyi index 6ee30aa..b98d061 100644 --- a/django-stubs/db/models/query.pyi +++ b/django-stubs/db/models/query.pyi @@ -42,18 +42,17 @@ class QuerySet(Iterable[_T], Sized): def latest(self, *fields: Any, field_name: Optional[Any] = ...) -> _T: ... def first(self) -> Optional[_T]: ... def last(self) -> Optional[_T]: ... - def in_bulk( - self, id_list: Any = ..., *, field_name: str = ... - ) -> Union[Dict[int, models.Model], Dict[str, models.Model]]: ... + def in_bulk(self, id_list: Any = ..., *, field_name: str = ...) -> Dict[str, models.Model]: ... def delete(self) -> Tuple[int, Dict[str, int]]: ... def update(self, **kwargs: Any) -> int: ... + def _update(self, values: Any) -> Optional[Any]: ... def exists(self) -> bool: ... def explain(self, *, format: Optional[Any] = ..., **options: Any) -> str: ... def raw( self, raw_query: str, params: Any = ..., translations: Optional[Dict[str, str]] = ..., using: None = ... ) -> RawQuerySet: ... - def values(self, *fields: Any, **expressions: Any) -> QuerySet: ... - def values_list(self, *fields: Any, flat: bool = ..., named: bool = ...) -> List[Any]: ... + def values(self, *fields: str, **expressions: Any) -> QuerySet: ... + def values_list(self, *fields: str, flat: bool = ..., named: bool = ...) -> QuerySet: ... def dates(self, field_name: str, kind: str, order: str = ...) -> QuerySet: ... def datetimes(self, field_name: str, kind: str, order: str = ..., tzinfo: None = ...) -> QuerySet: ... def none(self) -> QuerySet[_T]: ... @@ -122,3 +121,6 @@ class RawQuerySet(Iterable[_T], Sized): def prefetch_related(self, *lookups: Any) -> RawQuerySet[_T]: ... def resolve_model_init_order(self) -> Tuple[List[str], List[int], List[Tuple[str, int]]]: ... def using(self, alias: Optional[str]) -> RawQuerySet[_T]: ... + +class InstanceCheckMeta(type): ... +class EmptyQuerySet(metaclass=InstanceCheckMeta): ... diff --git a/django-stubs/forms/__init__.pyi b/django-stubs/forms/__init__.pyi index 5c361a5..a24c1ad 100644 --- a/django-stubs/forms/__init__.pyi +++ b/django-stubs/forms/__init__.pyi @@ -2,8 +2,28 @@ from django.core.exceptions import ValidationError as ValidationError from .forms import Form as Form, BaseForm as BaseForm -from .models import ModelForm as ModelForm +from .models import ( + ModelForm as ModelForm, + ModelChoiceField as ModelChoiceField, + ModelMultipleChoiceField as ModelMultipleChoiceField, +) -from .widgets import Widget as Widget, ChoiceWidget as ChoiceWidget +from .widgets import ( + Widget as Widget, + ChoiceWidget as ChoiceWidget, + NumberInput as NumberInput, + Select as Select, + CheckboxInput as CheckboxInput, + CheckboxSelectMultiple as CheckboxSelectMultiple, +) -from .fields import Field as Field, CharField as CharField +from .fields import ( + Field as Field, + CharField as CharField, + ChoiceField as ChoiceField, + DurationField as DurationField, + FileField as FileField, + ImageField as ImageField, + DateTimeField as DateTimeField, + DateField as DateField, +) diff --git a/django-stubs/forms/widgets.pyi b/django-stubs/forms/widgets.pyi index 67fa4e4..1724a52 100644 --- a/django-stubs/forms/widgets.pyi +++ b/django-stubs/forms/widgets.pyi @@ -15,6 +15,7 @@ from django.utils.safestring import SafeText class MediaOrderConflictWarning(RuntimeWarning): ... class Media: + _js: str def __init__( self, media: Optional[type] = ..., diff --git a/django-stubs/http/request.pyi b/django-stubs/http/request.pyi index c28ade5..3277655 100644 --- a/django-stubs/http/request.pyi +++ b/django-stubs/http/request.pyi @@ -4,6 +4,7 @@ from io import BytesIO from typing import Any, BinaryIO, Dict, Iterable, Iterator, List, Optional, overload, Pattern, Tuple, Union +from django.contrib.sessions.backends.base import SessionBase from django.core.files import uploadhandler, uploadedfile from django.utils.datastructures import MultiValueDict, ImmutableList from django.urls import ResolverMatch @@ -17,17 +18,18 @@ class RawPostDataException(Exception): ... UploadHandlerList = Union[List[uploadhandler.FileUploadHandler], ImmutableList[uploadhandler.FileUploadHandler]] class HttpRequest(BytesIO): - GET = ... # type: QueryDict - POST = ... # type: QueryDict - COOKIES = ... # type: Dict[str, str] - META = ... # type: Dict[str, str] - FILES = ... # type: MultiValueDict[str, uploadedfile.UploadedFile] - path = ... # type: str - path_info = ... # type: str - method = ... # type: Optional[str] - resolver_match = ... # type: ResolverMatch - content_type = ... # type: Optional[str] - content_params = ... # type: Optional[Dict[str, str]] + GET: QueryDict = ... + POST: QueryDict = ... + COOKIES: Dict[str, str] = ... + META: Dict[str, str] = ... + FILES: MultiValueDict[str, uploadedfile.UploadedFile] = ... + path: str = ... + path_info: str = ... + method: Optional[str] = ... + resolver_match: ResolverMatch = ... + content_type: Optional[str] = ... + content_params: Optional[Dict[str, str]] = ... + session: SessionBase def __init__(self) -> None: ... def get_host(self) -> str: ... def get_port(self) -> str: ... diff --git a/django-stubs/test/__init__.pyi b/django-stubs/test/__init__.pyi index dba7773..45106f2 100644 --- a/django-stubs/test/__init__.pyi +++ b/django-stubs/test/__init__.pyi @@ -3,8 +3,16 @@ from .testcases import ( TransactionTestCase as TransactionTestCase, SimpleTestCase as SimpleTestCase, LiveServerTestCase as LiveServerTestCase, + skipIfDBFeature as skipIfDBFeature, + skipUnlessDBFeature as skipUnlessDBFeature, + skipUnlessAnyDBFeature as skipUnlessAnyDBFeature, ) -from .utils import override_settings as override_settings, modify_settings as modify_settings +from .utils import ( + override_settings as override_settings, + modify_settings as modify_settings, + override_script_prefix as override_script_prefix, + override_system_checks as override_system_checks, +) from .client import Client as Client, RequestFactory as RequestFactory diff --git a/django-stubs/test/testcases.pyi b/django-stubs/test/testcases.pyi index 28a1b4d..2488e0b 100644 --- a/django-stubs/test/testcases.pyi +++ b/django-stubs/test/testcases.pyi @@ -176,6 +176,7 @@ class CheckCondition: def skipIfDBFeature(*features: Any) -> Callable: ... def skipUnlessDBFeature(*features: Any) -> Callable: ... +def skipUnlessAnyDBFeature(*features: Any) -> Callable: ... class QuietWSGIRequestHandler(WSGIRequestHandler): def log_message(*args: Any) -> None: ... diff --git a/django-stubs/test/utils.pyi b/django-stubs/test/utils.pyi index 94a75ea..c25f978 100644 --- a/django-stubs/test/utils.pyi +++ b/django-stubs/test/utils.pyi @@ -1,7 +1,8 @@ import decimal import warnings +from contextlib import contextmanager from decimal import Decimal -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, IO from django.apps.registry import Apps from django.core.checks.registry import CheckRegistry @@ -102,3 +103,12 @@ class isolate_apps(TestContextDecorator): installed_apps: Tuple[str] = ... def __init__(self, *installed_apps: Any, **kwargs: Any) -> None: ... old_apps: Apps = ... + +@contextmanager +def extend_sys_path(*paths: str) -> Iterator[None]: ... +@contextmanager +def captured_output(stream_name) -> Iterator[IO[str]]: ... +@contextmanager +def captured_stdout() -> Iterator[IO[str]]: ... +@contextmanager +def captured_stderr() -> Iterator[IO[str]]: ... diff --git a/mypy_django_plugin/plugins/migrations.py b/mypy_django_plugin/plugins/migrations.py index 28b30c4..be71db9 100644 --- a/mypy_django_plugin/plugins/migrations.py +++ b/mypy_django_plugin/plugins/migrations.py @@ -21,6 +21,9 @@ def determine_model_cls_from_string_for_migrations(ctx: MethodContext) -> Type: if app_label is None: return ctx.default_return_type + if 'model_name' not in ctx.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) if model_name is None: diff --git a/scripts/typecheck_tests.py b/scripts/typecheck_tests.py index 065c5d8..c139365 100644 --- a/scripts/typecheck_tests.py +++ b/scripts/typecheck_tests.py @@ -9,28 +9,78 @@ from git import Repo from mypy import build from mypy.main import process_options +# Django branch to typecheck against DJANGO_BRANCH = 'stable/2.1.x' + +# Specific commit in the Django repository to check against 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. IGNORED_ERROR_PATTERNS = [ 'Need type annotation for', 'already defined on', 'Cannot assign to a', 'cannot perform relative import', 'broken_app', + 'cache_clear', + 'call_count', + 'call_args_list', + 'call_args', + '"password_changed" does not return a value', + '"validate_password" does not return a value', 'LazySettings', 'Cannot infer type of lambda', + '"refresh_from_db" of "Model"', + '"as_sql" undefined in superclass', + 'Incompatible types in assignment (expression has type "str", target has type "type")', 'Incompatible types in assignment (expression has type "Callable[', 'Invalid value for a to= parameter', 'Incompatible types in assignment (expression has type "FilteredChildAdmin", variable has type "ChildAdmin")', + 'Incompatible types in assignment (expression has type "RelatedFieldWidgetWrapper", variable has type "AdminRadioSelect")', + 'has incompatible type "MockRequest"; expected "WSGIRequest"', + '"NullTranslations" has no attribute "_catalog"', re.compile(r'"Callable\[\[(Any(, )?)+\], Any\]" has no attribute'), re.compile(r'"HttpResponseBase" has no attribute "[A-Za-z_]+"'), re.compile(r'Incompatible types in assignment \(expression has type "Tuple\[\]", ' r'variable has type "Tuple\[[A-Za-z, ]+\]"'), + re.compile(r'"validate" of "[A-Za-z]+" does not return a value'), + re.compile(r'Module has no attribute "[A-Za-z]+"'), + re.compile(r'"[A-Za-z\[\]]+" has no attribute "getvalue"'), + # TODO: remove when reassignment will be possible (in 0.670? ) + re.compile(r'Incompatible types in assignment \(expression has type "(QuerySet|List){1}\[[A-Za-z, ]+\]", ' + r'variable has type "(QuerySet|List){1}\[[A-Za-z, ]+\]"\)'), + re.compile(r'"MockRequest" has no attribute "[a-zA-Z_]+"'), ] + +# Test folders to typecheck TESTS_DIRS = [ 'absolute_url_overrides', 'admin_autodiscover', 'admin_changelist', + 'admin_checks', + 'admin_custom_urls', + 'admin_default_site', + 'admin_docs', + # TODO: 'admin_filters', + 'admin_inlines', + 'admin_ordering', + 'admin_registration', + 'admin_scripts', + # TODO: 'admin_utils', + # TODO: 'admin_views', + 'admin_widgets', + 'aggregation', + 'aggregation_regress', + 'annotations', + 'app_loading', + 'apps', + # TODO: auth_tests + 'base', + 'bash_completion', + 'basic', + 'builtin_server', + 'bulk_create', ]