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,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import builtins
2
4
  import collections.abc
3
5
  import datetime
@@ -10,6 +12,7 @@ import pathlib
10
12
  import re
11
13
  import types
12
14
  import uuid
15
+ from typing import Any
13
16
 
14
17
  from plain import models
15
18
  from plain.models.migrations.operations.base import Operation
@@ -19,23 +22,23 @@ from plain.utils.functional import LazyObject, Promise
19
22
 
20
23
 
21
24
  class BaseSerializer:
22
- def __init__(self, value):
25
+ def __init__(self, value: Any) -> None:
23
26
  self.value = value
24
27
 
25
- def serialize(self):
28
+ def serialize(self) -> tuple[str, set[str]]:
26
29
  raise NotImplementedError(
27
30
  "Subclasses of BaseSerializer must implement the serialize() method."
28
31
  )
29
32
 
30
33
 
31
34
  class BaseSequenceSerializer(BaseSerializer):
32
- def _format(self):
35
+ def _format(self) -> str:
33
36
  raise NotImplementedError(
34
37
  "Subclasses of BaseSequenceSerializer must implement the _format() method."
35
38
  )
36
39
 
37
- def serialize(self):
38
- imports = set()
40
+ def serialize(self) -> tuple[str, set[str]]:
41
+ imports: set[str] = set()
39
42
  strings = []
40
43
  for item in self.value:
41
44
  item_string, item_imports = serializer_factory(item).serialize()
@@ -46,26 +49,26 @@ class BaseSequenceSerializer(BaseSerializer):
46
49
 
47
50
 
48
51
  class BaseSimpleSerializer(BaseSerializer):
49
- def serialize(self):
52
+ def serialize(self) -> tuple[str, set[str]]:
50
53
  return repr(self.value), set()
51
54
 
52
55
 
53
56
  class ChoicesSerializer(BaseSerializer):
54
- def serialize(self):
57
+ def serialize(self) -> tuple[str, set[str]]:
55
58
  return serializer_factory(self.value.value).serialize()
56
59
 
57
60
 
58
61
  class DateTimeSerializer(BaseSerializer):
59
62
  """For datetime.*, except datetime.datetime."""
60
63
 
61
- def serialize(self):
64
+ def serialize(self) -> tuple[str, set[str]]:
62
65
  return repr(self.value), {"import datetime"}
63
66
 
64
67
 
65
68
  class DatetimeDatetimeSerializer(BaseSerializer):
66
69
  """For datetime.datetime."""
67
70
 
68
- def serialize(self):
71
+ def serialize(self) -> tuple[str, set[str]]:
69
72
  if self.value.tzinfo is not None and self.value.tzinfo != datetime.UTC:
70
73
  self.value = self.value.astimezone(datetime.UTC)
71
74
  imports = ["import datetime"]
@@ -73,13 +76,15 @@ class DatetimeDatetimeSerializer(BaseSerializer):
73
76
 
74
77
 
75
78
  class DecimalSerializer(BaseSerializer):
76
- def serialize(self):
79
+ def serialize(self) -> tuple[str, set[str]]:
77
80
  return repr(self.value), {"from decimal import Decimal"}
78
81
 
79
82
 
80
83
  class DeconstructableSerializer(BaseSerializer):
81
84
  @staticmethod
82
- def serialize_deconstructed(path, args, kwargs):
85
+ def serialize_deconstructed(
86
+ path: str, args: tuple[Any, ...], kwargs: dict[str, Any]
87
+ ) -> tuple[str, set[str]]:
83
88
  name, imports = DeconstructableSerializer._serialize_path(path)
84
89
  strings = []
85
90
  for arg in args:
@@ -93,23 +98,23 @@ class DeconstructableSerializer(BaseSerializer):
93
98
  return "{}({})".format(name, ", ".join(strings)), imports
94
99
 
95
100
  @staticmethod
96
- def _serialize_path(path):
101
+ def _serialize_path(path: str) -> tuple[str, set[str]]:
97
102
  module, name = path.rsplit(".", 1)
98
103
  if module == "plain.models":
99
- imports = {"from plain import models"}
104
+ imports: set[str] = {"from plain import models"}
100
105
  name = f"models.{name}"
101
106
  else:
102
107
  imports = {f"import {module}"}
103
108
  name = path
104
109
  return name, imports
105
110
 
106
- def serialize(self):
111
+ def serialize(self) -> tuple[str, set[str]]:
107
112
  return self.serialize_deconstructed(*self.value.deconstruct())
