From a77d5b27d84d9d615987fa823bd33e8280b67d5a Mon Sep 17 00:00:00 2001 From: Maxim Kurnikov Date: Mon, 1 Jul 2019 18:44:34 +0300 Subject: [PATCH] Update typecheck_tests to django 2.2 branch, remove unused ignores (#98) * update typecheck_tests to django 2.2 branch, remove unused ignores * lint fixes --- django-stubs/contrib/admin/widgets.pyi | 8 +- django-stubs/core/management/base.pyi | 8 +- django-stubs/db/migrations/graph.pyi | 2 +- django-stubs/db/migrations/loader.pyi | 2 +- .../db/migrations/operations/fields.pyi | 12 +- django-stubs/db/migrations/writer.pyi | 2 +- django-stubs/db/models/aggregates.pyi | 11 +- django-stubs/db/models/deletion.pyi | 4 +- django-stubs/db/models/functions/text.pyi | 6 +- django-stubs/db/models/indexes.pyi | 21 +- django-stubs/db/models/query.pyi | 4 +- django-stubs/template/defaultfilters.pyi | 2 +- django-stubs/urls/conf.pyi | 9 +- scripts/typecheck_tests.py | 248 ++++++------------ 14 files changed, 124 insertions(+), 215 deletions(-) diff --git a/django-stubs/contrib/admin/widgets.pyi b/django-stubs/contrib/admin/widgets.pyi index c39d646..a0d509a 100644 --- a/django-stubs/contrib/admin/widgets.pyi +++ b/django-stubs/contrib/admin/widgets.pyi @@ -1,10 +1,10 @@ -from typing import Any, Dict, List, Optional, Set, Tuple, Union +from typing import Any, Dict, Optional, Tuple, Union from uuid import UUID from django.contrib.admin.sites import AdminSite from django.db.models.fields.reverse_related import ForeignObjectRel, ManyToOneRel from django.forms.models import ModelChoiceIterator -from django.forms.widgets import ChoiceWidget, Media +from django.forms.widgets import Media from django import forms @@ -43,7 +43,7 @@ class ManyToManyRawIdWidget(ForeignKeyRawIdWidget): ... class RelatedFieldWidgetWrapper(forms.Widget): template_name: str = ... choices: ModelChoiceIterator = ... - widget: AutocompleteSelect = ... + widget: forms.Widget = ... rel: ManyToOneRel = ... can_add_related: bool = ... can_change_related: bool = ... @@ -52,7 +52,7 @@ class RelatedFieldWidgetWrapper(forms.Widget): admin_site: AdminSite = ... def __init__( self, - widget: ChoiceWidget, + widget: forms.Widget, rel: ForeignObjectRel, admin_site: AdminSite, can_add_related: Optional[bool] = ..., diff --git a/django-stubs/core/management/base.pyi b/django-stubs/core/management/base.pyi index 6765fa6..14398f4 100644 --- a/django-stubs/core/management/base.pyi +++ b/django-stubs/core/management/base.pyi @@ -42,10 +42,14 @@ class BaseCommand: stderr: OutputWrapper = ... style: Style = ... def __init__( - self, stdout: Optional[StringIO] = ..., stderr: Optional[StringIO] = ..., no_color: bool = ... + self, + stdout: Optional[StringIO] = ..., + stderr: Optional[StringIO] = ..., + no_color: bool = ..., + force_color: bool = ..., ) -> None: ... def get_version(self) -> str: ... - def create_parser(self, prog_name: str, subcommand: str) -> CommandParser: ... + def create_parser(self, prog_name: str, subcommand: str, **kwargs: Any) -> CommandParser: ... def add_arguments(self, parser: CommandParser) -> None: ... def print_help(self, prog_name: str, subcommand: str) -> None: ... def run_from_argv(self, argv: List[str]) -> None: ... diff --git a/django-stubs/db/migrations/graph.pyi b/django-stubs/db/migrations/graph.pyi index b45ee18..1568eed 100644 --- a/django-stubs/db/migrations/graph.pyi +++ b/django-stubs/db/migrations/graph.pyi @@ -47,7 +47,7 @@ class MigrationGraph: def iterative_dfs(self, start: Any, forwards: bool = ...): ... def root_nodes(self, app: Optional[str] = ...) -> List[Tuple[str, str]]: ... def leaf_nodes(self, app: Optional[str] = ...) -> List[Tuple[str, str]]: ... - def ensure_not_cyclic(self, start: Union[Tuple[str, str], Node], get_children: Callable) -> None: ... + def ensure_not_cyclic(self) -> None: ... def make_state( self, nodes: Optional[Tuple[str, str]] = ..., at_end: bool = ..., real_apps: List[str] = ... ) -> ProjectState: ... diff --git a/django-stubs/db/migrations/loader.pyi b/django-stubs/db/migrations/loader.pyi index b030df5..bd475aa 100644 --- a/django-stubs/db/migrations/loader.pyi +++ b/django-stubs/db/migrations/loader.pyi @@ -8,7 +8,7 @@ MIGRATIONS_MODULE_NAME: str class MigrationLoader: connection: Any = ... disk_migrations: Dict[Tuple[str, str], Migration] = ... - applied_migrations: None = ... + applied_migrations: Set[Tuple[str, str]] = ... ignore_no_migrations: bool = ... def __init__(self, connection: Any, load: bool = ..., ignore_no_migrations: bool = ...) -> None: ... @classmethod diff --git a/django-stubs/db/migrations/operations/fields.pyi b/django-stubs/db/migrations/operations/fields.pyi index 8420153..1909aa3 100644 --- a/django-stubs/db/migrations/operations/fields.pyi +++ b/django-stubs/db/migrations/operations/fields.pyi @@ -1,20 +1,20 @@ -from typing import Any +from typing import Any, Optional from django.db.models.fields import Field from .base import Operation class FieldOperation(Operation): - model_name: Any = ... + model_name: str = ... model_name_lower: str - name: Any = ... - def __init__(self, model_name: str, name: str) -> None: ... + name: str = ... + def __init__(self, model_name: str, name: str, field: Optional[Field] = ...) -> None: ... def name_lower(self) -> str: ... def is_same_model_operation(self, operation: FieldOperation) -> bool: ... def is_same_field_operation(self, operation: AddField) -> bool: ... class AddField(FieldOperation): - field: Any = ... - preserve_default: Any = ... + field: Field = ... + preserve_default: bool = ... def __init__(self, model_name: str, name: str, field: Field, preserve_default: bool = ...) -> None: ... class RemoveField(FieldOperation): ... diff --git a/django-stubs/db/migrations/writer.pyi b/django-stubs/db/migrations/writer.pyi index 641f9a7..4aa193e 100644 --- a/django-stubs/db/migrations/writer.pyi +++ b/django-stubs/db/migrations/writer.pyi @@ -21,7 +21,7 @@ class OperationWriter: class MigrationWriter: migration: Migration = ... needs_manual_porting: bool = ... - def __init__(self, migration: Union[type, Migration]) -> None: ... + def __init__(self, migration: Union[type, Migration], include_header: bool = ...) -> None: ... def as_string(self) -> str: ... @property def basedir(self) -> str: ... diff --git a/django-stubs/db/models/aggregates.pyi b/django-stubs/db/models/aggregates.pyi index bc9c8ff..6c56131 100644 --- a/django-stubs/db/models/aggregates.pyi +++ b/django-stubs/db/models/aggregates.pyi @@ -1,21 +1,14 @@ from typing import Any, Optional from django.db.models.expressions import Func -from django.db.models.query_utils import Q class Aggregate(Func): - name: Any = ... filter_template: str = ... - window_compatible: bool = ... filter: Any = ... - def __init__(self, *args: Any, filter: Optional[Any] = ..., **kwargs: Any) -> None: ... + def __init__(self, *expressions: Any, distinct: bool = ..., filter: Optional[Any] = ..., **extra: Any) -> None: ... class Avg(Aggregate): ... - -class Count(Aggregate): - template: str = ... - def __init__(self, expression: str, distinct: bool = ..., filter: Optional[Q] = ..., **extra: Any) -> None: ... - +class Count(Aggregate): ... class Max(Aggregate): ... class Min(Aggregate): ... class StdDev(Aggregate): ... diff --git a/django-stubs/db/models/deletion.pyi b/django-stubs/db/models/deletion.pyi index fd3eddc..abe7ae5 100644 --- a/django-stubs/db/models/deletion.pyi +++ b/django-stubs/db/models/deletion.pyi @@ -10,4 +10,6 @@ def PROTECT(collector, field, sub_objs, using): ... def SET(value: Any) -> Callable: ... class ProtectedError(IntegrityError): ... -class Collector: ... + +class Collector: + def __init__(self, using: str) -> None: ... diff --git a/django-stubs/db/models/functions/text.pyi b/django-stubs/db/models/functions/text.pyi index 20fe6ca..ab9b871 100644 --- a/django-stubs/db/models/functions/text.pyi +++ b/django-stubs/db/models/functions/text.pyi @@ -25,13 +25,15 @@ class Length(Transform): ... class Lower(Transform): ... class LPad(BytesToCharFieldConversionMixin, Func): - def __init__(self, expression: str, length: Union[Length, int], fill_text: Value = ..., **extra: Any) -> None: ... + def __init__( + self, expression: str, length: Optional[Union[Length, int]], fill_text: Value = ..., **extra: Any + ) -> None: ... class LTrim(Transform): ... class Ord(Transform): ... class Repeat(BytesToCharFieldConversionMixin, Func): - def __init__(self, expression: Union[Value, str], number: Union[Length, int], **extra: Any) -> None: ... + def __init__(self, expression: Union[Value, str], number: Optional[Union[Length, int]], **extra: Any) -> None: ... class Replace(Func): def __init__(self, expression: Combinable, text: Value, replacement: Value = ..., **extra: Any) -> None: ... diff --git a/django-stubs/db/models/indexes.pyi b/django-stubs/db/models/indexes.pyi index e35ae63..cb33b49 100644 --- a/django-stubs/db/models/indexes.pyi +++ b/django-stubs/db/models/indexes.pyi @@ -1,23 +1,34 @@ -from typing import Any, Dict, List, Optional, Tuple, Type, Union +from typing import Any, List, Optional, Sequence, Tuple, Type from django.db.backends.base.schema import BaseDatabaseSchemaEditor from django.db.backends.ddl_references import Statement from django.db.models.base import Model +from django.db.models.query_utils import Q class Index: model: Type[Model] suffix: str = ... max_name_length: int = ... - fields: List[str] = ... - fields_orders: List[Tuple[str, str]] = ... + fields: Sequence[str] = ... + fields_orders: Sequence[Tuple[str, str]] = ... name: str = ... db_tablespace: Optional[str] = ... - def __init__(self, *, fields: Any = ..., name: Optional[Any] = ..., db_tablespace: Optional[Any] = ...) -> None: ... + opclasses: Sequence[str] = ... + condition: Optional[Q] = ... + def __init__( + self, + *, + fields: Sequence[str] = ..., + name: Optional[str] = ..., + db_tablespace: Optional[str] = ..., + opclasses: Sequence[str] = ..., + condition: Optional[Q] = ... + ) -> None: ... def check_name(self) -> List[str]: ... def create_sql( self, model: Type[Model], schema_editor: BaseDatabaseSchemaEditor, using: str = ... ) -> Statement: ... def remove_sql(self, model: Type[Model], schema_editor: BaseDatabaseSchemaEditor) -> str: ... - def deconstruct(self) -> Tuple[str, Tuple, Dict[str, Union[List[str], str]]]: ... + def deconstruct(self) -> Any: ... def clone(self) -> Index: ... def set_name_with_model(self, model: Type[Model]) -> None: ... diff --git a/django-stubs/db/models/query.pyi b/django-stubs/db/models/query.pyi index b578190..e53adda 100644 --- a/django-stubs/db/models/query.pyi +++ b/django-stubs/db/models/query.pyi @@ -79,7 +79,9 @@ class QuerySet(Generic[_T, _Row], Collection[_Row], Sized): def aggregate(self, *args: Any, **kwargs: Any) -> Dict[str, Any]: ... def get(self, *args: Any, **kwargs: Any) -> _Row: ... def create(self, **kwargs: Any) -> _T: ... - def bulk_create(self, objs: Iterable[Model], batch_size: Optional[int] = ...) -> List[_T]: ... + def bulk_create( + self, objs: Iterable[Model], batch_size: Optional[int] = ..., ignore_conflicts: bool = ... + ) -> List[_T]: ... 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 diff --git a/django-stubs/template/defaultfilters.pyi b/django-stubs/template/defaultfilters.pyi index c1cb33b..d4cbabb 100644 --- a/django-stubs/template/defaultfilters.pyi +++ b/django-stubs/template/defaultfilters.pyi @@ -48,7 +48,7 @@ def last(value: List[str]) -> str: ... def length(value: Any) -> int: ... def length_is(value: Optional[Any], arg: Union[SafeText, int]) -> Union[bool, str]: ... def random(value: List[str]) -> str: ... -def slice_filter(value: Any, arg: str) -> Any: ... +def slice_filter(value: Any, arg: Union[str, int]) -> Any: ... def unordered_list(value: Any, autoescape: bool = ...) -> Any: ... def add(value: Any, arg: Any) -> Any: ... def get_digit(value: Any, arg: int) -> Any: ... diff --git a/django-stubs/urls/conf.pyi b/django-stubs/urls/conf.pyi index aaaab5d..9f8f6e6 100644 --- a/django-stubs/urls/conf.pyi +++ b/django-stubs/urls/conf.pyi @@ -1,11 +1,8 @@ -from typing import Any, Callable, List, Optional, Tuple, Union +from typing import Any, List, Optional, Tuple -from .resolvers import URLPattern, URLResolver +from .resolvers import URLResolver -def include( - arg: Union[List[Tuple[str, Callable]], List[URLPattern], List[URLResolver], Tuple[List[URLResolver], str], str], - namespace: Optional[str] = ..., -) -> Tuple[List[URLResolver], Optional[str], Optional[str]]: ... +def include(arg: Any, namespace: Optional[str] = ...) -> Tuple[List[URLResolver], Optional[str], Optional[str]]: ... path: Any re_path: Any diff --git a/scripts/typecheck_tests.py b/scripts/typecheck_tests.py index bd2ed69..1b5c33b 100644 --- a/scripts/typecheck_tests.py +++ b/scripts/typecheck_tests.py @@ -1,6 +1,7 @@ import itertools import os import re +import shutil import sys from contextlib import contextmanager from pathlib import Path @@ -13,10 +14,10 @@ from mypy.main import process_options PROJECT_DIRECTORY = Path(__file__).parent.parent # Django branch to typecheck against -DJANGO_BRANCH = 'stable/2.1.x' +DJANGO_BRANCH = 'stable/2.2.x' # Specific commit in the Django repository to check against -DJANGO_COMMIT_SHA = '8fe63dc4cd637c1422a9bf3e3421d64388e14afd' +DJANGO_COMMIT_SHA = '395cf7c37514b642c4bcf30e01fc1a2c4f82b2fe' # Some errors occur for the test suite itself, and cannot be addressed via django-stubs. They should be ignored # using this constant. @@ -60,40 +61,19 @@ IGNORED_ERRORS = { + 'expected "Union[str, bytes, bytearray]"', 'Incompatible types in assignment (expression has type "None", variable has type Module)', 'note:', - # Suppress false-positive error due to mypy being overly strict with base class compatibility checks even though - # objects/_default_manager are redefined in the subclass to be compatible with the base class definition. - # Can be removed when mypy issue is fixed: https://github.com/python/mypy/issues/2619 - re.compile(r'Definition of "(objects|_default_manager)" in base class "[A-Za-z0-9]+" is incompatible with ' - r'definition in base class "[A-Za-z0-9]+"'), - ], - 'admin_changelist': [ - 'Incompatible types in assignment (expression has type "FilteredChildAdmin", variable has type "ChildAdmin")' ], 'admin_scripts': [ 'Incompatible types in assignment (expression has type "Callable[' ], - 'admin_widgets': [ - 'Incompatible types in assignment (expression has type "RelatedFieldWidgetWrapper", ' - + 'variable has type "AdminRadioSelect")', - 'Incompatible types in assignment (expression has type "Union[Widget, Any]", ' - + 'variable has type "AutocompleteSelect")' - ], 'admin_utils': [ re.compile(r'Argument [0-9] to "lookup_field" has incompatible type'), 'MockModelAdmin', 'Incompatible types in assignment (expression has type "str", variable has type "Callable[..., Any]")', - 'Dict entry 0 has incompatible type "str": "Tuple[str, str, List[str]]"; expected "str": ' - + '"Tuple[str, str, Tuple[str, str]]"', - 'Incompatible types in assignment (expression has type "bytes", variable has type "str")' ], 'admin_views': [ 'Argument 1 to "FileWrapper" has incompatible type "StringIO"; expected "IO[bytes]"', 'Incompatible types in assignment', '"object" not callable', - 'Incompatible type for "pk" of "Collector" (got "int", expected "str")', - re.compile('Unexpected attribute "[a-z]+" for model "Model"'), - 'Unexpected attribute "two_id" for model "CyclicOne"', - 'Argument "is_dst" to "localize" of "BaseTzInfo" has incompatible type "None"; expected "bool"' ], 'aggregation': [ '"as_sql" undefined in superclass', @@ -101,13 +81,12 @@ IGNORED_ERRORS = { 'Incompatible type for "publisher" of "Book" (got "Optional[Publisher]", ' + 'expected "Union[Publisher, Combinable]")' ], - 'apps': [ - 'Incompatible types in assignment (expression has type "str", target has type "type")', - '"Callable[[bool, bool], List[Type[Model]]]" has no attribute "cache_clear"' - ], 'annotations': [ 'Incompatible type for "store" of "Employee" (got "Optional[Store]", expected "Union[Store, Combinable]")' ], + 'apps': [ + 'Incompatible types in assignment (expression has type "str", target has type "type")', + ], 'auth_tests': [ '"PasswordValidator" has no attribute "min_length"', '"validate_password" does not return a value', @@ -117,25 +96,11 @@ IGNORED_ERRORS = { ], 'basic': [ 'Unexpected keyword argument "unknown_kwarg" for "refresh_from_db" of "Model"', - '"refresh_from_db" of "Model" defined here', 'Unexpected attribute "foo" for model "Article"' ], - 'builtin_server': [ - 'has no attribute "getvalue"' - ], 'custom_lookups': [ 'in base class "SQLFuncMixin"' ], - 'custom_managers': [ - '_filter_CustomQuerySet', - '_filter_CustomManager', - re.compile(r'Cannot determine type of \'(abstract_persons|cars|plain_manager)\''), - # TODO: remove after 'objects' and '_default_manager' are handled in the plugin - 'Incompatible types in assignment (expression has type "CharField", ' - + 'base class "Model" defined the type as "Manager[Model]")', - # TODO: remove after from_queryset() handled in the plugin - 'Invalid base class' - ], 'csrf_tests': [ 'Incompatible types in assignment (expression has type "property", ' + 'base class "HttpRequest" defined the type as "QueryDict")' @@ -152,14 +117,10 @@ IGNORED_ERRORS = { 'deprecation': [ re.compile('"(old|new)" undefined in superclass') ], - 'db_typecasts': [ - '"object" has no attribute "__iter__"; maybe "__str__" or "__dir__"? (not iterable)' - ], - 'from_db_value': [ - 'has no attribute "vendor"' - ], - 'field_deconstruction': [ - 'Incompatible types in assignment (expression has type "ForeignKey[Any]", variable has type "CharField")' + 'db_functions': [ + 'for **', + 'expected "float"', + 'Incompatible types in assignment (expression has type "Optional[FloatModel]", variable has type "FloatModel")' ], 'file_uploads': [ '"handle_uncaught_exception" undefined in superclass' @@ -201,33 +162,24 @@ IGNORED_ERRORS = { 'Argument 1 to "update_or_create" of "QuerySet" has incompatible type "**Dict[str, object]"; ' + 'expected "Optional[MutableMapping[str, Any]]"' ], - 'httpwrappers': [ - 'Argument 2 to "appendlist" of "QueryDict" has incompatible type "List[str]"; expected "str"' - ], 'humanize_tests': [ 'Argument 1 to "append" of "list" has incompatible type "None"; expected "str"' ], - 'invalid_models_tests': [ - 'Argument "max_length" to "CharField" has incompatible type "str"; expected "Optional[int]"', - 'Argument "choices" to "CharField" has incompatible type "str"' - ], 'logging_tests': [ re.compile('"(setUpClass|tearDownClass)" undefined in superclass') ], 'lookup': [ 'Unexpected keyword argument "headline__startswith" for "in_bulk" of "QuerySet"', ], + 'm2o_recursive': [ + 'Incompatible type for "id" of "Category" (got "None", expected "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]")' ], - 'model_meta': [ - '"object" has no attribute "items"', - '"Field" has no attribute "many_to_many"', - ], - 'model_forms': [ - 'Argument "instance" to "InvalidModelForm" has incompatible type "Type[Category]"; expected "Optional[Model]"', - 'Invalid type "NewForm"' + 'middleware_exceptions': [ + 'Argument 1 to "append" of "list" has incompatible type "Tuple[Any, Any]"; expected "str"' ], 'model_fields': [ 'Incompatible types in assignment (expression has type "Type[Person]", variable has type', @@ -242,22 +194,19 @@ IGNORED_ERRORS = { 'Incompatible types in assignment (expression has type "FloatModel", variable has type ' '"Union[float, int, str, Combinable]")', ], - 'model_formsets': [ - 'Incompatible types in string interpolation (expression has type "object", ' - + 'placeholder has type "Union[int, float]")' - ], - 'model_formsets_regress': [ - 'Incompatible types in assignment (expression has type "Model", variable has type "User")' + 'model_indexes': [ + 'Argument "condition" to "Index" has incompatible type "str"; expected "Optional[Q]"' ], 'model_regress': [ - 'Too many arguments for "Worker"', re.compile(r'Incompatible type for "[a-z]+" of "Worker" \(got "int", expected') ], 'modeladmin': [ 'BandAdmin', 'base class "ModelAdmin" defined the type a', 'base class "InlineModelAdmin" defined the type a', - 'List item 0 has incompatible type "Type[ValidationTestInline]"; expected "Type[InlineModelAdmin]"' + 'List item 0 has incompatible type', + 'Incompatible types in assignment (expression has type "None", base class "AdminBase" ' + + 'defined the type as "List[str]")' ], 'migrate_signals': [ 'Value of type "None" is not indexable', @@ -265,40 +214,25 @@ IGNORED_ERRORS = { ], 'migrations': [ 'FakeMigration', - 'Incompatible types in assignment (expression has type "TextField", base class "Model" ' - + 'defined the type as "Manager[Model]")', - 'Incompatible types in assignment (expression has type "DeleteModel", variable has type "RemoveField")', - 'Argument "bases" to "CreateModel" has incompatible type "Tuple[Type[Mixin], Type[Mixin]]"; ' - + 'expected "Optional[Sequence[Union[Type[Model], str]]]"', - 'Argument 1 to "RunPython" has incompatible type "str"; expected "Callable[..., Any]"', 'FakeLoader', + 'Dict entry 0 has incompatible type "Any": "Set[Tuple[Any, ...]]"; expected "Any": "str"', + 'Argument 1 to "RunPython" has incompatible type "str"; expected "Callable[..., Any]"', 'Argument 1 to "append" of "list" has incompatible type "AddIndex"; expected "CreateModel"', - 'Unsupported operand types for - ("Set[Any]" and "None")', - ], - 'middleware_exceptions': [ - 'Argument 1 to "append" of "list" has incompatible type "Tuple[Any, Any]"; expected "str"' ], 'multiple_database': [ - 'Unexpected attribute "extra_arg" for model "Book"', 'Too many arguments for "create" of "QuerySet"' ], - 'queryset_pickle': [ - '"None" has no attribute "somefield"' - ], 'postgres_tests': [ - 'Cannot assign multiple types to name', - 'Incompatible types in assignment (expression has type "Type[Field[Any, Any]]', 'DummyArrayField', 'DummyJSONField', + 'Cannot assign multiple types to name', + 'Incompatible types in assignment (expression has type "Type[Field[Any, Any]]', 'Argument "encoder" to "JSONField" has incompatible type "DjangoJSONEncoder"; ' + 'expected "Optional[Type[JSONEncoder]]"', - 'for model "CITestModel"', 'Incompatible type for "field" of "IntegerArrayModel" (got "None", ' + 'expected "Union[Sequence[int], Combinable]")', - re.compile(r'Incompatible types in assignment \(expression has type "Type\[.+?\]", base class "UnaccentTest" ' - r'defined the type as "Type\[CharFieldModel\]"\)'), - 'Incompatible types in assignment (expression has type "Type[TextFieldModel]", base class "TrigramTest" ' - 'defined the type as "Type[CharFieldModel]")', + re.compile(r'Incompatible types in assignment \(expression has type "Type\[.+?\]", ' + r'base class "(UnaccentTest|TrigramTest)" defined the type as "Type\[CharFieldModel\]"\)'), ], 'properties': [ re.compile('Unexpected attribute "(full_name|full_name_2)" for model "Person"') @@ -314,33 +248,18 @@ IGNORED_ERRORS = { 'Incompatible type for "objectb" of "ObjectC" (got "ObjectA", expected' ' "Union[ObjectB, Combinable, None, None]")', ], + 'prefetch_related': [ + 'Incompatible types in assignment (expression has type "List[Room]", variable has type "QuerySet[Room, Room]")', + ], + 'proxy_model_inheritance': [ + 'Incompatible import of "ProxyModel"' + ], '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]"' ], - 'prefetch_related': [ - 'Incompatible types in assignment (expression has type "List[Room]", variable has type "QuerySet[Room, Room]")', - '"None" has no attribute "__iter__"', - 'has no attribute "read_by"' - ], - 'proxy_model_inheritance': [ - 'Incompatible import of "ProxyModel"' - ], - 'signals': [ - 'Argument 1 to "append" of "list" has incompatible type "Tuple[Any, Any, Optional[Any], Any]"; ' - + 'expected "Tuple[Any, Any, Any]"' - ], - 'syndication_tests': [ - 'List or tuple expected as variable arguments' - ], - 'staticfiles_tests': [ - 'Value of type "stat_result" is not indexable', - '"setUp" undefined in superclass', - 'Argument 1 to "write" of "IO" has incompatible type "bytes"; expected "str"', - 'Value of type "object" is not indexable' - ], 'schema': [ 'Incompatible type for "info" of "Note" (got "None", expected "Union[str, Combinable]")', 'Incompatible type for "detail_info" of "NoteRename" (got "None", expected "Union[str, Combinable]")', @@ -349,12 +268,34 @@ IGNORED_ERRORS = { 'settings_tests': [ 'Argument 1 to "Settings" has incompatible type "Optional[str]"; expected "str"' ], - 'transactions': [ - 'Incompatible types in assignment (expression has type "Thread", variable has type "Callable[[], Any]")' + 'signals': [ + 'Argument 1 to "append" of "list" has incompatible type "Tuple[Any, Any, Optional[Any], Any]"; ' + + 'expected "Tuple[Any, Any, Any]"' + ], + '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")', + ], + 'select_related_onetoone': [ + 'Incompatible types in assignment (expression has type "Parent2", variable has type "Parent1")', + ], + 'servers': [ + re.compile('Argument [0-9] to "WSGIRequestHandler"') + ], + 'template_tests': [ + re.compile(r'Argument 1 to "[a-zA-Z_]+" has incompatible type "int"; expected "str"'), + 'TestObject', + 'variable has type "Callable[[Any], Any]', + 'Value of type variable "AnyStr" of "urljoin" cannot be "Optional[str]"' + ], + 'template_backends': [ + 'Incompatible import of "Jinja2" (imported name has type "Type[Jinja2]", local name has type "object")', + 'TemplateStringsTests', ], 'test_client': [ - 'Incompatible types in assignment (expression has type "StreamingHttpResponse", ' - + 'variable has type "HttpResponse")', 'Incompatible types in assignment (expression has type "HttpResponse", ' + 'variable has type "StreamingHttpResponse")' ], @@ -362,70 +303,23 @@ IGNORED_ERRORS = { 'Incompatible types in assignment (expression has type "Dict[, ]", ' + 'variable has type "SessionBase")', 'Unsupported left operand type for + ("None")', - 'Both left and right operands are unions' ], 'timezones': [ 'Too few arguments for "render" of "Template"' ], 'test_runner': [ - 'Value of type "TestSuite" is not indexable', - '"TestSuite" has no attribute "_tests"', 'Argument "result" to "run" of "TestCase" has incompatible type "RemoteTestResult"; ' + 'expected "Optional[TestResult]"', - 'Item "TestSuite" of "Union[TestCase, TestSuite]" has no attribute "id"', - 'MockTestRunner', - 'Incompatible types in assignment (expression has type "Tuple[Union[TestCase, TestSuite], ...]", ' - + 'variable has type "TestSuite")' ], - 'template_tests': [ - 'Xtemplate', - re.compile(r'Argument 1 to "[a-zA-Z_]+" has incompatible type "int"; expected "str"'), - 'TestObject', - 'variable has type "Callable[[Any], Any]', - 'template_debug', - '"yield from" can\'t be applied to', - re.compile(r'List item [0-9] has incompatible type "URLResolver"; expected "URLPattern"'), - '"WSGIRequest" has no attribute "current_app"', - 'Value of type variable "AnyStr" of "urljoin" cannot be "Optional[str]"' - ], - 'template_backends': [ - 'Incompatible import of "Jinja2" (imported name has type "Type[Jinja2]", local name has type "object")', - 'TemplateStringsTests', + 'transactions': [ + 'Incompatible types in assignment (expression has type "Thread", variable has type "Callable[[], Any]")' ], 'urlpatterns': [ - '"object" has no attribute "__iter__"; maybe "__str__" or "__dir__"? (not iterable)', '"object" not callable' ], '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")', - 'Incompatible types in assignment (expression has type "None", variable has type "int")', - ], - 'select_related_onetoone': [ - '"None" has no attribute', - 'Incompatible types in assignment (expression has type "Parent2", variable has type "Parent1")', - ], - 'servers': [ - re.compile('Argument [0-9] to "WSGIRequestHandler"') - ], - 'sitemaps_tests': [ - 'Incompatible types in assignment (expression has type "str", ' - + 'base class "Sitemap" defined the type as "Callable[[Sitemap, Model], str]")' - ], - 'view_tests': [ - '"Handler" has no attribute "include_html"', - '"EmailMessage" has no attribute "alternatives"' - ] } # Test folders to typecheck TESTS_DIRS = [ @@ -520,10 +414,8 @@ TESTS_DIRS = [ 'inline_formsets', 'inspectdb', 'introspection', - # not practical # 'invalid_models_tests', - 'known_related_objects', 'logging_tests', 'lookup', @@ -701,6 +593,7 @@ def get_absolute_path_for_test(test_dirname: str): if __name__ == '__main__': 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' global_rc = 0 @@ -718,12 +611,17 @@ if __name__ == '__main__': else: tests_to_run = TESTS_DIRS - for dirname in tests_to_run: - abs_path = get_absolute_path_for_test(dirname) - print(f'Checking {abs_path}') + try: + for dirname in tests_to_run: + abs_path = get_absolute_path_for_test(dirname) + print(f'Checking {abs_path}') - rc = check_with_mypy(abs_path, mypy_config_file) - if rc != 0: - global_rc = 1 + rc = check_with_mypy(abs_path, mypy_config_file) + if rc != 0: + global_rc = 1 - sys.exit(global_rc) + sys.exit(global_rc) + + except BaseException as exc: + shutil.rmtree(mypy_cache_dir, ignore_errors=True) + raise exc