Skip to content

Commit eda5787

Browse files
Update Expression classes output_field to @cached_property or ClassVar and improve type (#1769)
* Update `BaseExpression.output_field` * Remove allowlist entries rm -rf .mypy_cache/ && bash ./scripts/stubtest.sh | rg "note: unused allowlist entry" | cut -d" " -f5 | xargs -I {} sd "{}\n" "" ./scripts/stubtest/allowlist_todo.txt * Change to ClassVar when necessary Found match using `rg "^ output_field = .*Field\(\)"` in django repository * Update allowlist and remove unused ignore * Fix last occurences and reorder allowlist
1 parent 997ac44 commit eda5787

File tree

19 files changed

+148
-125
lines changed

19 files changed

+148
-125
lines changed

django-stubs/contrib/gis/db/backends/postgis/operations.pyi

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ from typing import Any, Literal
22

33
from django.contrib.gis.db.backends.base.operations import BaseSpatialOperations
44
from django.contrib.gis.db.backends.utils import SpatialOperator
5+
from django.contrib.gis.db.models.fields import GeometryField
56
from django.db.backends.postgresql.operations import DatabaseOperations
67
from django.db.models import Func
8+
from django.utils.functional import cached_property
79

810
BILATERAL: Literal["bilateral"]
911

@@ -16,8 +18,8 @@ class PostGISOperator(SpatialOperator):
1618
class ST_Polygon(Func):
1719
function: str
1820
def __init__(self, expr: Any) -> None: ...
19-
@property
20-
def output_field(self) -> Any: ...
21+
@cached_property
22+
def output_field(self) -> GeometryField: ...
2123

2224
class PostGISOperations(BaseSpatialOperations, DatabaseOperations):
2325
name: str

django-stubs/contrib/gis/db/models/functions.pyi

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
from typing import Any
1+
from typing import Any, ClassVar
22

3+
from django.contrib.gis.db.models.fields import GeometryField
4+
from django.contrib.gis.db.models.sql.conversion import AreaField, DistanceField
35
from django.db.backends.base.base import BaseDatabaseWrapper
4-
from django.db.models import Func
6+
from django.db.models import BinaryField, BooleanField, FloatField, Func, IntegerField, TextField
57
from django.db.models import Transform as StandardTransform
68
from django.db.models.sql.compiler import SQLCompiler, _AsSqlType
9+
from django.utils.functional import cached_property
710

811
NUMERIC_TYPES: Any
912

@@ -15,8 +18,8 @@ class GeoFuncMixin:
1518
class GeoFunc(GeoFuncMixin, Func): ...
1619

1720
class GeomOutputGeoFunc(GeoFunc):
18-
@property
19-
def output_field(self) -> Any: ...
21+
@cached_property
22+
def output_field(self) -> GeometryField: ...
2023

2124
class SQLiteDecimalToFloatMixin:
2225
def as_sqlite(self, compiler: SQLCompiler, connection: BaseDatabaseWrapper, **extra_context: Any) -> _AsSqlType: ...
@@ -27,42 +30,42 @@ class OracleToleranceMixin:
2730

2831
class Area(OracleToleranceMixin, GeoFunc):
2932
arity: int
30-
@property
31-
def output_field(self) -> Any: ...
33+
@cached_property
34+
def output_field(self) -> AreaField: ...
3235
def as_sqlite(self, compiler: SQLCompiler, connection: BaseDatabaseWrapper, **extra_context: Any) -> _AsSqlType: ...
3336

3437
class Azimuth(GeoFunc):
35-
output_field: Any
38+
output_field: ClassVar[FloatField]
3639
arity: int
3740
geom_param_pos: Any
3841

3942
class AsGeoJSON(GeoFunc):
40-
output_field: Any
43+
output_field: ClassVar[TextField]
4144
def __init__(
4245
self, expression: Any, bbox: bool = ..., crs: bool = ..., precision: int = ..., **extra: Any
4346
) -> None: ...
4447
def as_oracle(self, compiler: SQLCompiler, connection: BaseDatabaseWrapper, **extra_context: Any) -> _AsSqlType: ...
4548

4649
class AsGML(GeoFunc):
4750
geom_param_pos: Any
48-
output_field: Any
51+
output_field: ClassVar[TextField]
4952
def __init__(self, expression: Any, version: int = ..., precision: int = ..., **extra: Any) -> None: ...
5053
def as_oracle(self, compiler: SQLCompiler, connection: BaseDatabaseWrapper, **extra_context: Any) -> _AsSqlType: ...
5154

5255
class AsKML(GeoFunc):
53-
output_field: Any
56+
output_field: ClassVar[TextField]
5457
def __init__(self, expression: Any, precision: int = ..., **extra: Any) -> None: ...
5558

5659
class AsSVG(GeoFunc):
57-
output_field: Any
60+
output_field: ClassVar[TextField]
5861
def __init__(self, expression: Any, relative: bool = ..., precision: int = ..., **extra: Any) -> None: ...
5962

6063
class AsWKB(GeoFunc):
61-
output_field: Any
64+
output_field: ClassVar[BinaryField]
6265
arity: int
6366

6467
class AsWKT(GeoFunc):
65-
output_field: Any
68+
output_field: ClassVar[TextField]
6669
arity: int
6770

6871
class BoundingCircle(OracleToleranceMixin, GeomOutputGeoFunc):
@@ -77,8 +80,8 @@ class Difference(OracleToleranceMixin, GeomOutputGeoFunc):
7780
geom_param_pos: Any
7881

7982
class DistanceResultMixin:
80-
@property
81-
def output_field(self) -> Any: ...
83+
@cached_property
84+
def output_field(self) -> DistanceField: ...
8285
def source_is_geography(self) -> Any: ...
8386

8487
class Distance(DistanceResultMixin, OracleToleranceMixin, GeoFunc):
@@ -97,20 +100,20 @@ class ForcePolygonCW(GeomOutputGeoFunc):
97100
arity: int
98101

99102
class FromWKB(GeoFunc):
100-
output_field: Any
103+
output_field: ClassVar[GeometryField]
101104
arity: int
102105

103106
class FromWKT(GeoFunc):
104-
output_field: Any
107+
output_field: ClassVar[GeometryField]
105108
arity: int
106109

107110
class GeoHash(GeoFunc):
108-
output_field: Any
111+
output_field: ClassVar[TextField]
109112
def __init__(self, expression: Any, precision: Any | None = ..., **extra: Any) -> None: ...
110113
def as_mysql(self, compiler: SQLCompiler, connection: BaseDatabaseWrapper, **extra_context: Any) -> _AsSqlType: ...
111114

112115
class GeometryDistance(GeoFunc):
113-
output_field: Any
116+
output_field: ClassVar[FloatField]
114117
arity: int
115118
function: str
116119
arg_joiner: str
@@ -122,11 +125,11 @@ class Intersection(OracleToleranceMixin, GeomOutputGeoFunc):
122125

123126
class IsEmpty(GeoFuncMixin, StandardTransform):
124127
lookup_name: str
125-
output_field: Any
128+
output_field: ClassVar[BooleanField]
126129

127130
class IsValid(OracleToleranceMixin, GeoFuncMixin, StandardTransform):
128131
lookup_name: str
129-
output_field: Any
132+
output_field: ClassVar[BooleanField]
130133
def as_oracle(self, compiler: SQLCompiler, connection: BaseDatabaseWrapper, **extra_context: Any) -> _AsSqlType: ...
131134

132135
class Length(DistanceResultMixin, OracleToleranceMixin, GeoFunc):
@@ -138,22 +141,22 @@ class Length(DistanceResultMixin, OracleToleranceMixin, GeoFunc):
138141
def as_sqlite(self, compiler: SQLCompiler, connection: BaseDatabaseWrapper, **extra_context: Any) -> _AsSqlType: ...
139142

140143
class LineLocatePoint(GeoFunc):
141-
output_field: Any
144+
output_field: ClassVar[FloatField]
142145
arity: int
143146
geom_param_pos: Any
144147

145148
class MakeValid(GeomOutputGeoFunc): ...
146149

147150
class MemSize(GeoFunc):
148-
output_field: Any
151+
output_field: ClassVar[IntegerField]
149152
arity: int
150153

151154
class NumGeometries(GeoFunc):
152-
output_field: Any
155+
output_field: ClassVar[IntegerField]
153156
arity: int
154157

155158
class NumPoints(GeoFunc):
156-
output_field: Any
159+
output_field: ClassVar[IntegerField]
157160
arity: int
158161

159162
class Perimeter(DistanceResultMixin, OracleToleranceMixin, GeoFunc):
Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,25 @@
1-
from django.db.models.aggregates import Aggregate
1+
from typing import ClassVar
2+
3+
from django.contrib.postgres.fields import ArrayField
4+
from django.db.models import Aggregate, BooleanField, JSONField, TextField
25

36
from .mixins import OrderableAggMixin
47

5-
class ArrayAgg(OrderableAggMixin, Aggregate): ...
8+
class ArrayAgg(OrderableAggMixin, Aggregate):
9+
@property
10+
def output_field(self) -> ArrayField: ...
11+
612
class BitAnd(Aggregate): ...
713
class BitOr(Aggregate): ...
8-
class BoolAnd(Aggregate): ...
9-
class BoolOr(Aggregate): ...
10-
class JSONBAgg(OrderableAggMixin, Aggregate): ...
11-
class StringAgg(OrderableAggMixin, Aggregate): ...
14+
15+
class BoolAnd(Aggregate):
16+
output_field: ClassVar[BooleanField]
17+
18+
class BoolOr(Aggregate):
19+
output_field: ClassVar[BooleanField]
20+
21+
class JSONBAgg(OrderableAggMixin, Aggregate):
22+
output_field: ClassVar[JSONField]
23+
24+
class StringAgg(OrderableAggMixin, Aggregate):
25+
output_field: ClassVar[TextField]

django-stubs/contrib/postgres/aggregates/statistics.pyi

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
1-
from django.db.models.aggregates import Aggregate
1+
from typing import ClassVar
2+
3+
from django.db.models import Aggregate, FloatField, IntegerField
4+
5+
class StatAggregate(Aggregate):
6+
output_field: ClassVar[FloatField]
27

3-
class StatAggregate(Aggregate): ...
48
class Corr(StatAggregate): ...
59
class CovarPop(StatAggregate): ...
610
class RegrAvgX(StatAggregate): ...
711
class RegrAvgY(StatAggregate): ...
8-
class RegrCount(StatAggregate): ...
12+
13+
class RegrCount(StatAggregate):
14+
output_field: ClassVar[IntegerField] # type: ignore[assignment]
15+
916
class RegrIntercept(StatAggregate): ...
1017
class RegrR2(StatAggregate): ...
1118
class RegrSlope(StatAggregate): ...
Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,23 @@
1-
from typing import Any
1+
from typing import Any, ClassVar
22

3-
from django.db.models import Field, Transform
3+
from django.contrib.postgres.fields.array import ArrayField
4+
from django.db.models import Field, TextField, Transform
45
from django.db.models.fields.mixins import CheckFieldDefaultMixin
56

67
class HStoreField(CheckFieldDefaultMixin, Field):
78
def get_transform(self, name: str) -> Any: ...
89

910
class KeyTransform(Transform):
11+
output_field: ClassVar[TextField]
12+
1013
def __init__(self, key_name: str, *args: Any, **kwargs: Any) -> None: ...
1114

1215
class KeyTransformFactory:
1316
def __init__(self, key_name: str) -> None: ...
1417
def __call__(self, *args: Any, **kwargs: Any) -> KeyTransform: ...
1518

16-
class KeysTransform(Transform): ...
17-
class ValuesTransform(Transform): ...
19+
class KeysTransform(Transform):
20+
output_field: ClassVar[ArrayField]
21+
22+
class ValuesTransform(Transform):
23+
output_field: ClassVar[ArrayField]
Lines changed: 12 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Any, Literal
1+
from typing import Any, ClassVar, Literal
22

33
from django.db import models
44
from django.db.models.lookups import PostgresOperatorLookup
@@ -43,68 +43,36 @@ class DateTimeRangeField(RangeField):
4343
class DateRangeField(RangeField):
4444
def __get__(self, instance: Any, owner: Any) -> DateRange: ...
4545

46-
class DateTimeRangeContains(PostgresOperatorLookup):
47-
lookup_name: str
48-
postgres_operator: str
46+
class DateTimeRangeContains(PostgresOperatorLookup): ...
4947

5048
class RangeContainedBy(PostgresOperatorLookup):
51-
lookup_name: str
5249
type_mapping: dict[str, str]
53-
postgres_operator: str
5450

55-
class FullyLessThan(PostgresOperatorLookup):
56-
lookup_name: str
57-
postgres_operator: str
58-
59-
class FullGreaterThan(PostgresOperatorLookup):
60-
lookup_name: str
61-
postgres_operator: str
62-
63-
class NotLessThan(PostgresOperatorLookup):
64-
lookup_name: str
65-
postgres_operator: str
66-
67-
class NotGreaterThan(PostgresOperatorLookup):
68-
lookup_name: str
69-
postgres_operator: str
70-
71-
class AdjacentToLookup(PostgresOperatorLookup):
72-
lookup_name: str
73-
postgres_operator: str
51+
class FullyLessThan(PostgresOperatorLookup): ...
52+
class FullGreaterThan(PostgresOperatorLookup): ...
53+
class NotLessThan(PostgresOperatorLookup): ...
54+
class NotGreaterThan(PostgresOperatorLookup): ...
55+
class AdjacentToLookup(PostgresOperatorLookup): ...
7456

7557
class RangeStartsWith(models.Transform):
76-
lookup_name: str
77-
function: str
7858
@property
7959
def output_field(self) -> models.Field: ...
8060

8161
class RangeEndsWith(models.Transform):
82-
lookup_name: str
83-
function: str
8462
@property
8563
def output_field(self) -> models.Field: ...
8664

8765
class IsEmpty(models.Transform):
88-
lookup_name: str
89-
function: str
90-
output_field: models.BooleanField
66+
output_field: ClassVar[models.BooleanField]
9167

9268
class LowerInclusive(models.Transform):
93-
lookup_name: str
94-
function: str
95-
output_field: models.BooleanField
69+
output_field: ClassVar[models.BooleanField]
9670

9771
class LowerInfinite(models.Transform):
98-
lookup_name: str
99-
function: str
100-
output_field: models.BooleanField
72+
output_field: ClassVar[models.BooleanField]
10173

10274
class UpperInclusive(models.Transform):
103-
lookup_name: str
104-
function: str
105-
output_field: models.BooleanField
75+
output_field: ClassVar[models.BooleanField]
10676

10777
class UpperInfinite(models.Transform):
108-
lookup_name: str
109-
function: str
110-
output_field: models.BooleanField
78+
output_field: ClassVar[models.BooleanField]
Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
from django.db.models import Func
1+
from typing import ClassVar
22

3-
class RandomUUID(Func): ...
4-
class TransactionNow(Func): ...
3+
from django.db.models import DateTimeField, Func, UUIDField
4+
5+
class RandomUUID(Func):
6+
output_field: ClassVar[UUIDField]
7+
8+
class TransactionNow(Func):
9+
output_field: ClassVar[DateTimeField]

0 commit comments

Comments
 (0)