108
113
 
109
114
 
110
115
  class DictionarySerializer(BaseSerializer):
111
- def serialize(self):
112
- imports = set()
116
+ def serialize(self) -> tuple[str, set[str]]:
117
+ imports: set[str] = set()
113
118
  strings = []
114
119
  for k, v in sorted(self.value.items()):
115
120
  k_string, k_imports = serializer_factory(k).serialize()
@@ -121,7 +126,7 @@ class DictionarySerializer(BaseSerializer):
121
126
 
122
127
 
123
128
  class EnumSerializer(BaseSerializer):
124
- def serialize(self):
129
+ def serialize(self) -> tuple[str, set[str]]:
125
130
  enum_class = self.value.__class__
126
131
  module = enum_class.__module__
127
132
  if issubclass(enum_class, enum.Flag):
@@ -140,19 +145,19 @@ class EnumSerializer(BaseSerializer):
140
145
 
141
146
 
142
147
  class FloatSerializer(BaseSimpleSerializer):
143
- def serialize(self):
148
+ def serialize(self) -> tuple[str, set[str]]:
144
149
  if math.isnan(self.value) or math.isinf(self.value):
145
150
  return f'float("{self.value}")', set()
146
151
  return super().serialize()
147
152
 
148
153
 
149
154
  class FrozensetSerializer(BaseSequenceSerializer):
150
- def _format(self):
155
+ def _format(self) -> str:
151
156
  return "frozenset([%s])"
152
157
 
153
158
 
154
159
  class FunctionTypeSerializer(BaseSerializer):
155
- def serialize(self):
160
+ def serialize(self) -> tuple[str, set[str]]:
156
161
  if getattr(self.value, "__self__", None) and isinstance(
157
162
  self.value.__self__, type
158
163
  ):
@@ -180,7 +185,7 @@ class FunctionTypeSerializer(BaseSerializer):
180
185
 
181
186
 
182
187
  class FunctoolsPartialSerializer(BaseSerializer):
183
- def serialize(self):
188
+ def serialize(self) -> tuple[str, set[str]]:
184
189
  # Serialize functools.partial() arguments
185
190
  func_string, func_imports = serializer_factory(self.value.func).serialize()
186
191
  args_string, args_imports = serializer_factory(self.value.args).serialize()
@@ -188,7 +193,12 @@ class FunctoolsPartialSerializer(BaseSerializer):
188
193
  self.value.keywords
189
194
  ).serialize()
190
195
  # Add any imports needed by arguments
