Skip to content

Commit 6226381

Browse files
authored
Recover after #909 (#925)
* Fix stubs related to `(Async)RequestFactory` and `(Async)Client` * Revert incorrect removal. * Allow set as `unique_together`, use shared type alias. * Revert `Q.__init__` to use only `*args, **kwargs` to remove false-positive with `Q(**{...})` * Add abstract methods to `HttpResponseBase` to create common interface. * Remove monkey-patched attributes from `HttpResponseBase` subclasses. * Add QueryDict mutability checks (+ plugin support) * Fix lint * Return back GenericForeignKey to `Options.get_fields` * Minor fixup * Make plugin code typecheck with `--warn-unreachable`, minor performance increase. * Better types for `{unique, index}_together` and Options. * Fix odd type of `URLResolver.urlconf_name` which isn't a str actually. * Better types for field migration operations. * Revert form.files to `MultiValueDict[str, UploadedFile]` * Compatibility fix (#916) * Do not assume that `Annotated` is always related to django-stubs (fixes #893) * Restrict `FormView.get_form` return type to `_FormT` (class type argument). Now it is resolved to `form_class` argument if present, but also errors if it is not subclass of _FormT * Fix CI (make test runnable on 3.8) * Fix CI (make test runnable on 3.8 _again_)
1 parent 16499a2 commit 6226381

File tree

29 files changed

+380
-138
lines changed

29 files changed

+380
-138
lines changed

django-stubs/core/handlers/asgi.pyi

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ from typing import (
1515
)
1616

1717
from django.core.handlers import base as base
18-
from django.http import HttpRequest, QueryDict
18+
from django.http.request import HttpRequest, _ImmutableQueryDict
1919
from django.http.response import HttpResponseBase
2020
from django.urls.resolvers import ResolverMatch, URLResolver
2121
from django.utils.datastructures import MultiValueDict
@@ -34,8 +34,8 @@ class ASGIRequest(HttpRequest):
3434
META: Dict[str, Any] = ...
3535
def __init__(self, scope: Mapping[str, Any], body_file: IO[bytes]) -> None: ...
3636
@property
37-
def GET(self) -> QueryDict: ... # type: ignore
38-
POST: QueryDict = ...
37+
def GET(self) -> _ImmutableQueryDict: ... # type: ignore
38+
POST: _ImmutableQueryDict = ...
3939
FILES: MultiValueDict = ...
4040
@property
4141
def COOKIES(self) -> Dict[str, str]: ... # type: ignore

django-stubs/db/migrations/operations/fields.pyi

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Any, Optional
1+
from typing import Optional
22

33
from django.db.models.fields import Field
44

@@ -23,15 +23,15 @@ class AddField(FieldOperation):
2323
class RemoveField(FieldOperation): ...
2424

2525
class AlterField(FieldOperation):
26-
field: Any = ...
27-
preserve_default: Any = ...
26+
field: Field = ...
27+
preserve_default: bool = ...
2828
def __init__(self, model_name: str, name: str, field: Field, preserve_default: bool = ...) -> None: ...
2929

3030
class RenameField(FieldOperation):
31-
old_name: Any = ...
32-
new_name: Any = ...
31+
old_name: str = ...
32+
new_name: str = ...
3333
def __init__(self, model_name: str, old_name: str, new_name: str) -> None: ...
3434
@property
35-
def old_name_lower(self): ...
35+
def old_name_lower(self) -> str: ...
3636
@property
3737
def new_name_lower(self) -> str: ...

django-stubs/db/migrations/operations/models.pyi

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Any, Dict, List, Optional, Sequence, Set, Tuple, Type, TypeVar, Union
1+
from typing import Any, Dict, List, Optional, Sequence, Set, Tuple, Type, Union
22

33
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
44
from django.db.migrations.operations.base import Operation
@@ -7,9 +7,7 @@ from django.db.models.constraints import BaseConstraint
77
from django.db.models.fields import Field
88
from django.db.models.indexes import Index
99
from django.db.models.manager import Manager
10-
from django.utils.datastructures import _ListOrTuple
11-
12-
_T = TypeVar("_T")
10+
from django.db.models.options import _OptionTogetherT
1311

