diff --git a/django-stubs/contrib/admin/filters.pyi b/django-stubs/contrib/admin/filters.pyi index d6913d7..80d6109 100644 --- a/django-stubs/contrib/admin/filters.pyi +++ b/django-stubs/contrib/admin/filters.pyi @@ -1,4 +1,4 @@ -from typing import Any, Callable, Dict, List, Optional, Tuple, Type +from typing import Any, Callable, Dict, List, Optional, Tuple, Type, Iterator from django.contrib.admin.options import ModelAdmin from django.core.handlers.wsgi import WSGIRequest @@ -16,7 +16,7 @@ class ListFilter: self, request: WSGIRequest, params: Dict[str, str], model: Type[Model], model_admin: ModelAdmin ) -> None: ... def has_output(self) -> bool: ... - def choices(self, changelist: Any) -> None: ... + def choices(self, changelist: Any) -> Optional[Iterator[Dict[str, Any]]]: ... def queryset(self, request: Any, queryset: QuerySet) -> Optional[QuerySet]: ... def expected_parameters(self) -> Optional[List[str]]: ... diff --git a/django-stubs/contrib/auth/base_user.pyi b/django-stubs/contrib/auth/base_user.pyi index 9349448..f64b2fe 100644 --- a/django-stubs/contrib/auth/base_user.pyi +++ b/django-stubs/contrib/auth/base_user.pyi @@ -1,4 +1,4 @@ -from typing import Any, Optional, Tuple, List +from typing import Any, Optional, Tuple, List, overload from django.db import models @@ -30,4 +30,8 @@ class AbstractBaseUser(models.Model): @classmethod def get_email_field_name(cls) -> str: ... @classmethod + @overload def normalize_username(cls, username: str) -> str: ... + @classmethod + @overload + def normalize_username(cls, username: Any) -> Any: ... diff --git a/django-stubs/contrib/contenttypes/models.pyi b/django-stubs/contrib/contenttypes/models.pyi index 5cf718e..2f2611f 100644 --- a/django-stubs/contrib/contenttypes/models.pyi +++ b/django-stubs/contrib/contenttypes/models.pyi @@ -4,12 +4,7 @@ from django.db import models from django.db.models.base import Model from django.db.models.query import QuerySet -class ContentTypeManager(models.Manager): - creation_counter: int - model: None - name: None - use_in_migrations: bool = ... - def __init__(self, *args: Any, **kwargs: Any) -> None: ... +class ContentTypeManager(models.Manager['ContentType']): def get_by_natural_key(self, app_label: str, model: str) -> ContentType: ... def get_for_model(self, model: Union[Type[Model], Model], for_concrete_model: bool = ...) -> ContentType: ... def get_for_models(self, *models: Any, for_concrete_models: bool = ...) -> Dict[Type[Model], ContentType]: ... @@ -18,9 +13,9 @@ class ContentTypeManager(models.Manager): class ContentType(models.Model): id: int - app_label: str = ... - model: str = ... - objects: Any = ... + app_label: models.CharField = ... + model: models.CharField = ... + objects: ContentTypeManager = ... @property def name(self) -> str: ... def model_class(self) -> Optional[Type[Model]]: ... diff --git a/django-stubs/contrib/flatpages/models.pyi b/django-stubs/contrib/flatpages/models.pyi index dc2b386..66b1862 100644 --- a/django-stubs/contrib/flatpages/models.pyi +++ b/django-stubs/contrib/flatpages/models.pyi @@ -1,14 +1,13 @@ -from typing import Any, Optional +from django.contrib.sites.models import Site from django.db import models class FlatPage(models.Model): - id: None - url: str = ... - title: str = ... - content: str = ... - enable_comments: bool = ... - template_name: str = ... - registration_required: bool = ... - sites: Any = ... + url: models.CharField = ... + title: models.CharField = ... + content: models.TextField = ... + enable_comments: models.BooleanField = ... + template_name: models.CharField = ... + registration_required: models.BooleanField = ... + sites: models.ManyToManyField[Site] = ... def get_absolute_url(self) -> str: ... diff --git a/django-stubs/core/files/uploadedfile.pyi b/django-stubs/core/files/uploadedfile.pyi index 89b7fa4..d991898 100644 --- a/django-stubs/core/files/uploadedfile.pyi +++ b/django-stubs/core/files/uploadedfile.pyi @@ -1,40 +1,43 @@ -# Stubs for django.core.files.uploadedfile (Python 3.5) - from typing import Any, Dict, IO, Iterator, Optional, Union from django.core.files import temp as tempfile from django.core.files.base import File class UploadedFile(File): - content_type = ... # type: Optional[str] - charset = ... # type: Optional[str] - content_type_extra = ... # type: Optional[Dict[str, str]] + content_type: Optional[str] = ... + charset: Optional[str] = ... + content_type_extra: Optional[Dict[str, str]] = ... def __init__( self, - file: IO, - name: str = None, - content_type: str = None, - size: int = None, - charset: str = None, - content_type_extra: Dict[str, str] = None, + file: Optional[IO] = ..., + name: Optional[str] = ..., + content_type: Optional[str] = ..., + size: Optional[int] = ..., + charset: Optional[str] = ..., + content_type_extra: Optional[Dict[str, str]] = ..., ) -> None: ... class TemporaryUploadedFile(UploadedFile): def __init__( - self, name: str, content_type: str, size: int, charset: str, content_type_extra: Dict[str, str] = None + self, + name: Optional[str], + content_type: Optional[str], + size: Optional[int], + charset: Optional[str], + content_type_extra: Optional[Dict[str, str]] = ..., ) -> None: ... def temporary_file_path(self) -> str: ... class InMemoryUploadedFile(UploadedFile): - field_name = ... # type: Optional[str] + field_name: Optional[str] = ... def __init__( self, file: IO, field_name: Optional[str], - name: str, + name: Optional[str], content_type: Optional[str], - size: int, + size: Optional[int], charset: Optional[str], - content_type_extra: Dict[str, str] = None, + content_type_extra: Dict[str, str] = ..., ) -> None: ... def chunks(self, chunk_size: int = None) -> Iterator[bytes]: ... def multiple_chunks(self, chunk_size: int = None) -> bool: ... diff --git a/django-stubs/db/models/expressions.pyi b/django-stubs/db/models/expressions.pyi index 464a6e8..180738a 100644 --- a/django-stubs/db/models/expressions.pyi +++ b/django-stubs/db/models/expressions.pyi @@ -168,12 +168,12 @@ class RawSQL(Expression): def __init__(self, sql: str, params: Sequence[Any], output_field: Optional[_OutputField] = ...) -> None: ... class Func(SQLiteNumericMixin, Expression): - function: Any = ... + function: str = ... template: str = ... arg_joiner: str = ... - arity: Any = ... + arity: int = ... source_expressions: List[Expression] = ... - extra: Any = ... + extra: Dict[Any, Any] = ... 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: ... diff --git a/django-stubs/db/models/fields/__init__.pyi b/django-stubs/db/models/fields/__init__.pyi index 9ad30a5..1ab2a2d 100644 --- a/django-stubs/db/models/fields/__init__.pyi +++ b/django-stubs/db/models/fields/__init__.pyi @@ -207,6 +207,7 @@ class GenericIPAddressField(Field): validators: Iterable[_ValidatorCallable] = ..., error_messages: Optional[_ErrorMessagesToOverride] = ..., ) -> None: ... + def __set__(self, instance, value: Union[str, int, Callable[..., Any], Combinable]): ... def __get__(self, instance, owner) -> str: ... class DateTimeCheckMixin: ... @@ -269,7 +270,7 @@ class DateTimeField(DateField): def __get__(self, instance, owner) -> datetime: ... class UUIDField(Field): - def __set__(self, instance, value: Any) -> None: ... + def __set__(self, instance, value: Union[str, uuid.UUID]) -> None: ... def __get__(self, instance, owner) -> uuid.UUID: ... class FilePathField(Field): diff --git a/django-stubs/db/models/functions/window.pyi b/django-stubs/db/models/functions/window.pyi index b09e31d..e4454d9 100644 --- a/django-stubs/db/models/functions/window.pyi +++ b/django-stubs/db/models/functions/window.pyi @@ -9,22 +9,19 @@ class CumeDist(Func): window_compatible: bool = ... class DenseRank(Func): - extra: Dict[Any, Any] - source_expressions: List[Any] - function: str = ... name: str = ... output_field: Any = ... window_compatible: bool = ... class FirstValue(Func): - arity: int = ... - function: str = ... name: str = ... window_compatible: bool = ... class LagLeadFunction(Func): window_compatible: bool = ... - def __init__(self, expression: Optional[str], offset: int = ..., default: None = ..., **extra: Any) -> Any: ... + def __init__( + self, expression: Optional[str], offset: int = ..., default: Optional[int] = ..., **extra: Any + ) -> None: ... class Lag(LagLeadFunction): function: str = ... diff --git a/django-stubs/http/multipartparser.pyi b/django-stubs/http/multipartparser.pyi index e55283c..c8843a6 100644 --- a/django-stubs/http/multipartparser.pyi +++ b/django-stubs/http/multipartparser.pyi @@ -1,4 +1,4 @@ -from io import BytesIO +from io import BytesIO, StringIO from typing import Any, Dict, Iterator, List, Optional, Tuple, Union from django.http.request import QueryDict @@ -11,7 +11,7 @@ class MultiPartParser: def __init__( self, META: Dict[str, Any], - input_data: BytesIO, + input_data: Union[StringIO, BytesIO], upload_handlers: Union[List[Any], ImmutableList], encoding: Optional[str] = ..., ) -> None: ... diff --git a/django-stubs/http/response.pyi b/django-stubs/http/response.pyi index 6f1374d..04b6e6f 100644 --- a/django-stubs/http/response.pyi +++ b/django-stubs/http/response.pyi @@ -27,7 +27,7 @@ class HttpResponseBase(Iterable[AnyStr]): charset: Optional[str] = ..., ) -> None: ... def serialize_headers(self) -> bytes: ... - def __setitem__(self, header: Union[str, bytes], value: Union[str, bytes]) -> None: ... + def __setitem__(self, header: Union[str, bytes], value: Union[str, bytes, int]) -> None: ... def __delitem__(self, header: Union[str, bytes]) -> None: ... def __getitem__(self, header: Union[str, bytes]) -> str: ... def has_header(self, header: str) -> bool: ... diff --git a/django-stubs/middleware/common.pyi b/django-stubs/middleware/common.pyi index 026433f..a5aa8f9 100644 --- a/django-stubs/middleware/common.pyi +++ b/django-stubs/middleware/common.pyi @@ -1,7 +1,7 @@ from typing import Any, Optional from django.http.request import HttpRequest -from django.http.response import HttpResponse, HttpResponseNotFound, HttpResponsePermanentRedirect +from django.http.response import HttpResponseBase, HttpResponsePermanentRedirect from django.utils.deprecation import MiddlewareMixin class CommonMiddleware(MiddlewareMixin): @@ -9,9 +9,9 @@ class CommonMiddleware(MiddlewareMixin): 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: ... + def process_response(self, request: HttpRequest, response: HttpResponseBase) -> HttpResponseBase: ... class BrokenLinkEmailsMiddleware(MiddlewareMixin): - def process_response(self, request: HttpRequest, response: HttpResponseNotFound) -> HttpResponseNotFound: ... + def process_response(self, request: HttpRequest, response: HttpResponseBase) -> HttpResponseBase: ... def is_internal_request(self, domain: str, referer: str) -> bool: ... def is_ignorable_request(self, request: HttpRequest, uri: str, domain: str, referer: str) -> bool: ... diff --git a/django-stubs/template/base.pyi b/django-stubs/template/base.pyi index e612689..c30a4e9 100644 --- a/django-stubs/template/base.pyi +++ b/django-stubs/template/base.pyi @@ -45,6 +45,7 @@ class Origin: @property def loader_name(self) -> Optional[str]: ... + class Template: name: Optional[str] = ... origin: Origin = ... @@ -53,7 +54,7 @@ class Template: nodelist: NodeList = ... def __init__( self, - template_string: str, + template_string: Union[Template, str], origin: Optional[Origin] = ..., name: Optional[str] = ..., engine: Optional[Engine] = ..., diff --git a/django-stubs/utils/formats.pyi b/django-stubs/utils/formats.pyi index be06162..7bf76d0 100644 --- a/django-stubs/utils/formats.pyi +++ b/django-stubs/utils/formats.pyi @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import datetime, date, time from decimal import Decimal from typing import Any, Iterator, List, Optional, Union @@ -8,14 +8,16 @@ FORMAT_SETTINGS: Any def reset_format_cache() -> None: ... def iter_format_modules(lang: str, format_module_path: Optional[Union[List[str], str]] = ...) -> Iterator[Any]: ... def get_format_modules(lang: Optional[str] = ..., reverse: bool = ...) -> List[Any]: ... -def get_format( - format_type: str, lang: Optional[str] = ..., use_l10n: Optional[bool] = ... -) -> Union[List[str], int, str]: ... +def get_format(format_type: str, lang: Optional[str] = ..., use_l10n: Optional[bool] = ...) -> str: ... get_format_lazy: Any -def date_format(value: Union[datetime, str], format: Optional[str] = ..., use_l10n: Optional[bool] = ...) -> str: ... -def time_format(value: Union[datetime, str], format: Optional[str] = ..., use_l10n: None = ...) -> str: ... +def date_format( + value: Union[date, datetime, str], format: Optional[str] = ..., use_l10n: Optional[bool] = ... +) -> str: ... +def time_format( + value: Union[time, datetime, str], format: Optional[str] = ..., use_l10n: Optional[bool] = ... +) -> str: ... def number_format( value: Union[Decimal, float, str], decimal_pos: Optional[int] = ..., diff --git a/django-stubs/utils/log.pyi b/django-stubs/utils/log.pyi index 2cda09d..867d31f 100644 --- a/django-stubs/utils/log.pyi +++ b/django-stubs/utils/log.pyi @@ -1,5 +1,6 @@ import logging.config -from typing import Any, Callable, Dict, Optional +from logging import LogRecord +from typing import Any, Callable, Dict, Optional, Union from django.core.mail.backends.locmem import EmailBackend from django.core.management.color import Style @@ -20,9 +21,13 @@ class AdminEmailHandler(logging.Handler): class CallbackFilter(logging.Filter): callback: Callable = ... def __init__(self, callback: Callable) -> None: ... + def filter(self, record: Union[str, LogRecord]) -> bool: ... -class RequireDebugFalse(logging.Filter): ... -class RequireDebugTrue(logging.Filter): ... +class RequireDebugFalse(logging.Filter): + def filter(self, record: Union[str, LogRecord]) -> bool: ... + +class RequireDebugTrue(logging.Filter): + def filter(self, record: Union[str, LogRecord]) -> bool: ... class ServerFormatter(logging.Formatter): datefmt: None diff --git a/django-stubs/utils/numberformat.pyi b/django-stubs/utils/numberformat.pyi index 42d6ef9..c4c2122 100644 --- a/django-stubs/utils/numberformat.pyi +++ b/django-stubs/utils/numberformat.pyi @@ -1,11 +1,11 @@ from decimal import Decimal -from typing import Any, Optional, Tuple, Union +from typing import Optional, Sequence, Union def format( number: Union[Decimal, float, str], decimal_sep: str, decimal_pos: Optional[int] = ..., - grouping: Union[Tuple[int, int, int], int] = ..., + grouping: Union[int, Sequence[int]] = ..., thousand_sep: str = ..., force_grouping: bool = ..., use_l10n: Optional[bool] = ..., diff --git a/django-stubs/views/generic/edit.pyi b/django-stubs/views/generic/edit.pyi index bbb8bcc..2e2fe5d 100644 --- a/django-stubs/views/generic/edit.pyi +++ b/django-stubs/views/generic/edit.pyi @@ -1,19 +1,21 @@ -from typing import Any, Dict, List, Optional, Type +from typing import Any, Callable, Dict, Optional, Sequence, Type, Union -from django.db import models -from django.forms import models as model_forms, Form # type: ignore # This will be solved when adding forms module -from django.http import HttpResponse, HttpRequest from django.views.generic.base import ContextMixin, TemplateResponseMixin, View from django.views.generic.detail import BaseDetailView, SingleObjectMixin, SingleObjectTemplateResponseMixin +from typing_extensions import Literal + +from django.db import models +from django.forms import Form +from django.http import HttpRequest, HttpResponse class FormMixin(ContextMixin): - initial = ... # type: Dict[str, object] - form_class = ... # type: Optional[Type[Form]] - success_url = ... # type: Optional[str] - prefix = ... # type: Optional[str] - request = ... # type: HttpRequest - def render_to_response(self, context: Dict[str, object], **response_kwargs: object) -> HttpResponse: ... - def get_initial(self) -> Dict[str, object]: ... + initial: Dict[str, Any] = ... + form_class: Optional[Type[Form]] = ... + success_url: Optional[Union[str, Callable[..., Any]]] = ... + prefix: Optional[str] = ... + request: HttpRequest = ... + def render_to_response(self, context: Dict[str, Any], **response_kwargs: object) -> HttpResponse: ... + def get_initial(self) -> Dict[str, Any]: ... def get_prefix(self) -> Optional[str]: ... def get_form_class(self) -> Type[Form]: ... def get_form(self, form_class: Type[Form] = None) -> Form: ... @@ -24,8 +26,8 @@ class FormMixin(ContextMixin): def get_context_data(self, **kwargs: object) -> Dict[str, Any]: ... class ModelFormMixin(FormMixin, SingleObjectMixin): - fields = ... # type: Optional[List[str]] - object = ... # type: models.Model + fields: Optional[Union[Sequence[str], Literal["__all__"]]] = ... + object: models.Model = ... def get_form_class(self) -> Type[Form]: ... def get_form_kwargs(self) -> Dict[str, object]: ... def get_success_url(self) -> str: ... diff --git a/mypy_django_plugin/main.py b/mypy_django_plugin/main.py index 42e8ede..3697546 100644 --- a/mypy_django_plugin/main.py +++ b/mypy_django_plugin/main.py @@ -62,7 +62,11 @@ def return_user_model_hook(ctx: FunctionContext) -> Type: if setting_expr is None: return ctx.default_return_type - app_label, _, model_class_name = get_string_value_from_expr(setting_expr).rpartition('.') + model_path = get_string_value_from_expr(setting_expr) + if model_path is None: + return ctx.default_return_type + + app_label, _, model_class_name = model_path.rpartition('.') if app_label is None: return ctx.default_return_type diff --git a/mypy_django_plugin/plugins/init_create.py b/mypy_django_plugin/plugins/init_create.py index f04679f..3066249 100644 --- a/mypy_django_plugin/plugins/init_create.py +++ b/mypy_django_plugin/plugins/init_create.py @@ -69,11 +69,14 @@ def redefine_and_typecheck_model_init(ctx: FunctionContext) -> Type: def redefine_and_typecheck_model_create(ctx: MethodContext) -> Type: api = cast(TypeChecker, ctx.api) if isinstance(ctx.type, Instance) and len(ctx.type.args) > 0: - model: TypeInfo = ctx.type.args[0].type + model_generic_arg = ctx.type.args[0] else: - if isinstance(ctx.default_return_type, AnyType): - return ctx.default_return_type - model: TypeInfo = ctx.default_return_type.type + model_generic_arg = ctx.default_return_type + + if isinstance(model_generic_arg, AnyType): + return ctx.default_return_type + + model: TypeInfo = model_generic_arg.type # extract name of base models for _ptr base_pointer_args = extract_base_pointer_args(model) diff --git a/scripts/typecheck_tests.py b/scripts/typecheck_tests.py index 3e94e94..24c3391 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', 'call_count', 'call_args_list', 'call_args'] +MOCK_OBJECTS = ['MockRequest', 'MockCompiler', 'modelz', 'call_count', 'call_args_list', 'call_args', 'MockUser'] IGNORED_ERRORS = { '__common__': [ *MOCK_OBJECTS, @@ -28,6 +28,7 @@ IGNORED_ERRORS = { 'Need type annotation for', 'Invalid value for a to= parameter', 'already defined (possibly by an import)', + 'gets multiple values for keyword argument', 'Cannot assign to a type', re.compile(r'Cannot assign to class variable "[a-z_]+" via instance'), # forms <-> models plugin support @@ -135,6 +136,9 @@ IGNORED_ERRORS = { 'dispatch': [ 'Argument 1 to "connect" of "Signal" has incompatible type "object"; expected "Callable[..., Any]"' ], + 'deprecation': [ + re.compile('"(old|new)" undefined in superclass') + ], 'db_typecasts': [ '"object" has no attribute "__iter__"; maybe "__str__" or "__dir__"? (not iterable)' ], @@ -147,6 +151,12 @@ IGNORED_ERRORS = { 'field_deconstruction': [ 'Incompatible types in assignment (expression has type "ForeignKey[Any]", variable has type "CharField")' ], + 'file_uploads': [ + '"handle_uncaught_exception" undefined in superclass' + ], + 'fixtures': [ + 'Incompatible types in assignment (expression has type "int", target has type "Iterable[str]")' + ], 'get_object_or_404': [ 'Argument 1 to "get_object_or_404" has incompatible type "str"; ' + 'expected "Union[Type[], Manager[], QuerySet[]]"', @@ -164,6 +174,9 @@ IGNORED_ERRORS = { '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') + ], 'model_inheritance_regress': [ 'Incompatible types in assignment (expression has type "List[Supplier]", variable has type "QuerySet[Supplier]")' ], @@ -175,6 +188,8 @@ IGNORED_ERRORS = { 'Incompatible types in assignment (expression has type "Type[Person]", variable has type', 'Unexpected keyword argument "name" for "Person"', 'Cannot assign multiple types to name "PersonTwoImages" without an explicit "Type[...]" annotation', + 'Incompatible types in assignment (expression has type "Type[Person]", ' + + 'base class "ImageFieldTestMixin" defined the type as "Type[PersonWithHeightAndWidth]")' ], 'model_regress': [ 'Too many arguments for "Worker"', @@ -200,6 +215,13 @@ IGNORED_ERRORS = { 'FakeLoader', 'Argument 1 to "append" of "list" has incompatible type "AddIndex"; expected "CreateModel"' ], + '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"' ], @@ -294,6 +316,13 @@ IGNORED_ERRORS = { 'select_related_onetoone': [ '"None" has no attribute' ], + '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"' @@ -328,10 +357,10 @@ TESTS_DIRS = [ 'builtin_server', 'bulk_create', # TODO: 'cache', - # TODO: 'check_framework', + 'check_framework', 'choices', 'conditional_processing', - # TODO: 'contenttypes_tests', + 'contenttypes_tests', 'context_processors', 'csrf_tests', 'custom_columns', @@ -347,36 +376,36 @@ TESTS_DIRS = [ 'db_typecasts', 'db_utils', 'dbshell', - # TODO: 'decorators', + 'decorators', 'defer', - # TODO: 'defer_regress', + 'defer_regress', 'delete', 'delete_regress', - # TODO: 'deprecation', + 'deprecation', 'dispatch', 'distinct_on_fields', 'empty', 'expressions', 'expressions_case', - # TODO: 'expressions_window', + 'expressions_window', # TODO: 'extra_regress', 'field_deconstruction', 'field_defaults', 'field_subclassing', # TODO: 'file_storage', - # TODO: 'file_uploads', + 'file_uploads', # TODO: 'files', 'filtered_relation', - # TODO: 'fixtures', + 'fixtures', 'fixtures_model_package', - # TODO: 'fixtures_regress', - # TODO: 'flatpages_tests', + 'fixtures_regress', + 'flatpages_tests', 'force_insert_update', 'foreign_object', # TODO: 'forms_tests', 'from_db_value', - # TODO: 'generic_inline_admin', - # TODO: 'generic_relations', + 'generic_inline_admin', + 'generic_relations', 'generic_relations_regress', # TODO: 'generic_views', 'get_earliest_or_latest', @@ -397,7 +426,7 @@ TESTS_DIRS = [ # 'invalid_models_tests', 'known_related_objects', - # TODO: 'logging_tests', + 'logging_tests', 'lookup', 'm2m_and_m2o', 'm2m_intermediary', @@ -415,13 +444,13 @@ TESTS_DIRS = [ 'many_to_one_null', 'max_lengths', # TODO: 'messages_tests', - # TODO: 'middleware', - # TODO: 'middleware_exceptions', + 'middleware', + 'middleware_exceptions', 'migrate_signals', 'migration_test_data_persistence', 'migrations', 'migrations2', - # TODO: 'model_fields', + 'model_fields', # TODO: 'model_forms', 'model_formsets', 'model_formsets_regress', @@ -433,7 +462,7 @@ TESTS_DIRS = [ 'model_package', 'model_regress', 'modeladmin', - # TODO: 'multiple_database', + 'multiple_database', 'mutually_referential', 'nested_foreign_keys', 'no_models', @@ -451,7 +480,7 @@ TESTS_DIRS = [ 'properties', 'proxy_model_inheritance', # TODO: 'proxy_models', - # TODO: 'queries', + 'queries', 'queryset_pickle', 'raw_query', 'redirects_tests', @@ -467,7 +496,7 @@ TESTS_DIRS = [ 'select_related_onetoone', 'select_related_regress', # TODO: 'serializers', - # TODO: 'servers', + 'servers', 'sessions_tests', 'settings_tests', 'shell', @@ -503,7 +532,6 @@ TESTS_DIRS = [ # TODO: 'urlpatterns_reverse', 'user_commands', # TODO: 'utils_tests', - # not annotatable without annotation in test # TODO: 'validation', 'validators', 'version',