191
- imports = {"import functools", *func_imports, *args_imports, *keywords_imports}
196
+ imports: set[str] = {
197
+ "import functools",
198
+ *func_imports,
199
+ *args_imports,
200
+ *keywords_imports,
201
+ }
192
202
  return (
193
203
  f"functools.{self.value.__class__.__name__}({func_string}, *{args_string}, **{keywords_string})",
194
204
  imports,
@@ -196,8 +206,8 @@ class FunctoolsPartialSerializer(BaseSerializer):
196
206
 
197
207
 
198
208
  class IterableSerializer(BaseSerializer):
199
- def serialize(self):
200
- imports = set()
209
+ def serialize(self) -> tuple[str, set[str]]:
210
+ imports: set[str] = set()
201
211
  strings = []
202
212
  for item in self.value:
203
213
  item_string, item_imports = serializer_factory(item).serialize()
@@ -210,13 +220,13 @@ class IterableSerializer(BaseSerializer):
210
220
 
211
221
 
212
222
  class ModelFieldSerializer(DeconstructableSerializer):
213
- def serialize(self):
223
+ def serialize(self) -> tuple[str, set[str]]:
214
224
  attr_name, path, args, kwargs = self.value.deconstruct()
215
225
  return self.serialize_deconstructed(path, args, kwargs)
216
226
 
217
227
 
218
228
  class OperationSerializer(BaseSerializer):
219
- def serialize(self):
229
+ def serialize(self) -> tuple[str, set[str]]:
220
230
  from plain.models.migrations.writer import OperationWriter
221
231
 
222
232
  string, imports = OperationWriter(self.value, indentation=0).serialize()
@@ -225,12 +235,12 @@ class OperationSerializer(BaseSerializer):
225
235
 
226
236
 
227
237
  class PathLikeSerializer(BaseSerializer):
228
- def serialize(self):
229
- return repr(os.fspath(self.value)), {}
238
+ def serialize(self) -> tuple[str, set[str]]:
239
+ return repr(os.fspath(self.value)), set()
230
240
 
231
241
 
232
242
  class PathSerializer(BaseSerializer):
233
- def serialize(self):
243
+ def serialize(self) -> tuple[str, set[str]]:
234
244
  # Convert concrete paths to pure paths to avoid issues with migrations
235
245
  # generated on one platform being used on a different platform.
236
246
  prefix = "Pure" if isinstance(self.value, pathlib.Path) else ""
@@ -238,7 +248,7 @@ class PathSerializer(BaseSerializer):
238
248
 
239
249
 
240
250
  class RegexSerializer(BaseSerializer):
241
- def serialize(self):
251
+ def serialize(self) -> tuple[str, set[str]]:
242
252
  regex_pattern, pattern_imports = serializer_factory(
243
253
  self.value.pattern
244
254
  ).serialize()
@@ -246,7 +256,7 @@ class RegexSerializer(BaseSerializer):
246
256
  # same implicit and explicit flags aren't equal.
247
257
  flags = self.value.flags ^ re.compile("").flags
248
258
  regex_flags, flag_imports = serializer_factory(flags).serialize()
249
- imports = {"import re", *pattern_imports, *flag_imports}
259
+ imports: set[str] = {"import re", *pattern_imports, *flag_imports}
250
260
  args = [regex_pattern]
251
261
  if flags:
252
262
  args.append(regex_flags)
@@ -254,33 +264,33 @@ class RegexSerializer(BaseSerializer):
254
264
 
255
265
 
256
266
  class SequenceSerializer(BaseSequenceSerializer):
257
- def _format(self):
267
+ def _format(self) -> str:
258
268
  return "[%s]"
259
269
 
260
270
 
261
271
  class SetSerializer(BaseSequenceSerializer):
262
- def _format(self):
272
+ def _format(self) -> str:
263
273
  # Serialize as a set literal except when value is empty because {}
264
274
  # is an empty dict.
265
275
  return "{%s}" if self.value else "set(%s)"
266
276
 
267
277
 
268
278
  class SettingsReferenceSerializer(BaseSerializer):
269
- def serialize(self):
279
+ def serialize(self) -> tuple[str, set[str]]:
270
280
  return f"settings.{self.value.setting_name}", {
271
281
  "from plain.runtime import settings"
272
282
  }
273
283
 
274
284
 
275
285
  class TupleSerializer(BaseSequenceSerializer):
276
- def _format(self):
286
+ def _format(self) -> str:
277
287
  # When len(value)==0, the empty tuple should be serialized as "()",
278
288
  # not "(,)" because (,) is invalid Python syntax.
279
289
  return "(%s)" if len(self.value) != 1 else "(%s,)"
280
290
 
281
291
 
282
292
  class TypeSerializer(BaseSerializer):
283
- def serialize(self):
293
+ def serialize(self) -> tuple[str, set[str]]:
284
294
  special_cases = [
285
295
  (models.Model, "models.Model", ["from plain import models"]),
286
296
  (types.NoneType, "types.NoneType", ["import types"]),
@@ -294,10 +304,11 @@ class TypeSerializer(BaseSerializer):
294
304
  return self.value.__name__, set()
295
305
  else:
296
306
  return f"{module}.{self.value.__qualname__}", {f"import {module}"}
307
+ return "", set()
297
308
 
298
309
 
299
310
  class UUIDSerializer(BaseSerializer):
300
- def serialize(self):
311
+ def serialize(self) -> tuple[str, set[str]]:
301
312
  return f"uuid.{repr(self.value)}", {"import uuid"}
302
313
 
303
314
 
@@ -331,7 +342,7 @@ class Serializer:
331
342
  }
332
343
 
333
344
  @classmethod
334
- def register(cls, type_, serializer):
345
+ def register(cls, type_: type[Any], serializer: type[BaseSerializer]) -> None:
335
346
  if not issubclass(serializer, BaseSerializer):
336
347
  raise ValueError(
337
348
  f"'{serializer.__name__}' must inherit from 'BaseSerializer'."
@@ -339,7 +350,7 @@ class Serializer:
339
350
  cls._registry[type_] = serializer
340
351
 
341
352
 
342
- def serializer_factory(value):
353
+ def serializer_factory(value: Any) -> BaseSerializer:
343
354
  if isinstance(value, Promise):
344
355
  value = str(value)
345
356
  elif isinstance(value, LazyObject):