plain.models 0.49.1__py3-none-any.whl → 0.50.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. plain/models/CHANGELOG.md +23 -0
  2. plain/models/aggregates.py +42 -19
  3. plain/models/backends/base/base.py +125 -105
  4. plain/models/backends/base/client.py +11 -3
  5. plain/models/backends/base/creation.py +22 -12
  6. plain/models/backends/base/features.py +10 -4
  7. plain/models/backends/base/introspection.py +29 -16
  8. plain/models/backends/base/operations.py +187 -91
  9. plain/models/backends/base/schema.py +267 -165
  10. plain/models/backends/base/validation.py +12 -3
  11. plain/models/backends/ddl_references.py +85 -43
  12. plain/models/backends/mysql/base.py +29 -26
  13. plain/models/backends/mysql/client.py +7 -2
  14. plain/models/backends/mysql/compiler.py +12 -3
  15. plain/models/backends/mysql/creation.py +5 -2
  16. plain/models/backends/mysql/features.py +24 -22
  17. plain/models/backends/mysql/introspection.py +22 -13
  18. plain/models/backends/mysql/operations.py +106 -39
  19. plain/models/backends/mysql/schema.py +48 -24
  20. plain/models/backends/mysql/validation.py +13 -6
  21. plain/models/backends/postgresql/base.py +41 -34
  22. plain/models/backends/postgresql/client.py +7 -2
  23. plain/models/backends/postgresql/creation.py +10 -5
  24. plain/models/backends/postgresql/introspection.py +15 -8
  25. plain/models/backends/postgresql/operations.py +109 -42
  26. plain/models/backends/postgresql/schema.py +85 -46
  27. plain/models/backends/sqlite3/_functions.py +151 -115
  28. plain/models/backends/sqlite3/base.py +37 -23
  29. plain/models/backends/sqlite3/client.py +7 -1
  30. plain/models/backends/sqlite3/creation.py +9 -5
  31. plain/models/backends/sqlite3/features.py +5 -3
  32. plain/models/backends/sqlite3/introspection.py +32 -16
  33. plain/models/backends/sqlite3/operations.py +125 -42
  34. plain/models/backends/sqlite3/schema.py +82 -58
  35. plain/models/backends/utils.py +52 -29
  36. plain/models/backups/cli.py +8 -6
  37. plain/models/backups/clients.py +16 -7
  38. plain/models/backups/core.py +24 -13
  39. plain/models/base.py +113 -74
  40. plain/models/cli.py +94 -63
  41. plain/models/config.py +1 -1
  42. plain/models/connections.py +23 -7
  43. plain/models/constraints.py +65 -47
  44. plain/models/database_url.py +1 -1
  45. plain/models/db.py +6 -2
  46. plain/models/deletion.py +66 -43
  47. plain/models/entrypoints.py +1 -1
  48. plain/models/enums.py +22 -11
  49. plain/models/exceptions.py +23 -8
  50. plain/models/expressions.py +440 -257
  51. plain/models/fields/__init__.py +253 -202
  52. plain/models/fields/json.py +120 -54
  53. plain/models/fields/mixins.py +12 -8
  54. plain/models/fields/related.py +284 -252
  55. plain/models/fields/related_descriptors.py +34 -25
  56. plain/models/fields/related_lookups.py +23 -11
  57. plain/models/fields/related_managers.py +81 -47
  58. plain/models/fields/reverse_related.py +58 -55
  59. plain/models/forms.py +89 -63
  60. plain/models/functions/comparison.py +71 -18
  61. plain/models/functions/datetime.py +79 -29
  62. plain/models/functions/math.py +43 -10
  63. plain/models/functions/mixins.py +24 -7
  64. plain/models/functions/text.py +104 -25
  65. plain/models/functions/window.py +12 -6
  66. plain/models/indexes.py +52 -28
  67. plain/models/lookups.py +228 -153
  68. plain/models/migrations/autodetector.py +86 -43
  69. plain/models/migrations/exceptions.py +7 -3
  70. plain/models/migrations/executor.py +33 -7
  71. plain/models/migrations/graph.py +79 -50
  72. plain/models/migrations/loader.py +45 -22
  73. plain/models/migrations/migration.py +23 -18
  74. plain/models/migrations/operations/base.py +37 -19
  75. plain/models/migrations/operations/fields.py +89 -42
  76. plain/models/migrations/operations/models.py +245 -143
  77. plain/models/migrations/operations/special.py +82 -25
  78. plain/models/migrations/optimizer.py +7 -2
  79. plain/models/migrations/questioner.py +58 -31
  80. plain/models/migrations/recorder.py +18 -11
  81. plain/models/migrations/serializer.py +50 -39
  82. plain/models/migrations/state.py +220 -133
  83. plain/models/migrations/utils.py +29 -13
  84. plain/models/migrations/writer.py +17 -14
  85. plain/models/options.py +63 -56
  86. plain/models/otel.py +16 -6
  87. plain/models/preflight.py +35 -12
  88. plain/models/query.py +323 -228
  89. plain/models/query_utils.py +93 -58
  90. plain/models/registry.py +34 -16
  91. plain/models/sql/compiler.py +146 -97
  92. plain/models/sql/datastructures.py +38 -25
  93. plain/models/sql/query.py +255 -169
  94. plain/models/sql/subqueries.py +32 -21
  95. plain/models/sql/where.py +54 -29
  96. plain/models/test/pytest.py +15 -11
  97. plain/models/test/utils.py +4 -2
  98. plain/models/transaction.py +20 -7
  99. plain/models/utils.py +13 -5
  100. {plain_models-0.49.1.dist-info → plain_models-0.50.0.dist-info}/METADATA +1 -1
  101. plain_models-0.50.0.dist-info/RECORD +122 -0
  102. plain_models-0.49.1.dist-info/RECORD +0 -122
  103. {plain_models-0.49.1.dist-info → plain_models-0.50.0.dist-info}/WHEEL +0 -0
  104. {plain_models-0.49.1.dist-info → plain_models-0.50.0.dist-info}/entry_points.txt +0 -0
  105. {plain_models-0.49.1.dist-info → plain_models-0.50.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,12 +1,25 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Any
4
+
1
5
  from plain.models.expressions import Func, Value
2
6
  from plain.models.fields import CharField, IntegerField, TextField
3
7
  from plain.models.functions import Cast, Coalesce
4
8
  from plain.models.lookups import Transform
5
9
 
10
+ if TYPE_CHECKING:
11
+ from plain.models.backends.base.base import BaseDatabaseWrapper
12
+ from plain.models.sql.compiler import SQLCompiler
13
+
6
14
 
7
15
  class MySQLSHA2Mixin:
8
- def as_mysql(self, compiler, connection, **extra_context):
9
- return super().as_sql(
16
+ def as_mysql(
17
+ self,
18
+ compiler: SQLCompiler,
19
+ connection: BaseDatabaseWrapper,
20
+ **extra_context: Any,
21
+ ) -> tuple[str, tuple[Any, ...]]:
22
+ return super().as_sql( # type: ignore[misc]
10
23
  compiler,
11
24
  connection,
12
25
  template=f"SHA2(%(expressions)s, {self.function[3:]})",
@@ -15,8 +28,13 @@ class MySQLSHA2Mixin:
15
28
 
16
29
 
17
30
  class PostgreSQLSHAMixin:
18
- def as_postgresql(self, compiler, connection, **extra_context):
19
- return super().as_sql(
31
+ def as_postgresql(
32
+ self,
33
+ compiler: SQLCompiler,
34
+ connection: BaseDatabaseWrapper,
35
+ **extra_context: Any,
36
+ ) -> tuple[str, tuple[Any, ...]]:
37
+ return super().as_sql( # type: ignore[misc]
20
38
  compiler,
21
39
  connection,
22
40
  template="ENCODE(DIGEST(%(expressions)s, '%(function)s'), 'hex')",
@@ -29,7 +47,12 @@ class Chr(Transform):
29
47
  function = "CHR"
30
48
  lookup_name = "chr"
31
49
 
32
- def as_mysql(self, compiler, connection, **extra_context):
50
+ def as_mysql(
51
+ self,
52
+ compiler: SQLCompiler,
53
+ connection: BaseDatabaseWrapper,
54
+ **extra_context: Any,
55
+ ) -> tuple[str, tuple[Any, ...]]:
33
56
  return super().as_sql(
34
57
  compiler,
35
58
  connection,
@@ -38,7 +61,12 @@ class Chr(Transform):
38
61
  **extra_context,
39
62
  )
40
63
 
41
- def as_sqlite(self, compiler, connection, **extra_context):
64
+ def as_sqlite(
65
+ self,
66
+ compiler: SQLCompiler,
67
+ connection: BaseDatabaseWrapper,
68
+ **extra_context: Any,
69
+ ) -> tuple[str, tuple[Any, ...]]:
42
70
  return super().as_sql(compiler, connection, function="CHAR", **extra_context)
43
71
 
44
72
 
@@ -50,7 +78,12 @@ class ConcatPair(Func):
50
78
 
51
79
  function = "CONCAT"
52
80
 
53
- def as_sqlite(self, compiler, connection, **extra_context):
81
+ def as_sqlite(
82
+ self,
83
+ compiler: SQLCompiler,
84
+ connection: BaseDatabaseWrapper,
85
+ **extra_context: Any,
86
+ ) -> tuple[str, tuple[Any, ...]]:
54
87
  coalesced = self.coalesce()
55
88
  return super(ConcatPair, coalesced).as_sql(
56
89
  compiler,
@@ -60,7 +93,12 @@ class ConcatPair(Func):
60
93
  **extra_context,
61
94
  )
62
95
 
63
- def as_postgresql(self, compiler, connection, **extra_context):
96
+ def as_postgresql(
97
+ self,
98
+ compiler: SQLCompiler,
99
+ connection: BaseDatabaseWrapper,
100
+ **extra_context: Any,
101
+ ) -> tuple[str, tuple[Any, ...]]:
64
102
  copy = self.copy()
65
103
  copy.set_source_expressions(
66
104
  [
@@ -74,7 +112,12 @@ class ConcatPair(Func):
74
112
  **extra_context,
75
113
  )
76
114
 
77
- def as_mysql(self, compiler, connection, **extra_context):
115
+ def as_mysql(
116
+ self,
117
+ compiler: SQLCompiler,
118
+ connection: BaseDatabaseWrapper,
119
+ **extra_context: Any,
120
+ ) -> tuple[str, tuple[Any, ...]]:
78
121
  # Use CONCAT_WS with an empty separator so that NULLs are ignored.
79
122
  return super().as_sql(
80
123
  compiler,
@@ -84,7 +127,7 @@ class ConcatPair(Func):
84
127
  **extra_context,
85
128
  )
86
129
 
87
- def coalesce(self):
130
+ def coalesce(self) -> ConcatPair:
88
131
  # null on either side results in null for expression, wrap with coalesce
89
132
  c = self.copy()
90
133
  c.set_source_expressions(
@@ -106,13 +149,13 @@ class Concat(Func):
106
149
  function = None
107
150
  template = "%(expressions)s"
108
151
 
109
- def __init__(self, *expressions, **extra):
152
+ def __init__(self, *expressions: Any, **extra: Any) -> None:
110
153
  if len(expressions) < 2:
111
154
  raise ValueError("Concat must take at least two expressions")
112
155
  paired = self._paired(expressions)
113
156
  super().__init__(paired, **extra)
114
157
 
115
- def _paired(self, expressions):
158
+ def _paired(self, expressions: tuple[Any, ...]) -> ConcatPair:
116
159
  # wrap pairs of expressions in successive concat functions
117
160
  # exp = [a, b, c, d]
118
161
  # -> ConcatPair(a, ConcatPair(b, ConcatPair(c, d))))
@@ -126,7 +169,7 @@ class Left(Func):
126
169
  arity = 2
127
170
  output_field = CharField()
128
171
 
129
- def __init__(self, expression, length, **extra):
172
+ def __init__(self, expression: Any, length: Any, **extra: Any) -> None:
130
173
  """
131
174
  expression: the name of a field, or an expression returning a string
132
175
  length: the number of characters to return from the start of the string
@@ -136,10 +179,15 @@ class Left(Func):
136
179
  raise ValueError("'length' must be greater than 0.")
137
180
  super().__init__(expression, length, **extra)
138
181
 
139
- def get_substr(self):
182
+ def get_substr(self) -> Substr:
140
183
  return Substr(self.source_expressions[0], Value(1), self.source_expressions[1])
141
184
 
142
- def as_sqlite(self, compiler, connection, **extra_context):
185
+ def as_sqlite(
186
+ self,
187
+ compiler: SQLCompiler,
188
+ connection: BaseDatabaseWrapper,
189
+ **extra_context: Any,
190
+ ) -> tuple[str, tuple[Any, ...]]:
143
191
  return self.get_substr().as_sqlite(compiler, connection, **extra_context)
144
192
 
145
193
 
@@ -150,7 +198,12 @@ class Length(Transform):
150
198
  lookup_name = "length"
151
199
  output_field = IntegerField()
152
200
 
153
- def as_mysql(self, compiler, connection, **extra_context):
201
+ def as_mysql(
202
+ self,
203
+ compiler: SQLCompiler,
204
+ connection: BaseDatabaseWrapper,
205
+ **extra_context: Any,
206
+ ) -> tuple[str, tuple[Any, ...]]:
154
207
  return super().as_sql(
155
208
  compiler, connection, function="CHAR_LENGTH", **extra_context
156
209
  )
@@ -165,7 +218,9 @@ class LPad(Func):
165
218
  function = "LPAD"
166
219
  output_field = CharField()
167
220
 
168
- def __init__(self, expression, length, fill_text=Value(" "), **extra):
221
+ def __init__(
222
+ self, expression: Any, length: Any, fill_text: Any = Value(" "), **extra: Any
223
+ ) -> None:
169
224
  if (
170
225
  not hasattr(length, "resolve_expression")
171
226
  and length is not None
@@ -190,10 +245,20 @@ class Ord(Transform):
190
245
  lookup_name = "ord"
191
246
  output_field = IntegerField()
192
247
 
193
- def as_mysql(self, compiler, connection, **extra_context):
248
+ def as_mysql(
249
+ self,
250
+ compiler: SQLCompiler,
251
+ connection: BaseDatabaseWrapper,
252
+ **extra_context: Any,
253
+ ) -> tuple[str, tuple[Any, ...]]:
194
254
  return super().as_sql(compiler, connection, function="ORD", **extra_context)
195
255
 
196
- def as_sqlite(self, compiler, connection, **extra_context):
256
+ def as_sqlite(
257
+ self,
258
+ compiler: SQLCompiler,
259
+ connection: BaseDatabaseWrapper,
260
+ **extra_context: Any,
261
+ ) -> tuple[str, tuple[Any, ...]]:
197
262
  return super().as_sql(compiler, connection, function="UNICODE", **extra_context)
198
263
 
199
264
 
@@ -201,7 +266,7 @@ class Repeat(Func):
201
266
  function = "REPEAT"
202
267
  output_field = CharField()
203
268
 
204
- def __init__(self, expression, number, **extra):
269
+ def __init__(self, expression: Any, number: Any, **extra: Any) -> None:
205
270
  if (
206
271
  not hasattr(number, "resolve_expression")
207
272
  and number is not None
@@ -214,7 +279,9 @@ class Repeat(Func):
214
279
  class Replace(Func):
215
280
  function = "REPLACE"
216
281
 
217
- def __init__(self, expression, text, replacement=Value(""), **extra):
282
+ def __init__(
283
+ self, expression: Any, text: Any, replacement: Any = Value(""), **extra: Any
284
+ ) -> None:
218
285
  super().__init__(expression, text, replacement, **extra)
219
286
 
220
287
 
@@ -226,7 +293,7 @@ class Reverse(Transform):
226
293
  class Right(Left):
227
294
  function = "RIGHT"
228
295
 
229
- def get_substr(self):
296
+ def get_substr(self) -> Substr:
230
297
  return Substr(
231
298
  self.source_expressions[0], self.source_expressions[1] * Value(-1)
232
299
  )
@@ -277,7 +344,12 @@ class StrIndex(Func):
277
344
  arity = 2
278
345
  output_field = IntegerField()
279
346
 
280
- def as_postgresql(self, compiler, connection, **extra_context):
347
+ def as_postgresql(
348
+ self,
349
+ compiler: SQLCompiler,
350
+ connection: BaseDatabaseWrapper,
351
+ **extra_context: Any,
352
+ ) -> tuple[str, tuple[Any, ...]]:
281
353
  return super().as_sql(compiler, connection, function="STRPOS", **extra_context)
282
354
 
283
355
 
@@ -285,7 +357,9 @@ class Substr(Func):
285
357
  function = "SUBSTRING"
286
358
  output_field = CharField()
287
359
 
288
- def __init__(self, expression, pos, length=None, **extra):
360
+ def __init__(
361
+ self, expression: Any, pos: Any, length: Any = None, **extra: Any
362
+ ) -> None:
289
363
  """
290
364
  expression: the name of a field, or an expression returning a string
291
365
  pos: an integer > 0, or an expression returning an integer
@@ -299,7 +373,12 @@ class Substr(Func):
299
373
  expressions.append(length)
300
374
  super().__init__(*expressions, **extra)
301
375
 
302
- def as_sqlite(self, compiler, connection, **extra_context):
376
+ def as_sqlite(
377
+ self,
378
+ compiler: SQLCompiler,
379
+ connection: BaseDatabaseWrapper,
380
+ **extra_context: Any,
381
+ ) -> tuple[str, tuple[Any, ...]]:
303
382
  return super().as_sql(compiler, connection, function="SUBSTR", **extra_context)
304
383
 
305
384
 
@@ -1,5 +1,9 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
1
5
  from plain.models.expressions import Func
2
- from plain.models.fields import FloatField, IntegerField
6
+ from plain.models.fields import Field, FloatField, IntegerField
3
7
 
4
8
  __all__ = [
5
9
  "CumeDist",
@@ -37,7 +41,9 @@ class FirstValue(Func):
37
41
  class LagLeadFunction(Func):
38
42
  window_compatible = True
39
43
 
40
- def __init__(self, expression, offset=1, default=None, **extra):
44
+ def __init__(
45
+ self, expression: Any, offset: int = 1, default: Any = None, **extra: Any
46
+ ) -> None:
41
47
  if expression is None:
42
48
  raise ValueError(
43
49
  f"{self.__class__.__name__} requires a non-null source expression."
@@ -51,7 +57,7 @@ class LagLeadFunction(Func):
51
57
  args += (default,)
52
58
  super().__init__(*args, **extra)
53
59
 
54
- def _resolve_output_field(self):
60
+ def _resolve_output_field(self) -> Field:
55
61
  sources = self.get_source_expressions()
56
62
  return sources[0].output_field
57
63
 
@@ -74,7 +80,7 @@ class NthValue(Func):
74
80
  function = "NTH_VALUE"
75
81
  window_compatible = True
76
82
 
77
- def __init__(self, expression, nth=1, **extra):
83
+ def __init__(self, expression: Any, nth: int = 1, **extra: Any) -> None:
78
84
  if expression is None:
79
85
  raise ValueError(
80
86
  f"{self.__class__.__name__} requires a non-null source expression."
@@ -85,7 +91,7 @@ class NthValue(Func):
85
91
  )
86
92
  super().__init__(expression, nth, **extra)
87
93
 
88
- def _resolve_output_field(self):
94
+ def _resolve_output_field(self) -> Field:
89
95
  sources = self.get_source_expressions()
90
96
  return sources[0].output_field
91
97
 
@@ -95,7 +101,7 @@ class Ntile(Func):
95
101
  output_field = IntegerField()
96
102
  window_compatible = True
97
103
 
98
- def __init__(self, num_buckets=1, **extra):
104
+ def __init__(self, num_buckets: int = 1, **extra: Any) -> None:
99
105
  if num_buckets <= 0:
100
106
  raise ValueError("num_buckets must be greater than 0.")
101
107
  super().__init__(num_buckets, **extra)
plain/models/indexes.py CHANGED
@@ -1,4 +1,7 @@
1
+ from __future__ import annotations
2
+
1
3
  from types import NoneType
4
+ from typing import TYPE_CHECKING, Any
2
5
 
3
6
  from plain.models.backends.utils import names_digest, split_identifier
4
7
  from plain.models.expressions import Col, ExpressionList, F, Func, OrderBy
@@ -7,6 +10,14 @@ from plain.models.query_utils import Q
7
10
  from plain.models.sql import Query
8
11
  from plain.utils.functional import partition
9
12
 
13
+ if TYPE_CHECKING:
14
+ from plain.models.backends.base.base import BaseDatabaseWrapper
15
+ from plain.models.backends.base.schema import BaseDatabaseSchemaEditor
16
+ from plain.models.backends.ddl_references import Statement
17
+ from plain.models.base import Model
18
+ from plain.models.expressions import Expression
19
+ from plain.models.sql.compiler import SQLCompiler
20
+
10
21
  __all__ = ["Index"]
11
22
 
12
23
 
@@ -18,13 +29,13 @@ class Index:
18
29
 
19
30
  def __init__(
20
31
  self,
21
- *expressions,
22
- fields=(),
23
- name=None,
24
- opclasses=(),
25
- condition=None,
26
- include=None,
27
- ):
32
+ *expressions: Any,
33
+ fields: tuple[str, ...] | list[str] = (),
34
+ name: str | None = None,
35
+ opclasses: tuple[str, ...] | list[str] = (),
36
+ condition: Q | None = None,
37
+ include: tuple[str, ...] | list[str] | None = None,
38
+ ) -> None:
28
39
  if opclasses and not name:
29
40
  raise ValueError("An index must be named to use opclasses.")
30
41
  if not isinstance(condition, NoneType | Q):
@@ -77,10 +88,12 @@ class Index:
77
88
  )
78
89
 
79
90
  @property
80
- def contains_expressions(self):
91
+ def contains_expressions(self) -> bool:
81
92
  return bool(self.expressions)
82
93
 
83
- def _get_condition_sql(self, model, schema_editor):
94
+ def _get_condition_sql(
95
+ self, model: type[Model], schema_editor: BaseDatabaseSchemaEditor
96
+ ) -> str | None:
84
97
  if self.condition is None:
85
98
  return None
86
99
  query = Query(model=model, alias_cols=False)
@@ -89,7 +102,9 @@ class Index:
89
102
  sql, params = where.as_sql(compiler, schema_editor.connection)
90
103
  return sql % tuple(schema_editor.quote_value(p) for p in params)
91
104
 
92
- def create_sql(self, model, schema_editor, **kwargs):
105
+ def create_sql(
106
+ self, model: type[Model], schema_editor: BaseDatabaseSchemaEditor, **kwargs: Any
107
+ ) -> Statement:
93
108
  include = [
94
109
  model._meta.get_field(field_name).column for field_name in self.include
95
110
  ]
@@ -111,15 +126,15 @@ class Index:
111
126
  for field_name, _ in self.fields_orders
112
127
  ]
113
128
  if schema_editor.connection.features.supports_index_column_ordering:
114
- col_suffixes = [order[1] for order in self.fields_orders]
129
+ col_suffixes = tuple(order[1] for order in self.fields_orders)
115
130
  else:
116
- col_suffixes = [""] * len(self.fields_orders)
131
+ col_suffixes = ("",) * len(self.fields_orders)
117
132
  expressions = None
118
133
  return schema_editor._create_index_sql(
119
134
  model,
120
135
  fields=fields,
121
136
  name=self.name,
122
- col_suffixes=col_suffixes,
137
+ col_suffixes=col_suffixes, # type: ignore[arg-type]
123
138
  opclasses=self.opclasses,
124
139
  condition=condition,
125
140
  include=include,
@@ -127,10 +142,12 @@ class Index:
127
142
  **kwargs,
128
143
  )
129
144
 
130
- def remove_sql(self, model, schema_editor, **kwargs):
145
+ def remove_sql(
146
+ self, model: type[Model], schema_editor: BaseDatabaseSchemaEditor, **kwargs: Any
147
+ ) -> Statement:
131
148
  return schema_editor._delete_index_sql(model, self.name, **kwargs)
132
149
 
133
- def deconstruct(self):
150
+ def deconstruct(self) -> tuple[str, tuple[Expression, ...], dict[str, Any]]:
134
151
  path = f"{self.__class__.__module__}.{self.__class__.__name__}"
135
152
  path = path.replace("plain.models.indexes", "plain.models")
136
153
  kwargs = {"name": self.name}
@@ -144,12 +161,12 @@ class Index:
144
161
  kwargs["include"] = self.include
145
162
  return (path, self.expressions, kwargs)
146
163
 
147
- def clone(self):
164
+ def clone(self) -> Index:
148
165
  """Create a copy of this Index."""
149
166
  _, args, kwargs = self.deconstruct()
150
167
  return self.__class__(*args, **kwargs)
151
168
 
152
- def set_name_with_model(self, model):
169
+ def set_name_with_model(self, model: type[Model]) -> None:
153
170
  """
154
171
  Generate a unique name for the index.
155
172
 
@@ -184,7 +201,7 @@ class Index:
184
201
  if self.name[0] == "_" or self.name[0].isdigit():
185
202
  self.name = f"D{self.name[1:]}"
186
203
 
187
- def __repr__(self):
204
+ def __repr__(self) -> str:
188
205
  return "<{}:{}{}{}{}{}{}>".format(
189
206
  self.__class__.__qualname__,
190
207
  "" if not self.fields else f" fields={repr(self.fields)}",
@@ -195,9 +212,9 @@ class Index:
195
212
  "" if not self.opclasses else f" opclasses={repr(self.opclasses)}",
196
213
  )
197
214
 
198
- def __eq__(self, other):
215
+ def __eq__(self, other: object) -> bool:
199
216
  if self.__class__ == other.__class__:
200
- return self.deconstruct() == other.deconstruct()
217
+ return self.deconstruct() == other.deconstruct() # type: ignore[attr-defined]
201
218
  return NotImplemented
202
219
 
203
220
 
@@ -207,7 +224,9 @@ class IndexExpression(Func):
207
224
  template = "%(expressions)s"
208
225
  wrapper_classes = (OrderBy, Collate)
209
226
 
210
- def set_wrapper_classes(self, connection=None):
227
+ def set_wrapper_classes(
228
+ self, connection: BaseDatabaseWrapper | None = None
229
+ ) -> None:
211
230
  # Some databases (e.g. MySQL) treats COLLATE as an indexed expression.
212
231
  if connection and connection.features.collate_as_index_expression:
213
232
  self.wrapper_classes = tuple(
@@ -220,12 +239,12 @@ class IndexExpression(Func):
220
239
 
221
240
  def resolve_expression(
222
241
  self,
223
- query=None,
224
- allow_joins=True,
225
- reuse=None,
226
- summarize=False,
227
- for_save=False,
228
- ):
242
+ query: Any = None,
243
+ allow_joins: bool = True,
244
+ reuse: Any = None,
245
+ summarize: bool = False,
246
+ for_save: bool = False,
247
+ ) -> Expression:
229
248
  expressions = list(self.flatten())
230
249
  # Split expressions and wrappers.
231
250
  index_expressions, wrappers = partition(
@@ -287,6 +306,11 @@ class IndexExpression(Func):
287
306
  query, allow_joins, reuse, summarize, for_save
288
307
  )
289
308
 
290
- def as_sqlite(self, compiler, connection, **extra_context):
309
+ def as_sqlite(
310
+ self,
311
+ compiler: SQLCompiler,
312
+ connection: BaseDatabaseWrapper,
313
+ **extra_context: Any,
314
+ ) -> tuple[str, tuple[Any, ...]]:
291
315
  # Casting to numeric is unnecessary.
292
316
  return self.as_sql(compiler, connection, **extra_context)