Coverage for src/typedal/fields.py: 55%

1"""

2 This file contains available Field types.

3"""

4

5 import datetime as dt

6 import decimal

7 import types

8 import typing

9

10 from pydal.objects import Table

11

12 from .core import TypeDAL, TypedFieldType, TypedTable

13

14 T = typing.TypeVar("T", bound=typing.Type[typing.Any])

15

16

17## general

18

19

20 def TypedField(

21 _type: T | types.UnionType,

22 **kwargs: typing.Any,

23) -> T:

24 """

25 sneaky: its a function and not a class, because there's a return type.

26

27 and the return type (T) is the input type in _type

28

29 Example:

30 age: TypedField(int, default=18)

31 """

32 return TypedFieldType(_type, **kwargs)

33

34

35 TYPE_STR = typing.Type[str]

36 TYPE_INT = typing.Type[int]

37 TYPE_LIST_OF_INT = typing.Type[list[int]]

38

39

40## specific

41 def StringField(**kw: typing.Any) -> TYPE_STR:

42 """

43 Pydal type is string, Python type is str.

44 """

45 kw["type"] = "string"

46 return TypedField(str, **kw)

47

48

49 String = StringField

50

51

52 def TextField(**kw: typing.Any) -> TYPE_STR:

53 """

54 Pydal type is text, Python type is str.

55 """

56 kw["type"] = "text"

57 return TypedField(str, **kw)

58

59

60 Text = TextField

61

62

63 def BlobField(**kw: typing.Any) -> typing.Type[bytes]:

64 """

65 Pydal type is blob, Python type is bytes.

66 """

67 kw["type"] = "blob"

68 return TypedField(bytes, **kw)

69

70

71 Blob = BlobField

72

73

74 def BooleanField(**kw: typing.Any) -> typing.Type[bool]:

75 """

76 Pydal type is boolean, Python type is bool.

77 """

78 kw["type"] = "boolean"

79 return TypedField(bool, **kw)

80

81

82 Boolean = BooleanField

83

84

85 def IntegerField(**kw: typing.Any) -> typing.Type[int]:

86 """

87 Pydal type is integer, Python type is int.

88 """

89 kw["type"] = "integer"

90 return TypedField(int, **kw)

91

92

93 Integer = IntegerField

94

95

96 def DoubleField(**kw: typing.Any) -> typing.Type[float]:

97 """

98 Pydal type is double, Python type is float.

99 """

100 kw["type"] = "double"

101 return TypedField(float, **kw)

102

103

104 Double = DoubleField

105

106

107 def DecimalField(n: int, m: int, **kw: typing.Any) -> typing.Type[decimal.Decimal]:

108 """

109 Pydal type is decimal, Python type is Decimal.

110 """

111 kw["type"] = f"decimal({n}, {m})"

112 return TypedField(decimal.Decimal, **kw)

113

114

115 Decimal = DecimalField

116

117

118 def DateField(**kw: typing.Any) -> typing.Type[dt.date]:

119 """

120 Pydal type is date, Python type is datetime.date.

121 """

122 kw["type"] = "date"

123 return TypedField(dt.date, **kw)

124

125

126 Date = DateField

127

128

129 def TimeField(**kw: typing.Any) -> typing.Type[dt.time]:

130 """

131 Pydal type is time, Python type is datetime.time.

132 """

133 kw["type"] = "time"

134 return TypedField(dt.time, **kw)

135

136

137 Time = TimeField

138

139

140 def DatetimeField(**kw: typing.Any) -> typing.Type[dt.datetime]:

141 """

142 Pydal type is datetime, Python type is datetime.datetime.

143 """

144 kw["type"] = "datetime"

145 return TypedField(dt.datetime, **kw)

146

147

148 Datetime = DatetimeField

149

150

151 def PasswordField(**kw: typing.Any) -> TYPE_STR:

152 """

153 Pydal type is password, Python type is str.

154 """

155 kw["type"] = "password"

156 return TypedField(str, **kw)

157

158

159 Password = PasswordField

160

161

162 def UploadField(**kw: typing.Any) -> TYPE_STR:

163 """

164 Pydal type is upload, Python type is str.

165 """

166 kw["type"] = "upload"

167 return TypedField(str, **kw)

168

169

170 Upload = UploadField

171

172 T_subclass = typing.TypeVar("T_subclass", TypedTable, Table)

173

174

175 def ReferenceField(other_table: str | TypedTable | Table | T_subclass, **kw: typing.Any) -> TYPE_INT:

176 """

177 Pydal type is reference, Python type is int (id).

178 """

179 if isinstance(other_table, str):

180 kw["type"] = f"reference {other_table}"

181 elif isinstance(other_table, Table):

182 # db.table

183 kw["type"] = f"reference {other_table._tablename}"

184 elif issubclass(type(other_table), type) and issubclass(other_table, TypedTable):

185 # SomeTable

186 snakename = TypeDAL._to_snake(other_table.__name__)

187 kw["type"] = f"reference {snakename}"

188 else:

189 raise ValueError(f"Don't know what to do with {type(other_table)}")

190

191 return TypedField(int, **kw)

192

193

194 Reference = ReferenceField

195

196

197 def ListStringField(**kw: typing.Any) -> typing.Type[list[str]]:

198 """

199 Pydal type is list:string, Python type is list of str.

200 """

201 kw["type"] = "list:string"

202 return TypedField(list[str], **kw)

203

204

205 ListString = ListStringField

206

207

208 def ListIntegerField(**kw: typing.Any) -> TYPE_LIST_OF_INT:

209 """

210 Pydal type is list:integer, Python type is list of int.

211 """

212 kw["type"] = "list:integer"

213 return TypedField(list[int], **kw)

214

215

216 ListInteger = ListIntegerField

217

218

219 def ListReferenceField(other_table: str, **kw: typing.Any) -> TYPE_LIST_OF_INT:

220 """

221 Pydal type is list:reference, Python type is list of int (id).

222 """

223 kw["type"] = f"list:reference {other_table}"

224 return TypedField(list[int], **kw)

225

226

227 ListReference = ListReferenceField

228

229

230 def JSONField(**kw: typing.Any) -> typing.Type[object]:

231 """

232 Pydal type is json, Python type is object (can be anything JSON-encodable).

233 """

234 kw["type"] = "json"

235 return TypedField(object, **kw)

236

237

238 def BigintField(**kw: typing.Any) -> TYPE_INT:

239 """

240 Pydal type is bigint, Python type is int.

241 """

242 kw["type"] = "bigint"

243 return TypedField(int, **kw)

244

245

246 Bigint = BigintField