Coverage for snekql/expressions.py: 83%

29 statements  

« prev     ^ index     » next       coverage.py v7.14.1, created at 2026-06-07 21:13 +0300

1"""Query Builder expression objects shared by fields and queries.""" 

2 

3from __future__ import annotations 

4 

5from dataclasses import dataclass, field 

6from typing import TypeVar 

7 

8from snekql.errors import QueryConstructionError 

9 

10OwnerT = TypeVar("OwnerT") 

11 

12 

13@dataclass(frozen=True) 

14class Predicate[OwnerT]: 

15 """Boolean SQL predicate for one table model. 

16 

17 Predicates are produced by column descriptor methods such as `User.email.eq`. 

18 They compose with `&`, `|`, and `~` instead of Python comparison operators. 

19 """ 

20 

21 kind: str = "" 

22 column: object | None = None 

23 value: object = None 

24 values: tuple[object, ...] = field(default_factory=tuple) 

25 children: tuple[Predicate[OwnerT], ...] = field(default_factory=tuple) 

26 

27 def __and__(self, other: Predicate[OwnerT]) -> Predicate[OwnerT]: 

28 return Predicate(kind="and", children=(self, other)) 

29 

30 def __or__(self, other: Predicate[OwnerT]) -> Predicate[OwnerT]: 

31 return Predicate(kind="or", children=(self, other)) 

32 

33 def __invert__(self) -> Predicate[OwnerT]: 

34 return Predicate(kind="not", children=(self,)) 

35 

36 def __bool__(self) -> bool: 

37 msg = "predicates cannot be used as booleans" 

38 raise QueryConstructionError(msg) 

39 

40 

41@dataclass(frozen=True) 

42class OrderBy[OwnerT]: 

43 """SQL ordering expression for one table model. 

44 

45 `OrderBy` values are produced by column descriptor methods like `.asc()` and 

46 `.desc()` and consumed by select query builders. 

47 """ 

48 

49 column: object | None = None 

50 direction: str = "" 

51 

52 

53@dataclass(frozen=True) 

54class Assignment[OwnerT]: 

55 """SQL update assignment for one table model. 

56 

57 `Assignment` values are produced by update-assignable column descriptors via 

58 `.to(value)` and consumed by `update(Model).set(...)`. 

59 """ 

60 

61 column: object | None = None 

62 value: object = None