mirror of
https://github.com/davidhalter/django-stubs.git
synced 2025-12-08 13:04:47 +08:00
split error suppression for tests typechecking, fix ci, bunch of fixes
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
from django.contrib.admin.decorators import register as register
|
||||
from django.contrib.admin.filters import (
|
||||
from .decorators import register as register
|
||||
from .filters import (
|
||||
AllValuesFieldListFilter as AllValuesFieldListFilter,
|
||||
BooleanFieldListFilter as BooleanFieldListFilter,
|
||||
ChoicesFieldListFilter as ChoicesFieldListFilter,
|
||||
@@ -10,14 +10,15 @@ from django.contrib.admin.filters import (
|
||||
RelatedOnlyFieldListFilter as RelatedOnlyFieldListFilter,
|
||||
SimpleListFilter as SimpleListFilter,
|
||||
)
|
||||
from django.contrib.admin.helpers import ACTION_CHECKBOX_NAME as ACTION_CHECKBOX_NAME
|
||||
from django.contrib.admin.options import (
|
||||
from .helpers import ACTION_CHECKBOX_NAME as ACTION_CHECKBOX_NAME
|
||||
from .options import (
|
||||
HORIZONTAL as HORIZONTAL,
|
||||
VERTICAL as VERTICAL,
|
||||
ModelAdmin as ModelAdmin,
|
||||
StackedInline as StackedInline,
|
||||
TabularInline as TabularInline,
|
||||
)
|
||||
from django.contrib.admin.sites import AdminSite as AdminSite, site as site
|
||||
from .sites import AdminSite as AdminSite, site as site
|
||||
from . import checks as checks
|
||||
|
||||
def autodiscover() -> None: ...
|
||||
|
||||
@@ -11,11 +11,8 @@ def check_dependencies(**kwargs: Any) -> List[_CheckError]: ...
|
||||
class BaseModelAdminChecks:
|
||||
def check(self, admin_obj: BaseModelAdmin, **kwargs: Any) -> List[_CheckError]: ...
|
||||
|
||||
class ModelAdminChecks(BaseModelAdminChecks):
|
||||
def check(self, admin_obj: ModelAdmin, **kwargs: Any) -> List[_CheckError]: ...
|
||||
|
||||
class InlineModelAdminChecks(BaseModelAdminChecks):
|
||||
def check(self, inline_obj: InlineModelAdmin, **kwargs: Any) -> List[_CheckError]: ...
|
||||
class ModelAdminChecks(BaseModelAdminChecks): ...
|
||||
class InlineModelAdminChecks(BaseModelAdminChecks): ...
|
||||
|
||||
def must_be(type: Any, option: Any, obj: Any, id: Any): ...
|
||||
def must_inherit_from(parent: Any, option: Any, obj: Any, id: Any): ...
|
||||
|
||||
@@ -21,6 +21,7 @@ from django.urls.resolvers import URLPattern
|
||||
from django.utils.safestring import SafeText
|
||||
|
||||
from django.db.models.fields import Field
|
||||
from django.template.response import TemplateResponse
|
||||
|
||||
IS_POPUP_VAR: str
|
||||
TO_FIELD_VAR: str
|
||||
@@ -229,7 +230,7 @@ class ModelAdmin(BaseModelAdmin):
|
||||
) -> HttpResponse: ...
|
||||
def changelist_view(
|
||||
self, request: WSGIRequest, extra_context: Optional[Dict[str, str]] = ...
|
||||
) -> HttpResponseBase: ...
|
||||
) -> TemplateResponse: ...
|
||||
def get_deleted_objects(
|
||||
self, objs: QuerySet, request: WSGIRequest
|
||||
) -> Tuple[List[Any], Dict[Any, Any], Set[Any], List[Any]]: ...
|
||||
|
||||
@@ -3,12 +3,14 @@ from datetime import datetime
|
||||
from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union
|
||||
from uuid import UUID
|
||||
|
||||
from django.forms.models import ModelChoiceIterator
|
||||
|
||||
from django import forms
|
||||
from django.contrib.admin.sites import AdminSite
|
||||
from django.db.models.fields.reverse_related import ForeignObjectRel
|
||||
from django.db.models.fields.reverse_related import ForeignObjectRel, ManyToOneRel, ManyToManyRel
|
||||
from django.db.models.query_utils import Q
|
||||
from django.forms.fields import Field
|
||||
from django.forms.widgets import ChoiceWidget, Media, Widget
|
||||
from django.forms.widgets import ChoiceWidget, Media, Widget, DateTimeBaseInput
|
||||
from django.http.request import QueryDict
|
||||
from django.utils.datastructures import MultiValueDict
|
||||
|
||||
@@ -92,7 +94,7 @@ def url_params_from_lookup_dict(
|
||||
class ForeignKeyRawIdWidget(forms.TextInput):
|
||||
attrs: Dict[Any, Any]
|
||||
template_name: str = ...
|
||||
rel: django.db.models.fields.reverse_related.ManyToOneRel = ...
|
||||
rel: ManyToOneRel = ...
|
||||
admin_site: AdminSite = ...
|
||||
db: None = ...
|
||||
def __init__(self, rel: ForeignObjectRel, admin_site: AdminSite, attrs: None = ..., using: None = ...) -> None: ...
|
||||
@@ -107,7 +109,7 @@ class ManyToManyRawIdWidget(ForeignKeyRawIdWidget):
|
||||
admin_site: AdminSite
|
||||
attrs: Dict[Any, Any]
|
||||
db: None
|
||||
rel: django.db.models.fields.reverse_related.ManyToManyRel
|
||||
rel: ManyToManyRel
|
||||
template_name: str = ...
|
||||
def get_context(
|
||||
self, name: str, value: Optional[List[int]], attrs: Optional[Dict[str, str]]
|
||||
@@ -122,8 +124,8 @@ class RelatedFieldWidgetWrapper(forms.Widget):
|
||||
needs_multipart_form: bool = ...
|
||||
attrs: Dict[Any, Any] = ...
|
||||
choices: ModelChoiceIterator = ...
|
||||
widget: django.contrib.admin.widgets.AutocompleteSelect = ...
|
||||
rel: django.db.models.fields.reverse_related.ManyToOneRel = ...
|
||||
widget: AutocompleteSelect = ...
|
||||
rel: ManyToOneRel = ...
|
||||
can_add_related: bool = ...
|
||||
can_change_related: bool = ...
|
||||
can_delete_related: bool = ...
|
||||
|
||||
@@ -14,6 +14,9 @@ class SupportsLen(Protocol):
|
||||
class SupportsCount(Protocol):
|
||||
def count(self) -> int: ...
|
||||
|
||||
class SupportsOrdered(Protocol):
|
||||
ordered: bool = ...
|
||||
|
||||
class Paginator:
|
||||
object_list: QuerySet = ...
|
||||
per_page: int = ...
|
||||
@@ -21,7 +24,7 @@ class Paginator:
|
||||
allow_empty_first_page: bool = ...
|
||||
def __init__(
|
||||
self,
|
||||
object_list: Union[SupportsLen, SupportsCount],
|
||||
object_list: Union[SupportsLen, SupportsCount, SupportsOrdered],
|
||||
per_page: Union[int, str],
|
||||
orphans: int = ...,
|
||||
allow_empty_first_page: bool = ...,
|
||||
|
||||
6
django-stubs/core/signals.pyi
Normal file
6
django-stubs/core/signals.pyi
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.dispatch import Signal
|
||||
|
||||
request_started: Signal = ...
|
||||
request_finished: Signal = ...
|
||||
got_request_exception: Signal = ...
|
||||
setting_changed: Signal = ...
|
||||
@@ -47,6 +47,7 @@ from .fields.related import (
|
||||
ForeignObject as ForeignObject,
|
||||
)
|
||||
from .fields.files import ImageField as ImageField, FileField as FileField
|
||||
from .fields.proxy import OrderWrt as OrderWrt
|
||||
|
||||
from .deletion import (
|
||||
CASCADE as CASCADE,
|
||||
@@ -54,6 +55,7 @@ from .deletion import (
|
||||
SET_NULL as SET_NULL,
|
||||
DO_NOTHING as DO_NOTHING,
|
||||
PROTECT as PROTECT,
|
||||
SET as SET,
|
||||
)
|
||||
|
||||
from .query import (
|
||||
@@ -97,3 +99,5 @@ from .aggregates import (
|
||||
)
|
||||
|
||||
from .indexes import Index as Index
|
||||
|
||||
from . import signals as signals
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from typing import Any, Callable
|
||||
|
||||
from django.db import IntegrityError
|
||||
|
||||
def CASCADE(collector, field, sub_objs, using): ...
|
||||
@@ -5,5 +7,6 @@ def SET_NULL(collector, field, sub_objs, using): ...
|
||||
def SET_DEFAULT(collector, field, sub_objs, using): ...
|
||||
def DO_NOTHING(collector, field, sub_objs, using): ...
|
||||
def PROTECT(collector, field, sub_objs, using): ...
|
||||
def SET(value: Any) -> Callable: ...
|
||||
|
||||
class ProtectedError(IntegrityError): ...
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import Any, Optional
|
||||
from typing import Any
|
||||
|
||||
from django.db.models import fields
|
||||
|
||||
|
||||
@@ -152,8 +152,10 @@ class Prefetch(object):
|
||||
def get_current_queryset(self, level) -> Optional[QuerySet]: ...
|
||||
|
||||
def prefetch_related_objects(model_instances: Iterable[_T], *related_lookups: Union[str, Prefetch]) -> None: ...
|
||||
def get_prefetcher(instance: _T, through_attr: str, to_attr: str) -> Tuple[Any, Any, bool, bool]: ...
|
||||
def get_prefetcher(instance: Model, through_attr: str, to_attr: str) -> Tuple[Any, Any, bool, bool]: ...
|
||||
|
||||
class ModelIterable(Iterable[_T]):
|
||||
def __iter__(self) -> Iterator[_T]: ...
|
||||
|
||||
class ModelIterable(Iterable[_T]): ...
|
||||
class InstanceCheckMeta(type): ...
|
||||
class EmptyQuerySet(metaclass=InstanceCheckMeta): ...
|
||||
|
||||
@@ -7,7 +7,7 @@ from django.dispatch import Signal
|
||||
class_prepared: Any
|
||||
|
||||
class ModelSignal(Signal):
|
||||
def connect(
|
||||
def connect( # type: ignore
|
||||
self,
|
||||
receiver: Callable,
|
||||
sender: Optional[Union[Type[Model], str]] = ...,
|
||||
@@ -15,7 +15,7 @@ class ModelSignal(Signal):
|
||||
dispatch_uid: None = ...,
|
||||
apps: Optional[Apps] = ...,
|
||||
) -> None: ...
|
||||
def disconnect(
|
||||
def disconnect( # type: ignore
|
||||
self,
|
||||
receiver: Callable = ...,
|
||||
sender: Optional[Union[Type[Model], str]] = ...,
|
||||
|
||||
@@ -15,15 +15,15 @@ class Signal:
|
||||
def __init__(self, providing_args: List[str] = ..., use_caching: bool = ...) -> None: ...
|
||||
def connect(
|
||||
self,
|
||||
receiver: Any,
|
||||
sender: Optional[Union[Type[Model], AppConfig]] = ...,
|
||||
receiver: Callable,
|
||||
sender: Optional[Union[Type[Model], AppConfig, str]] = ...,
|
||||
weak: bool = ...,
|
||||
dispatch_uid: Optional[str] = ...,
|
||||
) -> None: ...
|
||||
def disconnect(
|
||||
self,
|
||||
receiver: Optional[Callable] = ...,
|
||||
sender: Optional[Union[Type[Model], AppConfig]] = ...,
|
||||
sender: Optional[Union[Type[Model], AppConfig, str]] = ...,
|
||||
dispatch_uid: Optional[str] = ...,
|
||||
) -> bool: ...
|
||||
def has_listeners(self, sender: Any = ...) -> bool: ...
|
||||
|
||||
@@ -34,6 +34,9 @@ from .widgets import (
|
||||
SelectMultiple as SelectMultiple,
|
||||
TimeInput as TimeInput,
|
||||
URLInput as URLInput,
|
||||
SelectDateWidget as SelectDateWidget,
|
||||
SplitHiddenDateTimeWidget as SplitHiddenDateTimeWidget,
|
||||
SplitDateTimeWidget as SplitDateTimeWidget,
|
||||
)
|
||||
|
||||
from .fields import (
|
||||
|
||||
@@ -159,6 +159,7 @@ class SelectMultiple(Select):
|
||||
allow_multiple_selected: bool = ...
|
||||
|
||||
class RadioSelect(ChoiceWidget):
|
||||
can_add_related: bool
|
||||
option_template_name: str = ...
|
||||
|
||||
class CheckboxSelectMultiple(ChoiceWidget):
|
||||
|
||||
@@ -12,3 +12,5 @@ from .base import Node as Node, NodeList as NodeList, Origin as Origin, Template
|
||||
from .context import Context as Context, RequestContext as RequestContext
|
||||
|
||||
from .library import Library as Library
|
||||
|
||||
from . import defaultfilters as defaultfilters
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from datetime import date, datetime, timedelta
|
||||
from datetime import _date, datetime, timedelta
|
||||
from decimal import Decimal
|
||||
from typing import Any, Callable, Dict, Iterator, List, Optional, Tuple, Union
|
||||
|
||||
@@ -72,14 +72,14 @@ def unordered_list(
|
||||
value: Union[Iterator[Any], List[Union[List[Union[List[Union[List[str], str]], str]], str]]], autoescape: bool = ...
|
||||
) -> SafeText: ...
|
||||
def add(
|
||||
value: Union[List[int], Tuple[int, int], date, int, str],
|
||||
value: Union[List[int], Tuple[int, int], _date, int, str],
|
||||
arg: Union[List[int], Tuple[int, int], timedelta, int, str],
|
||||
) -> Union[List[int], Tuple[int, int, int, int], date, int, str]: ...
|
||||
) -> Union[List[int], Tuple[int, int, int, int], _date, int, str]: ...
|
||||
def get_digit(value: Union[int, str], arg: int) -> Union[int, str]: ...
|
||||
def date(value: Optional[Union[datetime, str]], arg: Optional[str] = ...) -> str: ...
|
||||
def date(value: Optional[Union[_date, datetime, str]], arg: Optional[str] = ...) -> str: ...
|
||||
def time(value: Optional[Union[datetime, str]], arg: Optional[str] = ...) -> str: ...
|
||||
def timesince_filter(value: Optional[date], arg: Optional[date] = ...) -> str: ...
|
||||
def timeuntil_filter(value: Optional[date], arg: Optional[date] = ...) -> str: ...
|
||||
def timesince_filter(value: Optional[_date], arg: Optional[_date] = ...) -> str: ...
|
||||
def timeuntil_filter(value: Optional[_date], arg: Optional[_date] = ...) -> str: ...
|
||||
def default(value: Optional[Union[int, str]], arg: Union[int, str]) -> Union[int, str]: ...
|
||||
def default_if_none(value: Optional[str], arg: Union[int, str]) -> Union[int, str]: ...
|
||||
def divisibleby(value: int, arg: int) -> bool: ...
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from typing import Any
|
||||
from django.core.signals import setting_changed as setting_changed
|
||||
|
||||
template_rendered: Any
|
||||
COMPLEX_OVERRIDE_SETTINGS: Any
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import decimal
|
||||
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
|
||||
@@ -107,13 +108,13 @@ class isolate_apps(TestContextDecorator):
|
||||
@contextmanager
|
||||
def extend_sys_path(*paths: str) -> Iterator[None]: ...
|
||||
@contextmanager
|
||||
def captured_output(stream_name) -> Iterator[IO[str]]: ...
|
||||
def captured_output(stream_name) -> Iterator[StringIO]: ...
|
||||
@contextmanager
|
||||
def captured_stdin() -> Iterator[IO[str]]: ...
|
||||
def captured_stdin() -> Iterator[StringIO]: ...
|
||||
@contextmanager
|
||||
def captured_stdout() -> Iterator[IO[str]]: ...
|
||||
def captured_stdout() -> Iterator[StringIO]: ...
|
||||
@contextmanager
|
||||
def captured_stderr() -> Iterator[IO[str]]: ...
|
||||
def captured_stderr() -> Iterator[StringIO]: ...
|
||||
@contextmanager
|
||||
def freeze_time(t: float) -> Iterator[None]: ...
|
||||
def tag(*tags: str): ...
|
||||
|
||||
@@ -9,6 +9,8 @@ from git import Repo
|
||||
from mypy import build
|
||||
from mypy.main import process_options
|
||||
|
||||
PROJECT_DIRECTORY = Path(__file__).parent.parent
|
||||
|
||||
# Django branch to typecheck against
|
||||
DJANGO_BRANCH = 'stable/2.1.x'
|
||||
|
||||
@@ -17,64 +19,114 @@ 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"',
|
||||
'Definition of "as_sql" in base class',
|
||||
'expression has type "property"',
|
||||
'"object" has no attribute "__iter__"',
|
||||
'Too few arguments for "dates" of "QuerySet"',
|
||||
'has no attribute "vendor"',
|
||||
'Argument 1 to "get_list_or_404" has incompatible type "List',
|
||||
'error: "AdminRadioSelect" has no attribute "can_add_related"',
|
||||
'MockCompiler',
|
||||
'SessionTestsMixin',
|
||||
'Argument 1 to "Paginator" has incompatible type "ObjectList"',
|
||||
'"Type[Morsel[Any]]" has no attribute "_reserved"',
|
||||
'Argument 1 to "append" of "list"',
|
||||
'Argument 1 to "bytes"',
|
||||
'"full_clean" of "Model" does not return a value',
|
||||
'"object" not callable',
|
||||
'Item "GenericForeignKey" of "Union[GenericForeignKey, Model, None]" has no attribute "read_by"',
|
||||
'Item "Model" of "Union[GenericForeignKey, Model, None]" has no attribute "read_by"',
|
||||
re.compile('Cannot determine type of \'(objects|stuff|specimens|normal_manager)\''),
|
||||
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)\[[A-Za-z, ]+\]", '
|
||||
r'variable has type "(QuerySet|List)\[[A-Za-z, ]+\]"\)'),
|
||||
re.compile(r'"(MockRequest|DummyRequest|DummyUser)" has no attribute "[a-zA-Z_]+"'),
|
||||
# TODO: remove when form <-> model plugin support is added
|
||||
re.compile(r'"Model" has no attribute "[A-Za-z_]+"'),
|
||||
re.compile(r'Argument 1 to "get_object_or_404" has incompatible type "(str|Type\[CustomClass\])"'),
|
||||
re.compile(r'"None" has no attribute "[a-zA-Z_0-9]+"'),
|
||||
]
|
||||
|
||||
MOCK_OBJECTS = ['MockRequest', 'MockCompiler', 'modelz']
|
||||
IGNORED_ERRORS = {
|
||||
'__common__': [
|
||||
*MOCK_OBJECTS,
|
||||
'LazySettings',
|
||||
'NullTranslations',
|
||||
'Need type annotation for',
|
||||
'Invalid value for a to= parameter',
|
||||
'already defined (possibly by an import)',
|
||||
'Cannot assign to a type',
|
||||
# forms <-> models plugin support
|
||||
'"Model" has no attribute',
|
||||
re.compile(r'Cannot determine type of \'(objects|stuff)\''),
|
||||
# settings
|
||||
re.compile(r'Module has no attribute "[A-Z_]+"'),
|
||||
# attributes assigned to test functions
|
||||
re.compile(r'"Callable\[\[(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, ]+\]"'),
|
||||
# assign method to a method
|
||||
'Cannot assign to a method',
|
||||
'Cannot infer type of lambda',
|
||||
re.compile(r'Incompatible types in assignment \(expression has type "Callable\[\[(Any(, )?)+\], Any\]", '
|
||||
r'variable has type "Callable\['),
|
||||
],
|
||||
'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 "Widget", variable has type "AutocompleteSelect")'
|
||||
],
|
||||
'aggregation': [
|
||||
'Incompatible types in assignment (expression has type "QuerySet[Any]", variable has type "List[Any]")',
|
||||
'"as_sql" undefined in superclass'
|
||||
],
|
||||
'aggregation_regress': [
|
||||
'Incompatible types in assignment (expression has type "List[str]", variable has type "QuerySet[Author]")'
|
||||
],
|
||||
'basic': [
|
||||
'Unexpected keyword argument "unknown_kwarg" for "refresh_from_db" of "Model"',
|
||||
'"refresh_from_db" of "Model" defined here'
|
||||
],
|
||||
'builtin_server': [
|
||||
'has no attribute "getvalue"'
|
||||
],
|
||||
'csrf_tests': [
|
||||
'Incompatible types in assignment (expression has type "property", ' +
|
||||
'base class "HttpRequest" defined the type as "QueryDict")'
|
||||
],
|
||||
'dates': [
|
||||
'Too few arguments for "dates" of "QuerySet"',
|
||||
],
|
||||
'defer': [
|
||||
'Too many arguments for "refresh_from_db" of "Model"'
|
||||
],
|
||||
'db_typecasts': [
|
||||
'"object" has no attribute "__iter__"; maybe "__str__" or "__dir__"? (not iterable)'
|
||||
],
|
||||
'from_db_value': [
|
||||
'has no attribute "vendor"'
|
||||
],
|
||||
'get_object_or_404': [
|
||||
'Argument 1 to "get_object_or_404" has incompatible type "str"; '
|
||||
+ 'expected "Union[Type[Model], Manager[Any], QuerySet[Any]]"',
|
||||
'Argument 1 to "get_object_or_404" has incompatible type "Type[CustomClass]"; '
|
||||
+ 'expected "Union[Type[Model], Manager[Any], QuerySet[Any]]"',
|
||||
'Argument 1 to "get_list_or_404" has incompatible type "List[Type[Article]]"; '
|
||||
+ 'expected "Union[Type[Model], Manager[Any], QuerySet[Any]]"'
|
||||
],
|
||||
'model_inheritance_regress': [
|
||||
'Incompatible types in assignment (expression has type "List[Supplier]", variable has type "QuerySet[Supplier]")'
|
||||
],
|
||||
'model_meta': [
|
||||
'"object" has no attribute "items"',
|
||||
'"Field" has no attribute "many_to_many"'
|
||||
],
|
||||
'migrate_signals': [
|
||||
'Value of type "None" is not indexable',
|
||||
],
|
||||
'queryset_pickle': [
|
||||
'"None" has no attribute "somefield"'
|
||||
],
|
||||
'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"'
|
||||
],
|
||||
'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'
|
||||
],
|
||||
'sessions_tests': [
|
||||
'base class "SessionTestsMixin" defined the type as "None")',
|
||||
'has no attribute "_reserved"'
|
||||
],
|
||||
'select_related_onetoone': [
|
||||
'"None" has no attribute'
|
||||
]
|
||||
}
|
||||
# Test folders to typecheck
|
||||
TESTS_DIRS = [
|
||||
'absolute_url_overrides',
|
||||
@@ -190,7 +242,7 @@ TESTS_DIRS = [
|
||||
# TODO: 'messages_tests',
|
||||
# TODO: 'middleware',
|
||||
# TODO: 'middleware_exceptions',
|
||||
# SKIPPED (all errors are false positives) 'migrate_signals',
|
||||
'migrate_signals',
|
||||
'migration_test_data_persistence',
|
||||
# TODO: 'migrations',
|
||||
'migrations2',
|
||||
@@ -201,7 +253,7 @@ TESTS_DIRS = [
|
||||
'model_indexes',
|
||||
# TODO: 'model_inheritance',
|
||||
'model_inheritance_regress',
|
||||
# SKIPPED (all errors are false positives) 'model_meta',
|
||||
'model_meta',
|
||||
'model_options',
|
||||
'model_package',
|
||||
'model_regress',
|
||||
@@ -298,8 +350,8 @@ def cd(path):
|
||||
os.chdir(prev_cwd)
|
||||
|
||||
|
||||
def is_ignored(line: str) -> bool:
|
||||
for pattern in IGNORED_ERROR_PATTERNS:
|
||||
def is_ignored(line: str, test_folder_name: str) -> bool:
|
||||
for pattern in IGNORED_ERRORS['__common__'] + IGNORED_ERRORS.get(test_folder_name, []):
|
||||
if isinstance(pattern, Pattern):
|
||||
if pattern.search(line):
|
||||
return True
|
||||
@@ -309,22 +361,29 @@ def is_ignored(line: str) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
def replace_with_clickable_location(error: str, abs_test_folder: Path) -> str:
|
||||
raw_path, _, error_line = error.partition(': ')
|
||||
fname, line_number = raw_path.split(':')
|
||||
path = abs_test_folder.joinpath(fname).relative_to(PROJECT_DIRECTORY)
|
||||
clickable_location = f'./{path}:{line_number}'
|
||||
return error.replace(raw_path, clickable_location)
|
||||
|
||||
|
||||
def check_with_mypy(abs_path: Path, config_file_path: Path) -> int:
|
||||
error_happened = False
|
||||
with cd(abs_path):
|
||||
sources, options = process_options(['--config-file', str(config_file_path), str(abs_path)])
|
||||
res = build.build(sources, options)
|
||||
for error_line in res.errors:
|
||||
if not is_ignored(error_line):
|
||||
if not is_ignored(error_line, abs_path.name):
|
||||
error_happened = True
|
||||
print(error_line)
|
||||
print(replace_with_clickable_location(error_line, abs_test_folder=abs_path))
|
||||
return int(error_happened)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
project_directory = Path(__file__).parent.parent
|
||||
mypy_config_file = (project_directory / 'scripts' / 'mypy.ini').absolute()
|
||||
repo_directory = project_directory / 'django-sources'
|
||||
mypy_config_file = (PROJECT_DIRECTORY / 'scripts' / 'mypy.ini').absolute()
|
||||
repo_directory = PROJECT_DIRECTORY / 'django-sources'
|
||||
tests_root = repo_directory / 'tests'
|
||||
global_rc = 0
|
||||
|
||||
@@ -337,8 +396,8 @@ if __name__ == '__main__':
|
||||
|
||||
repo.git.checkout(DJANGO_COMMIT_SHA)
|
||||
for dirname in TESTS_DIRS:
|
||||
abs_path = (project_directory / tests_root / dirname).absolute()
|
||||
print(f'Checking {abs_path.as_uri()}')
|
||||
abs_path = (PROJECT_DIRECTORY / tests_root / dirname).absolute()
|
||||
print(f'Checking {abs_path}')
|
||||
|
||||
rc = check_with_mypy(abs_path, mypy_config_file)
|
||||
if rc != 0:
|
||||
|
||||
Reference in New Issue
Block a user