Coverage for src/typedal/types.py: 100%

1"""

2 Stuff to make mypy happy.

3"""

4

5 import typing

6 from datetime import datetime

7 from typing import Any, Optional, TypedDict

8

9 from pydal.adapters.base import BaseAdapter

10 from pydal.helpers.classes import OpRow as _OpRow

11 from pydal.helpers.classes import Reference as _Reference

12 from pydal.objects import Expression as _Expression

13 from pydal.objects import Field as _Field

14 from pydal.objects import Query as _Query

15 from pydal.objects import Rows as _Rows

16 from pydal.objects import Set as _Set

17 from pydal.objects import Table as _Table

18 from pydal.validators import Validator as _Validator

19 from typing_extensions import NotRequired

20

21 if typing.TYPE_CHECKING:

22 from .core import TypedField

23

24 AnyDict: typing.TypeAlias = dict[str, Any]

25

26

27 class Query(_Query): # type: ignore

28 """

29 Pydal Query object.

30

31 Makes mypy happy.

32 """

33

34

35 class Expression(_Expression): # type: ignore

36 """

37 Pydal Expression object.

38

39 Make mypy happy.

40 """

41

42

43 class Set(_Set): # type: ignore

44 """

45 Pydal Set object.

46

47 Make mypy happy.

48 """

49

50

51 if typing.TYPE_CHECKING:

52

53 class OpRow:

54 """

55 Pydal OpRow object for typing (otherwise mypy thinks it's Any).

56

57 Make mypy happy.

58 """

59

60 def __getitem__(self, item: str) -> typing.Any:

61 """

62 Dict [] get notation.

63 """

64

65 def __setitem__(self, key: str, value: typing.Any) -> None:

66 """

67 Dict [] set notation.

68 """

69

70 # ... and more methods

71

72 else:

73

74 class OpRow(_OpRow): # type: ignore

75 """

76 Pydal OpRow object at runtime just uses pydal's version.

77

78 Make mypy happy.

79 """

80

81

82 class Reference(_Reference): # type: ignore

83 """

84 Pydal Reference object.

85

86 Make mypy happy.

87 """

88

89

90 class Field(_Field): # type: ignore

91 """

92 Pydal Field object.

93

94 Make mypy happy.

95 """

96

97

98 class Rows(_Rows): # type: ignore

99 """

100 Pydal Rows object.

101

102 Make mypy happy.

103 """

104

105 def column(self, column: typing.Any = None) -> list[typing.Any]:

106 """

107 Get a list of all values in a specific column.

108

109 Example:

110 rows.column('name') -> ['Name 1', 'Name 2', ...]

111 """

112 return [r[str(column) if column else self.colnames[0]] for r in self]

113

114

115 class Validator(_Validator): # type: ignore

116 """

117 Pydal Validator object.

118

119 Make mypy happy.

120 """

121

122

123 class _Types:

124 """

125 Internal type storage for stuff that mypy otherwise won't understand.

126 """

127

128 NONETYPE = type(None)

129

130

131 class Pagination(TypedDict):

132 """

133 Pagination key of a paginate dict has these items.

134 """

135

136 total_items: int

137 current_page: int

138 per_page: int

139 total_pages: int

140 has_next_page: bool

141 has_prev_page: bool

142 next_page: Optional[int]

143 prev_page: Optional[int]

144

145

146 class PaginateDict(TypedDict):

147 """

148 Result of PaginatedRows.as_dict().

149 """

150

151 data: dict[int, AnyDict]

152 pagination: Pagination

153

154

155 class CacheMetadata(TypedDict):

156 """

157 Used by query builder metadata in the 'cache' key.

158 """

159

160 enabled: bool

161 depends_on: list[Any]

162 key: NotRequired[str | None]

163 status: NotRequired[str | None]

164 expires_at: NotRequired[datetime | None]

165 cached_at: NotRequired[datetime | None]

166

167

168 class PaginationMetadata(TypedDict):

169 """

170 Used by query builder metadata in the 'pagination' key.

171 """

172

173 limit: int

174 current_page: int

175 max_page: int

176 rows: int

177 min_max: tuple[int, int]

178

179

180 class TableProtocol(typing.Protocol): # pragma: no cover

181 """

182 Make mypy happy.

183 """

184

185 id: "TypedField[int]"

186

187 def __getitem__(self, item: str) -> Field:

188 """

189 Tell mypy a Table supports dictionary notation for columns.

190 """

191

192

193 class Table(_Table, TableProtocol): # type: ignore

194 """

195 Make mypy happy.

196 """

197

198

199 class CacheFn(typing.Protocol):

200 """

201 The cache model (e.g. cache.ram) accepts these parameters (all filled by dfeault).

202 """

203

204 def __call__(

205 self: BaseAdapter,

206 sql: str = "",

207 fields: typing.Iterable[str] = (),

208 attributes: typing.Iterable[str] = (),

209 colnames: typing.Iterable[str] = (),

210 ) -> Rows:

211 """

212 Only used for type-hinting.

213 """

214

215

216# CacheFn = typing.Callable[[], Rows]

217 CacheModel = typing.Callable[[str, CacheFn, int], Rows]

218 CacheTuple = tuple[CacheModel, int]

219

220

221 class SelectKwargs(typing.TypedDict, total=False):

222 """

223 Possible keyword arguments for .select().

224 """

225

226 join: Optional[list[Expression]]

227 left: Optional[list[Expression]]

228 orderby: Optional[Expression | str | Table]

229 limitby: Optional[tuple[int, int]]

230 distinct: bool | Field | Expression

231 orderby_on_limitby: bool

232 cacheable: bool

233 cache: CacheTuple

234

235

236 class Metadata(TypedDict):

237 """

238 Loosely structured metadata used by Query Builder.

239 """

240

241 cache: NotRequired[CacheMetadata]

242 pagination: NotRequired[PaginationMetadata]

243

244 query: NotRequired[Query | str | None]

245 ids: NotRequired[str]

246

247 final_query: NotRequired[Query | str | None]

248 final_args: NotRequired[list[Any]]

249 final_kwargs: NotRequired[SelectKwargs]

250 relationships: NotRequired[set[str]]

251

252 sql: NotRequired[str]