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.
Files changed (108) hide show
  1. plain/models/CHANGELOG.md +27 -0
  2. plain/models/README.md +26 -42
  3. plain/models/__init__.py +2 -0
  4. plain/models/aggregates.py +42 -19
  5. plain/models/backends/base/base.py +125 -105
  6. plain/models/backends/base/client.py +11 -3
  7. plain/models/backends/base/creation.py +24 -14
  8. plain/models/backends/base/features.py +10 -4
  9. plain/models/backends/base/introspection.py +37 -20
  10. plain/models/backends/base/operations.py +187 -91
  11. plain/models/backends/base/schema.py +338 -218
  12. plain/models/backends/base/validation.py +13 -4
  13. plain/models/backends/ddl_references.py +85 -43
  14. plain/models/backends/mysql/base.py +29 -26
  15. plain/models/backends/mysql/client.py +7 -2
  16. plain/models/backends/mysql/compiler.py +13 -4
  17. plain/models/backends/mysql/creation.py +5 -2
  18. plain/models/backends/mysql/features.py +24 -22
  19. plain/models/backends/mysql/introspection.py +22 -13
  20. plain/models/backends/mysql/operations.py +107 -40
  21. plain/models/backends/mysql/schema.py +52 -28
  22. plain/models/backends/mysql/validation.py +13 -6
  23. plain/models/backends/postgresql/base.py +41 -34
  24. plain/models/backends/postgresql/client.py +7 -2
  25. plain/models/backends/postgresql/creation.py +10 -5
  26. plain/models/backends/postgresql/introspection.py +15 -8
  27. plain/models/backends/postgresql/operations.py +110 -43
  28. plain/models/backends/postgresql/schema.py +88 -49
  29. plain/models/backends/sqlite3/_functions.py +151 -115
  30. plain/models/backends/sqlite3/base.py +37 -23
  31. plain/models/backends/sqlite3/client.py +7 -1
  32. plain/models/backends/sqlite3/creation.py +9 -5
  33. plain/models/backends/sqlite3/features.py +5 -3
  34. plain/models/backends/sqlite3/introspection.py +32 -16
  35. plain/models/backends/sqlite3/operations.py +126 -43
  36. plain/models/backends/sqlite3/schema.py +127 -92
  37. plain/models/backends/utils.py +52 -29
  38. plain/models/backups/cli.py +8 -6
  39. plain/models/backups/clients.py +16 -7
  40. plain/models/backups/core.py +24 -13
  41. plain/models/base.py +221 -229
  42. plain/models/cli.py +98 -67
  43. plain/models/config.py +1 -1
  44. plain/models/connections.py +23 -7
  45. plain/models/constraints.py +79 -56
  46. plain/models/database_url.py +1 -1
  47. plain/models/db.py +6 -2
  48. plain/models/deletion.py +80 -56
  49. plain/models/entrypoints.py +1 -1
  50. plain/models/enums.py +22 -11
  51. plain/models/exceptions.py +23 -8
  52. plain/models/expressions.py +441 -258
  53. plain/models/fields/__init__.py +272 -217
  54. plain/models/fields/json.py +123 -57
  55. plain/models/fields/mixins.py +12 -8
  56. plain/models/fields/related.py +324 -290
  57. plain/models/fields/related_descriptors.py +33 -24
  58. plain/models/fields/related_lookups.py +24 -12
  59. plain/models/fields/related_managers.py +102 -79
  60. plain/models/fields/reverse_related.py +66 -63
  61. plain/models/forms.py +101 -75
  62. plain/models/functions/comparison.py +71 -18
  63. plain/models/functions/datetime.py +79 -29
  64. plain/models/functions/math.py +43 -10
  65. plain/models/functions/mixins.py +24 -7
  66. plain/models/functions/text.py +104 -25
  67. plain/models/functions/window.py +12 -6
  68. plain/models/indexes.py +57 -32
  69. plain/models/lookups.py +228 -153
  70. plain/models/meta.py +505 -0
  71. plain/models/migrations/autodetector.py +86 -43
  72. plain/models/migrations/exceptions.py +7 -3
  73. plain/models/migrations/executor.py +33 -7
  74. plain/models/migrations/graph.py +79 -50
  75. plain/models/migrations/loader.py +45 -22
  76. plain/models/migrations/migration.py +23 -18
  77. plain/models/migrations/operations/base.py +38 -20
  78. plain/models/migrations/operations/fields.py +95 -48
  79. plain/models/migrations/operations/models.py +246 -142
  80. plain/models/migrations/operations/special.py +82 -25
  81. plain/models/migrations/optimizer.py +7 -2
  82. plain/models/migrations/questioner.py +58 -31
  83. plain/models/migrations/recorder.py +27 -16
  84. plain/models/migrations/serializer.py +50 -39
  85. plain/models/migrations/state.py +232 -156
  86. plain/models/migrations/utils.py +30 -14
  87. plain/models/migrations/writer.py +17 -14
  88. plain/models/options.py +189 -518
  89. plain/models/otel.py +16 -6
  90. plain/models/preflight.py +42 -17
  91. plain/models/query.py +400 -251
  92. plain/models/query_utils.py +109 -69
  93. plain/models/registry.py +40 -21
  94. plain/models/sql/compiler.py +190 -127
  95. plain/models/sql/datastructures.py +38 -25
  96. plain/models/sql/query.py +320 -225
  97. plain/models/sql/subqueries.py +36 -25
  98. plain/models/sql/where.py +54 -29
  99. plain/models/test/pytest.py +15 -11
  100. plain/models/test/utils.py +4 -2
  101. plain/models/transaction.py +20 -7
  102. plain/models/utils.py +17 -6
  103. {plain_models-0.49.2.dist-info → plain_models-0.51.0.dist-info}/METADATA +27 -43
  104. plain_models-0.51.0.dist-info/RECORD +123 -0
  105. plain_models-0.49.2.dist-info/RECORD +0 -122
  106. {plain_models-0.49.2.dist-info → plain_models-0.51.0.dist-info}/WHEEL +0 -0
  107. {plain_models-0.49.2.dist-info → plain_models-0.51.0.dist-info}/entry_points.txt +0 -0
  108. {plain_models-0.49.2.dist-info → plain_models-0.51.0.dist-info}/licenses/LICENSE +0 -0
