plain.models 0.49.2__py3-none-any.whl → 0.51.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.
- plain/models/CHANGELOG.md +27 -0
- plain/models/README.md +26 -42
- plain/models/__init__.py +2 -0
- plain/models/aggregates.py +42 -19
- plain/models/backends/base/base.py +125 -105
- plain/models/backends/base/client.py +11 -3
- plain/models/backends/base/creation.py +24 -14
- plain/models/backends/base/features.py +10 -4
- plain/models/backends/base/introspection.py +37 -20
- plain/models/backends/base/operations.py +187 -91
- plain/models/backends/base/schema.py +338 -218
- plain/models/backends/base/validation.py +13 -4
- plain/models/backends/ddl_references.py +85 -43
- plain/models/backends/mysql/base.py +29 -26
- plain/models/backends/mysql/client.py +7 -2
- plain/models/backends/mysql/compiler.py +13 -4
- plain/models/backends/mysql/creation.py +5 -2
- plain/models/backends/mysql/features.py +24 -22
- plain/models/backends/mysql/introspection.py +22 -13
- plain/models/backends/mysql/operations.py +107 -40
- plain/models/backends/mysql/schema.py +52 -28
- plain/models/backends/mysql/validation.py +13 -6
- plain/models/backends/postgresql/base.py +41 -34
- plain/models/backends/postgresql/client.py +7 -2
- plain/models/backends/postgresql/creation.py +10 -5
- plain/models/backends/postgresql/introspection.py +15 -8
- plain/models/backends/postgresql/operations.py +110 -43
- plain/models/backends/postgresql/schema.py +88 -49
- plain/models/backends/sqlite3/_functions.py +151 -115
- plain/models/backends/sqlite3/base.py +37 -23
- plain/models/backends/sqlite3/client.py +7 -1
- plain/models/backends/sqlite3/creation.py +9 -5
- plain/models/backends/sqlite3/features.py +5 -3
- plain/models/backends/sqlite3/introspection.py +32 -16
- plain/models/backends/sqlite3/operations.py +126 -43
- plain/models/backends/sqlite3/schema.py +127 -92
- plain/models/backends/utils.py +52 -29
- plain/models/backups/cli.py +8 -6
- plain/models/backups/clients.py +16 -7
- plain/models/backups/core.py +24 -13
- plain/models/base.py +221 -229
- plain/models/cli.py +98 -67
- plain/models/config.py +1 -1
- plain/models/connections.py +23 -7
- plain/models/constraints.py +79 -56
- plain/models/database_url.py +1 -1
- plain/models/db.py +6 -2
- plain/models/deletion.py +80 -56
- plain/models/entrypoints.py +1 -1
- plain/models/enums.py +22 -11
- plain/models/exceptions.py +23 -8
- plain/models/expressions.py +441 -258
- plain/models/fields/__init__.py +272 -217
- plain/models/fields/json.py +123 -57
- plain/models/fields/mixins.py +12 -8
- plain/models/fields/related.py +324 -290
- plain/models/fields/related_descriptors.py +33 -24
- plain/models/fields/related_lookups.py +24 -12
- plain/models/fields/related_managers.py +102 -79
- plain/models/fields/reverse_related.py +66 -63
- plain/models/forms.py +101 -75
- plain/models/functions/comparison.py +71 -18
- plain/models/functions/datetime.py +79 -29
- plain/models/functions/math.py +43 -10
- plain/models/functions/mixins.py +24 -7
- plain/models/functions/text.py +104 -25
- plain/models/functions/window.py +12 -6
- plain/models/indexes.py +57 -32
- plain/models/lookups.py +228 -153
- plain/models/meta.py +505 -0
- plain/models/migrations/autodetector.py +86 -43
- plain/models/migrations/exceptions.py +7 -3
- plain/models/migrations/executor.py +33 -7
- plain/models/migrations/graph.py +79 -50
- plain/models/migrations/loader.py +45 -22
- plain/models/migrations/migration.py +23 -18
- plain/models/migrations/operations/base.py +38 -20
- plain/models/migrations/operations/fields.py +95 -48
- plain/models/migrations/operations/models.py +246 -142
- plain/models/migrations/operations/special.py +82 -25
- plain/models/migrations/optimizer.py +7 -2
- plain/models/migrations/questioner.py +58 -31
- plain/models/migrations/recorder.py +27 -16
- plain/models/migrations/serializer.py +50 -39
- plain/models/migrations/state.py +232 -156
- plain/models/migrations/utils.py +30 -14
- plain/models/migrations/writer.py +17 -14
- plain/models/options.py +189 -518
- plain/models/otel.py +16 -6
- plain/models/preflight.py +42 -17
- plain/models/query.py +400 -251
- plain/models/query_utils.py +109 -69
- plain/models/registry.py +40 -21
- plain/models/sql/compiler.py +190 -127
- plain/models/sql/datastructures.py +38 -25
- plain/models/sql/query.py +320 -225
- plain/models/sql/subqueries.py +36 -25
- plain/models/sql/where.py +54 -29
- plain/models/test/pytest.py +15 -11
- plain/models/test/utils.py +4 -2
- plain/models/transaction.py +20 -7
- plain/models/utils.py +17 -6
- {plain_models-0.49.2.dist-info → plain_models-0.51.0.dist-info}/METADATA +27 -43
- plain_models-0.51.0.dist-info/RECORD +123 -0
- plain_models-0.49.2.dist-info/RECORD +0 -122
- {plain_models-0.49.2.dist-info → plain_models-0.51.0.dist-info}/WHEEL +0 -0
- {plain_models-0.49.2.dist-info → plain_models-0.51.0.dist-info}/entry_points.txt +0 -0
- {plain_models-0.49.2.dist-info → plain_models-0.51.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(
|
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 = {
|
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):
|