1412
class ModelOperation(Operation):
1513
name: str = ...
@@ -53,10 +51,10 @@ class AlterTogetherOptionOperation(ModelOptionOperation):
5351
def __init__(
5452
self,
5553
name: str,
56-
option_value: _ListOrTuple[Tuple[str, str]],
54+
option_value: Optional[_OptionTogetherT],
5755
) -> None: ...
5856
@property
59-
def option_value(self) -> Set[Tuple[str, str]]: ...
57+
def option_value(self) -> Optional[Set[Tuple[str, ...]]]: ...
6058
def deconstruct(self) -> Tuple[str, Sequence[Any], Dict[str, Any]]: ...
6159
def state_forwards(self, app_label: str, state: Any) -> None: ...
6260
def database_forwards(
@@ -72,26 +70,26 @@ class AlterTogetherOptionOperation(ModelOptionOperation):
7270

7371
class AlterUniqueTogether(AlterTogetherOptionOperation):
7472
option_name: str = ...
75-
unique_together: _ListOrTuple[Tuple[str, str]] = ...
76-
def __init__(self, name: str, unique_together: _ListOrTuple[Tuple[str, str]]) -> None: ...
73+
unique_together: Optional[Set[Tuple[str, ...]]] = ...
74+
def __init__(self, name: str, unique_together: Optional[_OptionTogetherT]) -> None: ...
7775

7876
class AlterIndexTogether(AlterTogetherOptionOperation):
7977
option_name: str = ...
80-
index_together: _ListOrTuple[Tuple[str, str]] = ...
81-
def __init__(self, name: str, index_together: _ListOrTuple[Tuple[str, str]]) -> None: ...
78+
index_together: Optional[Set[Tuple[str, ...]]] = ...
79+
def __init__(self, name: str, index_together: Optional[_OptionTogetherT]) -> None: ...
8280

8381
class AlterOrderWithRespectTo(ModelOptionOperation):
8482
order_with_respect_to: str = ...
8583
def __init__(self, name: str, order_with_respect_to: str) -> None: ...
8684

8785
class AlterModelOptions(ModelOptionOperation):
88-
ALTER_OPTION_KEYS: Any = ...
89-
options: Dict[str, str] = ...
86+
ALTER_OPTION_KEYS: List[str] = ...
87+
options: Dict[str, Any] = ...
9088
def __init__(self, name: str, options: Dict[str, Any]) -> None: ...
9189

9290
class AlterModelManagers(ModelOptionOperation):
93-
managers: Any = ...
94-
def __init__(self, name: Any, managers: Any) -> None: ...
91+
managers: Sequence[Manager] = ...
92+
def __init__(self, name: str, managers: Sequence[Manager]) -> None: ...
9593

9694
class IndexOperation(Operation):
9795
option_name: str = ...

django-stubs/db/migrations/operations/special.pyi

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
11
import sys
2-
from typing import Any, Dict, List, Mapping, Optional, Sequence, Tuple, Union
2+
from typing import Any, Mapping, Optional, Sequence, Union
33

44
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
55
from django.db.migrations.state import StateApps
6-
7-
if sys.version_info < (3, 8):
8-
from typing_extensions import Literal
9-
else:
10-
from typing import Literal
6+
from django.utils.datastructures import _ListOrTuple
117

128
from .base import Operation
139

@@ -25,14 +21,14 @@ class SeparateDatabaseAndState(Operation):
2521

2622
class RunSQL(Operation):
2723
noop: Literal[""] = ...
28-
sql: Union[str, List[str], Tuple[str, ...]] = ...
29-
reverse_sql: Optional[Union[str, List[str], Tuple[str, ...]]] = ...
24+
sql: Union[str, _ListOrTuple[str]] = ...
25+
reverse_sql: Optional[Union[str, _ListOrTuple[str]]] = ...
3026
state_operations: Sequence[Operation] = ...
3127
hints: Mapping[str, Any] = ...
3228
def __init__(
3329
self,
34-
sql: Union[str, List[str], Tuple[str, ...]],
35-
reverse_sql: Optional[Union[str, List[str], Tuple[str, ...]]] = ...,
30+
sql: Union[str, _ListOrTuple[str]],
31+
reverse_sql: Optional[Union[str, _ListOrTuple[str]]] = ...,
3632
state_operations: Sequence[Operation] = ...,
3733
hints: Optional[Mapping[str, Any]] = ...,
3834
elidable: bool = ...,
@@ -44,13 +40,13 @@ class _CodeCallable(Protocol):
4440
class RunPython(Operation):
4541
code: _CodeCallable = ...
4642
reverse_code: Optional[_CodeCallable] = ...
47-
hints: Optional[Dict[str, Any]] = ...
43+
hints: Mapping[str, Any] = ...
4844
def __init__(
4945
self,
5046
code: _CodeCallable,
5147
reverse_code: Optional[_CodeCallable] = ...,
5248
atomic: Optional[bool] = ...,
53-
hints: Optional[Dict[str, Any]] = ...,
49+
hints: Optional[Mapping[str, Any]] = ...,
5450
elidable: bool = ...,
5551
) -> None: ...
5652
@staticmethod

django-stubs/db/models/options.pyi

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
from typing import Any, Dict, Generic, Iterable, List, Optional, Sequence, Set, Tuple, Type, TypeVar, Union
1+
import sys
2+
from typing import Any, Dict, Generic, Iterable, List, Optional, Sequence, Set, Tuple, Type, TypeVar, Union, overload
23

34
from django.apps.config import AppConfig
45
from django.apps.registry import Apps
@@ -11,16 +12,26 @@ from django.db.models.fields.related import ManyToManyField, OneToOneField
1112
from django.db.models.fields.reverse_related import ForeignObjectRel
1213
from django.db.models.manager import Manager
1314
from django.db.models.query_utils import PathInfo
14-
from django.utils.datastructures import ImmutableList
15+
from django.utils.datastructures import ImmutableList, _ListOrTuple
16+
17+
if sys.version_info < (3, 8):
18+
from typing_extensions import Literal
19+
else:
20+
from typing import Literal
1521

1622
PROXY_PARENTS: object
1723
EMPTY_RELATION_TREE: Any
1824
IMMUTABLE_WARNING: str
1925
DEFAULT_NAMES: Tuple[str, ...]
2026

21-
def normalize_together(
22-
option_together: Union[List[Tuple[str, str]], Tuple[Tuple[str, str], ...], Tuple[()], Tuple[str, str]]
23-
) -> Tuple[Tuple[str, str], ...]: ...
27+
_OptionTogetherT = Union[_ListOrTuple[Union[_ListOrTuple[str], str]], Set[Tuple[str, ...]]]
28+
29+
@overload
30+
def normalize_together(option_together: _ListOrTuple[Union[_ListOrTuple[str], str]]) -> Tuple[Tuple[str, ...], ...]: ...
31+
32+
# Any other value will be returned unchanged, but probably only set is semantically allowed
33+
@overload
34+
def normalize_together(option_together: Set[Tuple[str, ...]]) -> Set[Tuple[str, ...]]: ...
2435

2536
_T = TypeVar("_T")
2637

@@ -45,18 +56,18 @@ class Options(Generic[_M]):
4556
db_table: str = ...
4657
ordering: Optional[Sequence[str]] = ...
4758
indexes: List[Any] = ...
48-
unique_together: Union[Sequence[Tuple[str, str]], Tuple[str, str]] = ...
49-
index_together: Union[Sequence[Tuple[str, str]], Tuple[str, str]] = ...
59+
unique_together: Sequence[Tuple[str]] = ... # Are always normalized
60+
index_together: Sequence[Tuple[str]] = ... # Are always normalized
5061
select_on_save: bool = ...
5162
default_permissions: Sequence[str] = ...
5263
permissions: List[Any] = ...
5364
object_name: Optional[str] = ...
5465
app_label: str = ...
5566
get_latest_by: Optional[Sequence[str]] = ...
56-
order_with_respect_to: Optional[Any] = ...
67+
order_with_respect_to: Optional[str] = ...
5768
db_tablespace: str = ...
58-
required_db_features: List[Any] = ...
59-
required_db_vendor: Any = ...
69+
required_db_features: List[str] = ...
70+
required_db_vendor: Optional[Literal["sqlite", "postgresql", "mysql", "oracle"]] = ...
6071
meta: Optional[type] = ...
6172
pk: Optional[Field] = ...
6273
auto_field: Optional[AutoField] = ...
@@ -105,15 +116,15 @@ class Options(Generic[_M]):
105116
def default_manager(self) -> Optional[Manager]: ...
106117
@property
107118
def fields(self) -> ImmutableList[Field[Any, Any]]: ...
108-
def get_field(self, field_name: str) -> Union[Field, ForeignObjectRel]: ...
119+
def get_field(self, field_name: str) -> Union[Field, ForeignObjectRel, GenericForeignKey]: ...
109120
def get_base_chain(self, model: Type[Model]) -> List[Type[Model]]: ...
110121
def get_parent_list(self) -> List[Type[Model]]: ...
111122
def get_ancestor_link(self, ancestor: Type[Model]) -> Optional[OneToOneField]: ...
112123
def get_path_to_parent(self, parent: Type[Model]) -> List[PathInfo]: ...
113124
def get_path_from_parent(self, parent: Type[Model]) -> List[PathInfo]: ...
114125
def get_fields(
115126
self, include_parents: bool = ..., include_hidden: bool = ...
116-
) -> List[Union[Field[Any, Any], ForeignObjectRel]]: ...
127+
) -> List[Union[Field[Any, Any], ForeignObjectRel, GenericForeignKey]]: ...
117128
@property
118129
def total_unique_constraints(self) -> List[UniqueConstraint]: ...
119130
@property

django-stubs/db/models/query_utils.pyi

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ class Q(tree.Node):
4545
AND: str = ...
4646
OR: str = ...
4747
conditional: bool = ...
48-
def __init__(self, *args: Any, _connector: Optional[Any] = ..., _negated: bool = ..., **kwargs: Any) -> None: ...
48+
def __init__(self, *args: Any, **kwargs: Any) -> None: ...
49+
# Fake signature, the real is
50+
# def __init__(self, *args: Any, _connector: Optional[Any] = ..., _negated: bool = ..., **kwargs: Any) -> None: ...
4951
def __or__(self, other: Q) -> Q: ...
5052
def __and__(self, other: Q) -> Q: ...
5153
def __invert__(self) -> Q: ...

django-stubs/db/utils.pyi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ class ConnectionRouter:
4141
def __init__(self, routers: Optional[Iterable[Any]] = ...) -> None: ...
4242
@property
4343
def routers(self) -> List[Any]: ...
44+
def db_for_read(self, model: Type[Model], **hints: Any) -> str: ...
45+
def db_for_write(self, model: Type[Model], **hints: Any) -> str: ...
4446
def allow_relation(self, obj1: Model, obj2: Model, **hints: Any) -> bool: ...
4547
def allow_migrate(self, db: str, app_label: str, **hints: Any) -> bool: ...
4648
def allow_migrate_model(self, db: str, model: Type[Model]) -> bool: ...

django-stubs/forms/utils.pyi

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ from datetime import datetime
33
from typing import Any, Dict, Iterable, List, Mapping, Optional, Sequence, Union
44

55
from django.core.exceptions import ValidationError
6-
from django.core.files import File
6+
from django.core.files.uploadedfile import UploadedFile
7+
from django.utils.datastructures import MultiValueDict
78
from django.utils.safestring import SafeString
89

910
_DataT = Mapping[str, Any]
10-
_FilesT = Mapping[str, Iterable[File]]
11+
_FilesT = MultiValueDict[str, UploadedFile]
1112

1213
def pretty_name(name: str) -> str: ...
1314
def flatatt(attrs: Dict[str, Any]) -> SafeString: ...

0 commit comments

Comments
 (0)