Coverage for snekql/indexes.py: 90%
39 statements
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-07 21:13 +0300
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-07 21:13 +0300
1"""Public index declarations and normalized model index metadata."""
3from __future__ import annotations
5from dataclasses import dataclass
6from typing import Any, TypeVar, cast, overload
8from snekql.errors import ModelDeclarationError
9from snekql.storage import Attr
11OwnerT = TypeVar("OwnerT")
14@dataclass(frozen=True)
15class Index[OwnerT]:
16 """Public table-level index declaration.
18 >>> class User[S = Pending](Model[S, "User[Fetched]"]):
19 ... email: User.Col[str] = Text(nullable=False)
20 ... __indexes__ = [Index(email)]
21 """
23 columns: tuple[Attr[Any, Any, OwnerT, Any, Any], ...]
24 unique: bool
25 name: str | None
27 @overload
28 def __init__(
29 self,
30 *columns: Attr[Any, Any, OwnerT, Any, Any],
31 unique: bool = False,
32 name: str | None = None,
33 ) -> None: ...
35 @overload
36 def __init__(
37 self,
38 *columns: object,
39 unique: bool = False,
40 name: str | None = None,
41 ) -> None: ...
43 def __init__(
44 self,
45 *columns: object,
46 unique: bool = False,
47 name: str | None = None,
48 ) -> None:
49 if not columns:
50 msg = "Index() requires at least one column"
51 raise ModelDeclarationError(msg)
52 seen_column_ids: set[int] = set()
53 for column in columns:
54 if not isinstance(column, Attr):
55 msg = "Index() arguments must be snekql column descriptors"
56 raise ModelDeclarationError(msg)
57 attr_column = cast("Attr[Any, Any, OwnerT, Any, Any]", column)
58 column_id = id(attr_column)
59 if column_id in seen_column_ids:
60 msg = "Index() cannot repeat a column"
61 raise ModelDeclarationError(msg)
62 seen_column_ids.add(column_id)
63 object.__setattr__(
64 self,
65 "columns",
66 cast("tuple[Attr[Any, Any, OwnerT, Any, Any], ...]", columns),
67 )
68 object.__setattr__(self, "unique", unique)
69 object.__setattr__(self, "name", name)
72@dataclass(frozen=True, kw_only=True)
73class NormalizedIndex:
74 """Immutable model-owned index metadata used by schema startup."""
76 column_names: tuple[str, ...]
77 name: str
78 unique: bool
81def require_index_declaration(value: object) -> Index[Any]:
82 """Validate a public __indexes__ entry before normalization."""
84 if not isinstance(value, Index):
85 msg = "__indexes__ entries must be Index declarations"
86 raise ModelDeclarationError(msg)
87 return cast("Index[Any]", value)