async-orm-kit 1.0.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 (92) hide show
  1. async_orm_kit/__init__.py +151 -0
  2. async_orm_kit/_logging.py +58 -0
  3. async_orm_kit/_version.py +1 -0
  4. async_orm_kit/config.py +31 -0
  5. async_orm_kit/exceptions.py +353 -0
  6. async_orm_kit/integrations/__init__.py +15 -0
  7. async_orm_kit/integrations/alembic.py +56 -0
  8. async_orm_kit/integrations/fastapi.py +134 -0
  9. async_orm_kit/integrations/storage.py +213 -0
  10. async_orm_kit/manager/__init__.py +3 -0
  11. async_orm_kit/manager/manager.py +568 -0
  12. async_orm_kit/models/__init__.py +3 -0
  13. async_orm_kit/models/base.py +568 -0
  14. async_orm_kit/models/fields/__init__.py +23 -0
  15. async_orm_kit/models/fields/exceptions.py +10 -0
  16. async_orm_kit/models/fields/typed/__init__.py +11 -0
  17. async_orm_kit/models/fields/typed/base.py +155 -0
  18. async_orm_kit/models/fields/typed/file.py +697 -0
  19. async_orm_kit/models/fields/typed/string.py +38 -0
  20. async_orm_kit/models/fields/validators.py +110 -0
  21. async_orm_kit/models/meta.py +181 -0
  22. async_orm_kit/models/mixins.py +19 -0
  23. async_orm_kit/observers/__init__.py +4 -0
  24. async_orm_kit/observers/mixin.py +26 -0
  25. async_orm_kit/observers/model_observer.py +20 -0
  26. async_orm_kit/observers/queryset_observer.py +38 -0
  27. async_orm_kit/py.typed +0 -0
  28. async_orm_kit/query/__init__.py +0 -0
  29. async_orm_kit/query/aggregations/__init__.py +8 -0
  30. async_orm_kit/query/aggregations/avg.py +71 -0
  31. async_orm_kit/query/aggregations/base.py +317 -0
  32. async_orm_kit/query/aggregations/count.py +115 -0
  33. async_orm_kit/query/aggregations/max.py +76 -0
  34. async_orm_kit/query/aggregations/min.py +76 -0
  35. async_orm_kit/query/aggregations/sum.py +73 -0
  36. async_orm_kit/query/compiler.py +574 -0
  37. async_orm_kit/query/debugger.py +269 -0
  38. async_orm_kit/query/expressions/__init__.py +62 -0
  39. async_orm_kit/query/expressions/analysis.py +56 -0
  40. async_orm_kit/query/expressions/case.py +321 -0
  41. async_orm_kit/query/expressions/collections.py +251 -0
  42. async_orm_kit/query/expressions/f.py +362 -0
  43. async_orm_kit/query/expressions/functions.py +202 -0
  44. async_orm_kit/query/expressions/subquery.py +206 -0
  45. async_orm_kit/query/expressions/value.py +101 -0
  46. async_orm_kit/query/expressions/window.py +95 -0
  47. async_orm_kit/query/lookups/__init__.py +121 -0
  48. async_orm_kit/query/lookups/conditions.py +315 -0
  49. async_orm_kit/query/lookups/correlated.py +86 -0
  50. async_orm_kit/query/lookups/json_paths.py +167 -0
  51. async_orm_kit/query/lookups/parser.py +276 -0
  52. async_orm_kit/query/lookups/resolver/__init__.py +5 -0
  53. async_orm_kit/query/lookups/resolver/context.py +150 -0
  54. async_orm_kit/query/lookups/resolver/resolver.py +572 -0
  55. async_orm_kit/query/lookups/resolver/types.py +150 -0
  56. async_orm_kit/query/mixins/__init__.py +0 -0
  57. async_orm_kit/query/mixins/combinable.py +376 -0
  58. async_orm_kit/query/mixins/orderable.py +208 -0
  59. async_orm_kit/query/observability.py +346 -0
  60. async_orm_kit/query/planning/__init__.py +0 -0
  61. async_orm_kit/query/planning/directives.py +117 -0
  62. async_orm_kit/query/planning/factory.py +40 -0
  63. async_orm_kit/query/planning/passes/__init__.py +15 -0
  64. async_orm_kit/query/planning/passes/base.py +47 -0
  65. async_orm_kit/query/planning/passes/cartesian_detection.py +74 -0
  66. async_orm_kit/query/planning/passes/depth_splitting.py +78 -0
  67. async_orm_kit/query/planning/passes/join_promotion.py +81 -0
  68. async_orm_kit/query/planning/passes/path_conflicts.py +103 -0
  69. async_orm_kit/query/planning/passes/redundant_path.py +67 -0
  70. async_orm_kit/query/planning/passes/strategy_selection.py +49 -0
  71. async_orm_kit/query/planning/planner.py +160 -0
  72. async_orm_kit/query/planning/protocols.py +78 -0
  73. async_orm_kit/query/postgres/__init__.py +0 -0
  74. async_orm_kit/query/postgres/optimizer.py +189 -0
  75. async_orm_kit/query/q.py +596 -0
  76. async_orm_kit/query/relations/__init__.py +0 -0
  77. async_orm_kit/query/relations/enums.py +16 -0
  78. async_orm_kit/query/relations/graph.py +908 -0
  79. async_orm_kit/query/relations/node.py +421 -0
  80. async_orm_kit/queryset/__init__.py +4 -0
  81. async_orm_kit/queryset/builder.py +1259 -0
  82. async_orm_kit/queryset/compound.py +379 -0
  83. async_orm_kit/queryset/executor.py +718 -0
  84. async_orm_kit/queryset/queryset.py +1726 -0
  85. async_orm_kit/queryset/state.py +164 -0
  86. async_orm_kit/queryset/validators.py +361 -0
  87. async_orm_kit/queryset/writer.py +755 -0
  88. async_orm_kit/types.py +23 -0
  89. async_orm_kit-1.0.0.dist-info/METADATA +318 -0
  90. async_orm_kit-1.0.0.dist-info/RECORD +92 -0
  91. async_orm_kit-1.0.0.dist-info/WHEEL +4 -0
  92. async_orm_kit-1.0.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,150 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import TYPE_CHECKING, Any