plain/models/otel.py CHANGED
@@ -1,10 +1,18 @@
1
+ from __future__ import annotations
2
+
1
3
  import re
2
4
  import traceback
5
+ from collections.abc import Generator
3
6
  from contextlib import contextmanager
4
- from typing import Any
7
+ from typing import TYPE_CHECKING, Any
5
8
 
6
9
  from opentelemetry import context as otel_context
7
10
  from opentelemetry import trace
11
+
12
+ if TYPE_CHECKING:
13
+ from opentelemetry.trace import Span
14
+
15
+ from plain.models.backends.base.base import BaseDatabaseWrapper
8
16
  from opentelemetry.semconv._incubating.attributes.db_attributes import (
9
17
  DB_QUERY_PARAMETER_TEMPLATE,
10
18
  DB_USER,
@@ -99,7 +107,9 @@ def _clean_identifier(identifier: str) -> str:
99
107
 
100
108
 
101
109
  @contextmanager
102
- def db_span(db, sql: Any, *, many: bool = False, params=None):
110
+ def db_span(
111
+ db: BaseDatabaseWrapper, sql: Any, *, many: bool = False, params: Any = None
112
+ ) -> Generator[Span | None, None, None]:
103
113
  """Open an OpenTelemetry CLIENT span for a database query.
104
114
 
105
115
  All common attributes (`db.*`, `network.*`, etc.) are set automatically.
@@ -107,7 +117,7 @@ def db_span(db, sql: Any, *, many: bool = False, params=None):
107
117
  """
108
118
 
109
119
  # Fast-exit if instrumentation suppression flag set in context.
110
- if otel_context.get_value(_SUPPRESS_KEY):
120
+ if otel_context.get_value(_SUPPRESS_KEY): # type: ignore[arg-type]
111
121
  yield None
112
122
  return
113
123
 
@@ -177,15 +187,15 @@ def db_span(db, sql: Any, *, many: bool = False, params=None):
177
187
 
178
188
 
179
189
  @contextmanager
180
- def suppress_db_tracing():
181
- token = otel_context.attach(otel_context.set_value(_SUPPRESS_KEY, True))
190
+ def suppress_db_tracing() -> Generator[None, None, None]:
191
+ token = otel_context.attach(otel_context.set_value(_SUPPRESS_KEY, True)) # type: ignore[arg-type]
182
192
  try:
183
193
  yield
184
194
  finally:
185
195
  otel_context.detach(token)
186
196
 
187
197
 
188
- def _get_code_attributes():
198
+ def _get_code_attributes() -> dict[str, Any]:
189
199
  """Extract code context attributes for the current database query.
190
200
 
191
201
  Returns a dict of OpenTelemetry code attributes.
plain/models/preflight.py CHANGED
@@ -1,8 +1,12 @@
1
+ from __future__ import annotations
2
+
1
3
  import inspect
2
4
  from collections import defaultdict
5
+ from collections.abc import Callable
6
+ from typing import Any
3
7
 
4
8
  from plain.models.db import db_connection
5
- from plain.models.registry import models_registry
9
+ from plain.models.registry import ModelsRegistry, models_registry
6
10
  from plain.packages import packages_registry
7
11
  from plain.preflight import PreflightCheck, PreflightResult, register_check
8
12
 
@@ -11,7 +15,7 @@ from plain.preflight import PreflightCheck, PreflightResult, register_check
11
15
  class CheckDatabaseBackends(PreflightCheck):
12
16
  """Validates database backend configuration when plain.models is available."""
13
17
 
14
- def run(self):
18
+ def run(self) -> list[PreflightResult]:
15
19
  return db_connection.validation.preflight()
16
20
 
17
21
 
@@ -19,14 +23,16 @@ class CheckDatabaseBackends(PreflightCheck):
19
23
  class CheckAllModels(PreflightCheck):
20
24
  """Validates all model definitions for common issues."""
21
25
 
22
- def run(self):
26
+ def run(self) -> list[PreflightResult]:
23
27
  db_table_models = defaultdict(list)
24
28
  indexes = defaultdict(list)
25
29
  constraints = defaultdict(list)
26
30
  errors = []
27
31
  models = models_registry.get_models()
28
32
  for model in models:
29
- db_table_models[model._meta.db_table].append(model._meta.label)
33
+ db_table_models[model.model_options.db_table].append(
34
+ model.model_options.label
35
+ )
30
36
  if not inspect.ismethod(model.preflight):
31
37
  errors.append(
32
38
  PreflightResult(
@@ -37,10 +43,10 @@ class CheckAllModels(PreflightCheck):
37
43
  )
38
44
  else:
39
45
  errors.extend(model.preflight())
40
- for model_index in model._meta.indexes:
41
- indexes[model_index.name].append(model._meta.label)
42
- for model_constraint in model._meta.constraints:
43
- constraints[model_constraint.name].append(model._meta.label)
46
+ for model_index in model.model_options.indexes:
47
+ indexes[model_index.name].append(model.model_options.label)
48
+ for model_constraint in model.model_options.constraints:
49
+ constraints[model_constraint.name].append(model.model_options.label)
44
50
  for db_table, model_labels in db_table_models.items():
45
51
  if len(model_labels) != 1:
46
52
  model_labels_str = ", ".join(model_labels)
@@ -84,7 +90,9 @@ class CheckAllModels(PreflightCheck):
84
90
  return errors
85
91
 
86
92
 
87
- def _check_lazy_references(models_registry, packages_registry):
93
+ def _check_lazy_references(
94
+ models_registry: ModelsRegistry, packages_registry: Any
95
+ ) -> list[PreflightResult]:
88
96
  """
89
97
  Ensure all lazy (i.e. string) model references have been resolved.
90
98
 
@@ -98,7 +106,9 @@ def _check_lazy_references(models_registry, packages_registry):
98
106
  if not pending_models:
99
107
  return []
100
108
 
101
- def extract_operation(obj):
109
+ def extract_operation(
110
+ obj: Any,
111
+ ) -> tuple[Callable[..., Any], list[Any], dict[str, Any]]:
102
112
  """
103
113
  Take a callable found in Packages._pending_operations and identify the
104
114
  original callable passed to Packages.lazy_model_operation(). If that
@@ -115,7 +125,7 @@ def _check_lazy_references(models_registry, packages_registry):
115
125
  operation = operation.func
116
126
  return operation, args, keywords
117
127
 
118
- def app_model_error(model_key):
128
+ def app_model_error(model_key: tuple[str, str]) -> str:
119
129
  try:
120
130
  packages_registry.get_package_config(model_key[0])
121
131
  model_error = "app '{}' doesn't provide model '{}'".format(*model_key)
@@ -129,7 +139,12 @@ def _check_lazy_references(models_registry, packages_registry):
129
139
  # pair, the original lazy function, and its positional and keyword args as
130
140
  # determined by extract_operation().
131
141
 
132
- def field_error(model_key, func, args, keywords):
142
+ def field_error(
143
+ model_key: tuple[str, str],
144
+ func: Callable[..., Any],
145
+ args: list[Any],
146
+ keywords: dict[str, Any],
147
+ ) -> PreflightResult:
133
148
  error_msg = (
134
149
  "The field %(field)s was declared with a lazy reference "
135
150
  "to '%(model)s', but %(model_error)s."
@@ -145,7 +160,12 @@ def _check_lazy_references(models_registry, packages_registry):
145
160
  id="fields.lazy_reference_not_resolvable",
146
161
  )
147
162
 
148
- def default_error(model_key, func, args, keywords):
163
+ def default_error(
164
+ model_key: tuple[str, str],
165
+ func: Callable[..., Any],
166
+ args: list[Any],
167
+ keywords: dict[str, Any],
168
+ ) -> PreflightResult:
149
169
  error_msg = (
150
170
  "%(op)s contains a lazy reference to %(model)s, but %(model_error)s."
151
171
  )
@@ -167,8 +187,13 @@ def _check_lazy_references(models_registry, packages_registry):
167
187
  ("plain.models.fields.related", "resolve_related_class"): field_error,
168
188
  }
169
189
 
170
- def build_error(model_key, func, args, keywords):
171
- key = (func.__module__, func.__name__)
190
+ def build_error(
191
+ model_key: tuple[str, str],
192
+ func: Callable[..., Any],
193
+ args: list[Any],
194
+ keywords: dict[str, Any],
195
+ ) -> PreflightResult | None:
196
+ key = (func.__module__, func.__name__) # type: ignore[attr-defined]
172
197
  error_fn = known_lazy.get(key, default_error)
173
198
  return error_fn(model_key, func, args, keywords) if error_fn else None
174
199
 
@@ -189,7 +214,7 @@ def _check_lazy_references(models_registry, packages_registry):
189
214
  class CheckLazyReferences(PreflightCheck):
190
215
  """Ensures all lazy (string) model references have been resolved."""
191
216
 
192
- def run(self):
217
+ def run(self) -> list[PreflightResult]:
193
218
  return _check_lazy_references(models_registry, packages_registry)
194
219
 
195
220
 
@@ -197,7 +222,7 @@ class CheckLazyReferences(PreflightCheck):
197
222
  class CheckDatabaseTables(PreflightCheck):
198
223
  """Checks for unknown tables in the database when plain.models is available."""
199
224
 
200
- def run(self):
225
+ def run(self) -> list[PreflightResult]:
201
226
  errors = []
202
227
 
203
228
  db_tables = db_connection.introspection.table_names()