5
+
6
+ if TYPE_CHECKING:
7
+ from async_orm_kit.query.relations.node import JoinNode
8
+
9
+
10
+ @dataclass(slots=True)
11
+ class FieldResolution:
12
+ """Result of field path resolution containing column and JOIN metadata.
13
+
14
+ FieldResolution encapsulates the complete result of resolving a Django-style
15
+ field path to a SQLAlchemy column. It includes the resolved column, the lookup
16
+ operator to apply, and optional JOIN metadata if relationship traversal occurred.
17
+
18
+ This data class serves as the contract between FieldResolver and ConditionBuilder,
19
+ providing all information needed to build a filter condition without exposing
20
+ internal resolution details.
21
+
22
+ Attributes:
23
+ field_path: Full relationship path traversed, empty string for root model
24
+ fields. Examples: "", "building", "user__books".
25
+ field_name: Terminal field name resolved, empty string for direct relation
26
+ filters. Examples: "name", "title", "".
27
+ lookup: Lookup operator to apply. Examples: "exact", "gte", "icontains".
28
+ column: SQLAlchemy column or alias to use in condition. Can be a Column,
29
+ InstrumentedAttribute, or AliasedClass for direct relation filters.
30
+ join_node: Optional JoinNode reference if relationship traversal occurred.
31
+ None for root model fields. Used for JOIN graph optimization.
32
+
33
+ Example:
34
+ >>> # Simple field resolution (no JOINs)
35
+ >>> FieldResolution(
36
+ ... field_path="",
37
+ ... field_name="status",
38
+ ... lookup="exact",
39
+ ... column=Product.status,
40
+ ... join_node=None
41
+ ... )
42
+ >>>
43
+ >>> # Relationship field resolution (with JOIN)
44
+ >>> FieldResolution(
45
+ ... field_path="category",
46
+ ... field_name="name",
47
+ ... lookup="icontains",
48
+ ... column=category_alias.name,
49
+ ... join_node=JoinNode(path="category", ...)
50
+ ... )
51
+ >>>
52
+ >>> # Direct relation filter (no terminal field)
53
+ >>> FieldResolution(
54
+ ... field_path="building",
55
+ ... field_name="",
56
+ ... lookup="exact",
57
+ ... column=building_alias,
58
+ ... join_node=JoinNode(path="building", ...)
59
+ ... )
60
+
61
+ Note:
62
+ The join_node reference enables ConditionBuilder to update JOIN metadata
63
+ based on the lookup operator (e.g., marking has_nullable_check for isnull
64
+ lookups). This bidirectional communication ensures optimal JOIN type
65
+ selection.
66
+ """
67
+
68
+ field_path: str # "building" or "books__author"
69
+ field_name: str # "name" or "title" or "" for direct relation
70
+ lookup: str # "exact", "gte", "icontains", etc.
71
+ column: Any # SQLAlchemy column or alias
72
+ join_node: JoinNode | None = None # Optional JoinNode reference
73
+
74
+
75
+ @dataclass(slots=True)
76
+ class ParsedFieldKey:
77
+ """Result of field key parsing containing relationship and field components.
78
+
79
+ ParsedFieldKey represents the structural decomposition of a Django-style field
80
+ key into its constituent parts: relationship traversal path, terminal field name,
81
+ and lookup operator. This is a pure data structure with no behavior, serving as
82
+ the intermediate result between key parsing and column resolution.
83
+
84
+ The parsing process uses model introspection to distinguish relationships from
85
+ fields and lookups, but doesn't create JOINs or resolve columns - that's
86
+ FieldResolver's job.
87
+
88
+ Attributes:
89
+ relationship_parts: List of relationship names to traverse in order.
90
+ Empty list for root model fields. Examples: [], ["building"], ["user", "books"].
91
+ field_name: Terminal field name to resolve on the final model.
92
+ Empty string for direct relation filters.
93
+ Examples: "name", "title", "".
94
+ lookup: Lookup operator extracted from the key.
95
+ Defaults to "exact" if not specified.
96
+ Examples: "exact", "gte", "icontains".
97
+ is_simple: True if no relationship traversal required (optimization flag).
98
+ False if relationship_parts is non-empty.
99
+
100
+ Example:
101
+ >>> # Simple field: "status"
102
+ >>> ParsedFieldKey(
103
+ ... relationship_parts=[],
104
+ ... field_name="status",
105
+ ... lookup="exact",
106
+ ... is_simple=True
107
+ ... )
108
+ >>>
109
+ >>> # Field with lookup: "price__gte"
110
+ >>> ParsedFieldKey(
111
+ ... relationship_parts=[],
112
+ ... field_name="price",
113
+ ... lookup="gte",
114
+ ... is_simple=True
115
+ ... )
116
+ >>>
117
+ >>> # Single relationship: "building__name"
118
+ >>> ParsedFieldKey(
119
+ ... relationship_parts=["building"],
120
+ ... field_name="name",
121
+ ... lookup="exact",
122
+ ... is_simple=False
123
+ ... )
124
+ >>>
125
+ >>> # Nested relationships: "user__books__title__icontains"
126
+ >>> ParsedFieldKey(
127
+ ... relationship_parts=["user", "books"],
128
+ ... field_name="title",
129
+ ... lookup="icontains",
130
+ ... is_simple=False
131
+ ... )
132
+ >>>
133
+ >>> # Direct relation filter: "building"
134
+ >>> ParsedFieldKey(
135
+ ... relationship_parts=["building"],
136
+ ... field_name="",
137
+ ... lookup="exact",
138
+ ... is_simple=False
139
+ ... )
140
+
141
+ Note:
142
+ The is_simple flag enables fast-path optimization in FieldResolver - simple
143
+ fields can skip JOIN graph interaction entirely. This is a significant
144
+ performance win for queries without relationship traversal.
145
+ """
146
+
147
+ relationship_parts: list[str] # ["building"] or ["user", "books"]
148
+ field_name: str # "name" or "title" or ""
149
+ lookup: str # "icontains", "exact", etc.
150
+ is_simple: bool # True if no relationship traversal
File without changes
@@ -0,0 +1,376 @@
1
+ from __future__ import annotations
2
+
3
+ import operator
4
+ from collections.abc import Callable
5
+ from typing import TYPE_CHECKING, Any
6
+
7
+ if TYPE_CHECKING:
8
+ from async_orm_kit.query.expressions.f import FExpression
9
+
10
+
11
+ class CombinableMixin:
12
+ """Mixin that adds arithmetic operators to ORM expression classes.
13
+
14
+ Enables any ORM expression with a ``resolve(field_resolver)`` method to
15
+ participate in database-level arithmetic through Python's operator
16
+ overloading. All operations produce :class:`FExpression` instances that
17
+ defer actual SQL generation until query compilation time.
18
+
19
+ This mixin implements both forward (``self OP other``) and reflected
20
+ (``other OP self``) variants for all binary operators, ensuring correct
21
+ behavior regardless of operand order. Unary operators (negation, absolute
22
+ value) are also supported.
23
+
24
+ Supported Operations:
25
+ - **Addition**: ``expr + other``, ``other + expr``
26
+ - **Subtraction**: ``expr - other``, ``other - expr``
27
+ - **Multiplication**: ``expr * other``, ``other * expr``
28
+ - **Division**: ``expr / other``, ``other / expr``
29
+ - **Modulo**: ``expr % other``, ``other % expr``
30
+ - **Negation**: ``-expr``
31
+ - **Absolute Value**: ``abs(expr)``
32
+
33
+ Classes Using This Mixin:
34
+ - :class:`F` — field references (``F("price") * 1.1``)
35
+ - :class:`Value` — literal values (``Value(100) - F("discount")``)
36
+ - :class:`Aggregate` — aggregate results (``Sum("qty") * F("price")``)
37
+ - :class:`FExpression` — nested expressions (composable trees)
38
+
39
+ Example:
40
+ >>> from async_orm_kit.query.expressions import F, Value
41
+ >>>
42
+ >>> # Simple field arithmetic — produces FExpression tree
43
+ >>> discount_price = F("price") * Value(0.9)
44
+ >>>
45
+ >>> # Mixed operand types — reflected operators handle int/float LHS
46
+ >>> adjusted = 100 - F("discount") # calls F.__rsub__, wraps 100 as LHS
47
+ >>>
48
+ >>> # Deep composition — arbitrary nesting
49
+ >>> margin = (F("revenue") - F("cost")) / F("revenue") * Value(100)
50
+ >>>
51
+ >>> # Unary operators
52
+ >>> negated = -F("balance") # FExpression(None, operator.neg, F("balance"))
53
+ >>> absolute = abs(F("balance")) # FExpression(None, "abs", F("balance"))
54
+
55
+ Note:
56
+ - All operators return new :class:`FExpression` instances; the original
57
+ expression objects are **never mutated** (immutable pattern).
58
+ - The ``other`` operand can be any type: another ``CombinableMixin``
59
+ subclass, a raw Python scalar (int, float, Decimal), or any object.
60
+ Non-expression operands are wrapped in :class:`Value` during
61
+ resolution by :class:`FExpression`.
62
+ - Reflected operators (``__radd__``, ``__rsub__``, etc.) are invoked by
63
+ Python when the left operand doesn't support the operation, enabling
64
+ expressions like ``2 * F("price")`` where ``int.__mul__`` returns
65
+ ``NotImplemented``.
66
+
67
+ See Also:
68
+ - :class:`FExpression` for the expression tree node structure and
69
+ resolution logic.
70
+ - :class:`F` for field reference construction.
71
+ - :class:`Value` for literal value wrapping.
72
+ """
73
+
74
+ def _make_expression(
75
+ self,
76
+ op: Callable,
77
+ other: Any,
78
+ reverse: bool = False,
79
+ ) -> FExpression:
80
+ """Create a binary :class:`FExpression` node from this expression and another operand.
81
+
82
+ Factory method used by all arithmetic dunder methods to construct
83
+ expression tree nodes. Handles operand ordering for both forward
84
+ and reflected operations.
85
+
86
+ Args:
87
+ op: Python operator function from the :mod:`operator` module
88
+ (e.g., ``operator.add``, ``operator.mul``). Stored in the
89
+ resulting :class:`FExpression` and applied during resolution.
90
+ other: Right-hand operand for forward operations, or left-hand
91
+ operand for reflected operations. Can be any type — non-expression
92
+ values are wrapped in :class:`Value` during FExpression resolution.
93
+ reverse: If ``True``, places ``other`` as the left operand and
94
+ ``self`` as the right operand. Used by reflected operators
95
+ (``__radd__``, ``__rsub__``, etc.) to preserve mathematical
96
+ correctness for non-commutative operations.
97
+
98
+ Returns:
99
+ FExpression: New expression tree node representing ``self OP other``
100
+ (forward) or ``other OP self`` (reflected).
101
+
102
+ Example:
103
+ >>> expr = F("price")
104
+ >>> # Forward: F("price") + 10 → FExpression(F("price"), add, 10)
105
+ >>> result = expr._make_expression(operator.add, 10)
106
+ >>>
107
+ >>> # Reflected: 10 - F("price") → FExpression(10, sub, F("price"))
108
+ >>> result = expr._make_expression(operator.sub, 10, reverse=True)
109
+
110
+ Note:
111
+ Uses a deferred import for :class:`FExpression` to avoid circular
112
+ dependencies, since FExpression itself inherits CombinableMixin.
113
+ """
114
+ from async_orm_kit.query.expressions.f import FExpression
115
+
116
+ if reverse:
117
+ return FExpression(other, op, self)
118
+ return FExpression(self, op, other)
119
+
120
+ def __add__(self, other: Any) -> FExpression:
121
+ """Create addition expression: ``self + other``.
122
+
123
+ Args:
124
+ other: Right-hand operand. Can be another expression or a scalar.
125
+
126
+ Returns:
127
+ FExpression representing ``self + other``.
128
+
129
+ Example:
130
+ >>> F("base_price") + F("tax")
131
+ >>> F("score") + 10
132
+ """
133
+ return self._make_expression(operator.add, other)
134
+
135
+ def __sub__(self, other: Any) -> FExpression:
136
+ """Create subtraction expression: ``self - other``.
137
+
138
+ Args:
139
+ other: Right-hand operand (subtrahend).
140
+
141
+ Returns:
142
+ FExpression representing ``self - other``.
143
+
144
+ Example:
145
+ >>> F("revenue") - F("cost")
146
+ >>> F("stock") - 1
147
+ """
148
+ return self._make_expression(operator.sub, other)
149
+
150
+ def __mul__(self, other: Any) -> FExpression:
151
+ """Create multiplication expression: ``self * other``.
152
+
153
+ Args:
154
+ other: Right-hand operand (multiplier).
155
+
156
+ Returns:
157
+ FExpression representing ``self * other``.
158
+
159
+ Example:
160
+ >>> F("price") * F("quantity")
161
+ >>> F("amount") * Value(Decimal("0.09")) # tax calculation
162
+ """
163
+ return self._make_expression(operator.mul, other)
164
+
165
+ def __truediv__(self, other: Any) -> FExpression:
166
+ """Create true division expression: ``self / other``.
167
+
168
+ Args:
169
+ other: Right-hand operand (divisor).
170
+
171
+ Returns:
172
+ FExpression representing ``self / other``.
173
+
174
+ Example:
175
+ >>> F("total") / F("count") # average
176
+ >>> F("profit") / F("revenue") * 100 # profit margin %
177
+
178
+ Note:
179
+ Database behavior for division by zero varies by backend:
180
+
181
+ - **PostgreSQL**: Raises ``division_by_zero`` error.
182
+ - **SQLite**: Returns ``NULL``.
183
+ - **MySQL**: Returns ``NULL``.
184
+
185
+ Consider using ``Case/When`` guards for safe division.
186
+ """
187
+ return self._make_expression(operator.truediv, other)
188
+
189
+ def __mod__(self, other: Any) -> FExpression:
190
+ """Create modulo expression: ``self % other``.
191
+
192
+ Args:
193
+ other: Right-hand operand (divisor for remainder).
194
+
195
+ Returns:
196
+ FExpression representing ``self % other``.
197
+
198
+ Example:
199
+ >>> F("row_number") % 2 # odd/even check
200
+ >>> F("seconds") % 60 # remaining seconds
201
+ """
202
+ return self._make_expression(operator.mod, other)
203
+
204
+ # ── Binary Arithmetic (Reflected) ───────────────────────────────
205
+
206
+ def __radd__(self, other: Any) -> FExpression:
207
+ """Create reflected addition: ``other + self``.
208
+
209
+ Called when the left operand's ``__add__`` returns ``NotImplemented``.
210
+ Since addition is commutative, the result is mathematically equivalent
211
+ to ``self + other``, but operand order is preserved for SQL clarity.
212
+
213
+ Args:
214
+ other: Left-hand operand (typically a Python scalar).
215
+
216
+ Returns:
217
+ FExpression representing ``other + self``.
218
+
219
+ Example:
220
+ >>> 100 + F("bonus") # int.__add__(F) → NotImplemented → F.__radd__(100)
221
+ """
222
+ return self._make_expression(operator.add, other, reverse=True)
223
+
224
+ def __rsub__(self, other: Any) -> FExpression:
225
+ """Create reflected subtraction: ``other - self``.
226
+
227
+ Called when the left operand's ``__sub__`` returns ``NotImplemented``.
228
+ Operand order matters here since subtraction is **not** commutative.
229
+
230
+ Args:
231
+ other: Left-hand operand (minuend).
232
+
233
+ Returns:
234
+ FExpression representing ``other - self``.
235
+
236
+ Example:
237
+ >>> 1000 - F("spent") # remaining budget
238
+ >>> # Generates: 1000 - "spent" column (NOT "spent" - 1000)
239
+ """
240
+ return self._make_expression(operator.sub, other, reverse=True)
241
+
242
+ def __rmul__(self, other: Any) -> FExpression:
243
+ """Create reflected multiplication: ``other * self``.
244
+
245
+ Called when the left operand's ``__mul__`` returns ``NotImplemented``.
246
+ Since multiplication is commutative, result is mathematically equivalent
247
+ to ``self * other``.
248
+
249
+ Args:
250
+ other: Left-hand operand (typically a Python scalar).
251
+
252
+ Returns:
253
+ FExpression representing ``other * self``.
254
+
255
+ Example:
256
+ >>> 2 * F("quantity") # double the quantity
257
+ """
258
+ return self._make_expression(operator.mul, other, reverse=True)
259
+
260
+ def __rtruediv__(self, other: Any) -> FExpression:
261
+ """Create reflected division: ``other / self``.
262
+
263
+ Called when the left operand's ``__truediv__`` returns ``NotImplemented``.
264
+ Operand order matters since division is **not** commutative.
265
+
266
+ Args:
267
+ other: Left-hand operand (dividend).
268
+
269
+ Returns:
270
+ FExpression representing ``other / self``.
271
+
272
+ Example:
273
+ >>> 1 / F("rate") # inverse rate
274
+ >>> # Generates: 1 / "rate" column (NOT "rate" / 1)
275
+ """
276
+ return self._make_expression(operator.truediv, other, reverse=True)
277
+
278
+ def __rmod__(self, other: Any) -> FExpression:
279
+ """Create reflected modulo: ``other % self``.
280
+
281
+ Called when the left operand's ``__mod__`` returns ``NotImplemented``.
282
+ Operand order matters since modulo is **not** commutative.
283
+
284
+ Args:
285
+ other: Left-hand operand (dividend for remainder).
286
+
287
+ Returns:
288
+ FExpression representing ``other % self``.
289
+
290
+ Example:
291
+ >>> 100 % F("divisor")
292
+ """
293
+ return self._make_expression(operator.mod, other, reverse=True)
294
+
295
+ # ── Unary Operators ─────────────────────────────────────────────
296
+
297
+ def __neg__(self) -> FExpression:
298
+ """Create negation expression: ``-self``.
299
+
300
+ Produces a unary :class:`FExpression` with ``None`` as the left operand
301
+ and ``operator.neg`` as the operation. During resolution, FExpression
302
+ recognizes the ``None`` left operand and applies unary negation.
303
+
304
+ Returns:
305
+ FExpression representing ``-self``.
306
+
307
+ Example:
308
+ >>> -F("balance") # negate balance
309
+ >>> -Sum("adjustments") # negate aggregate result
310
+ >>>
311
+ >>> # Equivalent SQL: -"balance", -SUM("adjustments")
312
+ """
313
+ from async_orm_kit.query.expressions.f import FExpression
314
+
315
+ return FExpression(None, operator.neg, self)
316
+
317
+ def __abs__(self) -> FExpression:
318
+ """Create absolute value expression: ``abs(self)``.
319
+
320
+ Produces a unary :class:`FExpression` with ``None`` as the left operand
321
+ and the string ``"abs"`` as the operation identifier. During resolution,
322
+ FExpression maps ``"abs"`` to the ``func.abs()`` SQL function.
323
+
324
+ Returns:
325
+ FExpression representing ``ABS(self)``.
326
+
327
+ Example:
328
+ >>> abs(F("profit_loss")) # absolute value of column
329
+ >>> abs(F("actual") - F("expected")) # absolute deviation
330
+ >>>
331
+ >>> # Equivalent SQL: ABS("profit_loss"), ABS("actual" - "expected")
332
+
333
+ Note:
334
+ Uses the string ``"abs"`` instead of ``operator.abs`` because
335
+ Python's :mod:`operator` module does not provide an ``abs``
336
+ function. The :class:`FExpression` resolver handles this string
337
+ specially by delegating to ``sqlalchemy.func.abs()``.
338
+ """
339
+ from async_orm_kit.query.expressions.f import FExpression
340
+
341
+ return FExpression(None, "abs", self)
342
+
343
+ def __floordiv__(self, other):
344
+ """Portable floor division using SQL FLOOR(lhs / rhs)."""
345
+ return self._make_expression("floordiv", other)
346
+
347
+ def __rfloordiv__(self, other):
348
+ return self._make_expression("floordiv", other, reverse=True)
349
+
350
+ def __and__(self, other):
351
+ """Bitwise AND (SQLAlchemy's dialect-aware operator)."""
352
+ return self._make_expression("bit_and", other)
353
+
354
+ def __rand__(self, other):
355
+ return self._make_expression("bit_and", other, reverse=True)
356
+
357
+ def __or__(self, other):
358
+ """Bitwise OR; Q expressions use their own logical OR."""
359
+ return self._make_expression("bit_or", other)
360
+
361
+ def __ror__(self, other):
362
+ return self._make_expression("bit_or", other, reverse=True)
363
+
364
+ def __invert__(self):
365
+ """Bitwise NOT; Q expressions use their own logical negation."""
366
+ from async_orm_kit.query.expressions.f import FExpression
367
+
368
+ return FExpression(None, "bit_not", self)
369
+
370
+ def eq(self, other):
371
+ """Explicit SQL equality, even for Value objects with structural __eq__."""
372
+ return self._make_expression(operator.eq, other)
373
+
374
+ def ne(self, other):
375
+ """Explicit SQL inequality, independent of Python object equality."""
376
+ return self._make_expression(operator.ne, other)