sera-2 1.23.0__py3-none-any.whl → 1.25.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.
@@ -0,0 +1,369 @@
1
+ from __future__ import annotations
2
+
3
+ from codegen.models import ImportHelper, PredefinedFn, Program, expr, stmt
4
+
5
+ from sera.make.ts_frontend.misc import TS_GLOBAL_IDENTS, get_normalizer
6
+ from sera.misc import assert_not_null, to_camel_case, to_pascal_case
7
+ from sera.models import (
8
+ Class,
9
+ DataProperty,
10
+ Enum,
11
+ ObjectProperty,
12
+ Package,
13
+ Schema,
14
+ TsTypeWithDep,
15
+ )
16
+ from sera.typing import is_set
17
+
18
+
19
+ def make_class_schema(schema: Schema, cls: Class, pkg: Package):
20
+ """Make schema definition for the class in frontend so that components can use this information select
21
+ appropriate components to display or edit the data.
22
+
23
+ Args:
24
+ schema: The overall schema of the application, which contains all classes & enums
25
+ cls: The class that we want to generate the schema
26
+ pkg: The output package (directory) for the class in the `@.models` package. For example, if the
27
+ class is `User`, then the package would be `src/models/user`.
28
+
29
+ Returns:
30
+ This function do not return anything as it writes the schema directly to a file.
31
+ """
32
+ if not cls.is_public:
33
+ # skip classes that are not public
34
+ return
35
+
36
+ program = Program()
37
+ prop_defs: list[tuple[DataProperty | ObjectProperty, expr.Expr, expr.Expr]] = []
38
+ prop_normalizers: list[tuple[expr.Expr, expr.Expr]] = []
39
+
40
+ import_helper = ImportHelper(program, TS_GLOBAL_IDENTS)
41
+
42
+ for prop in cls.properties.values():
43
+ # we must include private properties that are needed during upsert for our forms.
44
+ # if prop.data.is_private:
45
+ # # skip private fields as this is for APIs exchange
46
+ # continue
47
+ tspropname = to_camel_case(prop.name)
48
+ pypropname = prop.name
49
+ if isinstance(prop, ObjectProperty) and prop.target.db is not None:
50
+ # this is a database object, we append id to the property name
51
+ tspropname = tspropname + "Id"
52
+ pypropname = prop.name + "_id"
53
+
54
+ tsprop = {}
55
+
56
+ if isinstance(prop, DataProperty):
57
+ tstype = prop.get_data_model_datatype().get_typescript_type()
58
+ # for schema definition, we need to use the original type, not the type alias
59
+ # if prop.name == idprop.name:
60
+ # # use id type alias
61
+ # tstype = TsTypeWithDep(f"{cls.name}Id")
62
+ for dep in tstype.deps:
63
+ program.import_(dep, True)
64
+ tsprop = [
65
+ (
66
+ expr.ExprIdent("datatype"),
67
+ (
68
+ expr.ExprConstant(tstype.spectype)
69
+ if tstype.type not in schema.enums
70
+ else expr.ExprConstant("enum")
71
+ ),
72
+ ),
73
+ *(
74
+ [
75
+ (
76
+ expr.ExprIdent("enumType"),
77
+ export_enum_info(program, schema.enums[tstype.type]),
78
+ )
79
+ ]
80
+ if tstype.type in schema.enums
81
+ else []
82
+ ),
83
+ *(
84
+ [
85
+ (
86
+ expr.ExprIdent("foreignKeyTarget"),
87
+ expr.ExprConstant(prop.db.foreign_key.name),
88
+ )
89
+ ]
90
+ if prop.db is not None
91
+ and prop.db.is_primary_key
92
+ and prop.db.foreign_key is not None
93
+ else []
94
+ ),
95
+ (
96
+ expr.ExprIdent("isRequired"),
97
+ expr.ExprConstant(
98
+ not prop.is_optional
99
+ and prop.default_value is None
100
+ and prop.default_factory is None
101
+ ),
102
+ ),
103
+ ]
104
+
105
+ norm_func = get_normalizer(tstype, import_helper)
106
+ if norm_func is not None:
107
+ # we have a normalizer for this type
108
+ prop_normalizers.append((expr.ExprIdent(tspropname), norm_func))
109
+ else:
110
+ assert isinstance(prop, ObjectProperty)
111
+ if prop.target.db is not None:
112
+ # this class is stored in the database, we store the id instead
113
+ tstype = (
114
+ assert_not_null(prop.target.get_id_property())
115
+ .get_data_model_datatype()
116
+ .get_typescript_type()
117
+ )
118
+ else:
119
+ # we are going to store the whole object
120
+ tstype = TsTypeWithDep(
121
+ type=prop.target.name,
122
+ spectype=prop.target.name,
123
+ deps=[
124
+ f"@.models.{prop.target.get_tsmodule_name()}.{prop.target.name}.{prop.target.name}"
125
+ ],
126
+ )
127
+
128
+ # we don't store the type itself, but just the name of the type
129
+ # so not need to import the dependency
130
+ # if tstype.dep is not None:
131
+ # program.import_(
132
+ # tstype.dep,
133
+ # True,
134
+ # )
135
+
136
+ tsprop = [
137
+ (
138
+ expr.ExprIdent("targetClass"),
139
+ expr.ExprConstant(prop.target.name),
140
+ ),
141
+ (
142
+ expr.ExprIdent("datatype"),
143
+ expr.ExprConstant(
144
+ tstype.spectype if prop.target.db is not None else "undefined"
145
+ ),
146
+ ),
147
+ (
148
+ expr.ExprIdent("cardinality"),
149
+ expr.ExprConstant(prop.cardinality.value),
150
+ ),
151
+ (
152
+ expr.ExprIdent("isEmbedded"),
153
+ expr.ExprConstant(prop.target.db is None),
154
+ ),
155
+ (
156
+ expr.ExprIdent("isRequired"),
157
+ expr.ExprConstant(not prop.is_optional),
158
+ ),
159
+ ]
160
+
161
+ prop_defs.append(
162
+ (
163
+ prop,
164
+ expr.ExprIdent(tspropname),
165
+ PredefinedFn.dict(
166
+ [
167
+ (expr.ExprIdent("name"), expr.ExprConstant(pypropname)),
168
+ (expr.ExprIdent("tsName"), expr.ExprConstant(tspropname)),
169
+ (
170
+ expr.ExprIdent("updateFuncName"),
171
+ expr.ExprConstant(f"update{to_pascal_case(prop.name)}"),
172
+ ),
173
+ (
174
+ expr.ExprIdent("label"),
175
+ expr.ExprConstant(prop.label.to_dict()),
176
+ ),
177
+ (
178
+ expr.ExprIdent("description"),
179
+ (
180
+ expr.ExprConstant(prop.description.to_dict())
181
+ if not prop.description.is_empty()
182
+ else expr.ExprConstant("undefined")
183
+ ),
184
+ ),
185
+ (
186
+ expr.ExprIdent("constraints"),
187
+ PredefinedFn.list(
188
+ [
189
+ expr.ExprConstant(
190
+ constraint.get_typescript_constraint()
191
+ )
192
+ for constraint in prop.data.constraints
193
+ ]
194
+ ),
195
+ ),
196
+ ]
197
+ + tsprop
198
+ ),
199
+ )
200
+ )
201
+
202
+ for type in ["ObjectProperty", "DataProperty"]:
203
+ program.import_(f"sera-db.{type}", True)
204
+ if cls.db is not None:
205
+ program.import_(f"sera-db.Schema", True)
206
+ else:
207
+ program.import_(f"sera-db.EmbeddedSchema", True)
208
+
209
+ program.import_(f"@.models.{pkg.dir.name}.{cls.name}.{cls.name}", True)
210
+ program.import_(f"@.models.{pkg.dir.name}.Draft{cls.name}.Draft{cls.name}", True)
211
+ program.import_(
212
+ f"@.models.{pkg.dir.name}.Draft{cls.name}.draft{cls.name}Validators", True
213
+ )
214
+ if cls.db is not None:
215
+ program.import_(f"@.models.{pkg.dir.name}.{cls.name}.{cls.name}Id", True)
216
+
217
+ program.root(
218
+ stmt.LineBreak(),
219
+ stmt.TypescriptStatement(
220
+ f"export type {cls.name}SchemaType = "
221
+ + PredefinedFn.dict(
222
+ (
223
+ [
224
+ (expr.ExprIdent("id"), expr.ExprIdent(f"{cls.name}Id")),
225
+ ]
226
+ if cls.db is not None
227
+ else []
228
+ )
229
+ + [
230
+ (
231
+ expr.ExprIdent("publicProperties"),
232
+ expr.ExprIdent(
233
+ " | ".join(
234
+ [
235
+ expr.ExprConstant(
236
+ to_camel_case(prop.name) + "Id"
237
+ if isinstance(prop, ObjectProperty)
238
+ and prop.target.db is not None
239
+ else to_camel_case(prop.name)
240
+ ).to_typescript()
241
+ for prop in cls.properties.values()
242
+ if not prop.data.is_private
243
+ ]
244
+ )
245
+ ),
246
+ ),
247
+ (
248
+ expr.ExprIdent("allProperties"),
249
+ expr.ExprIdent(
250
+ f"{cls.name}SchemaType['publicProperties']"
251
+ + (
252
+ " | "
253
+ + " | ".join(
254
+ [
255
+ expr.ExprConstant(
256
+ to_camel_case(prop.name)
257
+ ).to_typescript()
258
+ for prop in cls.properties.values()
259
+ if prop.data.is_private
260
+ ]
261
+ )
262
+ if any(
263
+ prop.data.is_private
264
+ for prop in cls.properties.values()
265
+ )
266
+ else ""
267
+ )
268
+ ),
269
+ ),
270
+ (
271
+ expr.ExprIdent("cls"),
272
+ expr.ExprIdent(cls.name),
273
+ ),
274
+ (
275
+ expr.ExprIdent("draftCls"),
276
+ expr.ExprIdent(f"Draft{cls.name}"),
277
+ ),
278
+ ]
279
+ ).to_typescript()
280
+ + ";",
281
+ ),
282
+ stmt.LineBreak(),
283
+ stmt.TypescriptStatement(
284
+ f"const publicProperties: Record<{cls.name}SchemaType['publicProperties'], DataProperty | ObjectProperty> = "
285
+ + PredefinedFn.dict(
286
+ [
287
+ (prop_name, prop_def)
288
+ for prop, prop_name, prop_def in prop_defs
289
+ if not prop.data.is_private
290
+ ]
291
+ ).to_typescript()
292
+ + ";"
293
+ ),
294
+ stmt.LineBreak(),
295
+ stmt.TypescriptStatement(
296
+ (
297
+ f"export const {cls.name}Schema: Schema<{cls.name}SchemaType['id'], {cls.name}SchemaType['cls'], {cls.name}SchemaType['draftCls'], {cls.name}SchemaType['publicProperties'], {cls.name}SchemaType['allProperties'], {cls.name}SchemaType> = "
298
+ if cls.db is not None
299
+ else f"export const {cls.name}Schema: EmbeddedSchema<{cls.name}SchemaType['cls'], {cls.name}SchemaType['draftCls'], {cls.name}SchemaType['publicProperties'], {cls.name}SchemaType['allProperties']> = "
300
+ )
301
+ + PredefinedFn.dict(
302
+ [
303
+ (
304
+ expr.ExprIdent("publicProperties"),
305
+ expr.ExprIdent("publicProperties"),
306
+ ),
307
+ (
308
+ expr.ExprIdent("allProperties"),
309
+ expr.ExprIdent(
310
+ "{ ...publicProperties, "
311
+ + ", ".join(
312
+ [
313
+ f"{prop_name.to_typescript()}: {prop_def.to_typescript()}"
314
+ for prop, prop_name, prop_def in prop_defs
315
+ if prop.data.is_private
316
+ ]
317
+ )
318
+ + "}"
319
+ ),
320
+ ),
321
+ (
322
+ expr.ExprIdent("validators"),
323
+ expr.ExprIdent(f"draft{cls.name}Validators"),
324
+ ),
325
+ (
326
+ expr.ExprIdent("normalizers"),
327
+ PredefinedFn.dict(prop_normalizers),
328
+ ),
329
+ ]
330
+ + (
331
+ [
332
+ (
333
+ expr.ExprIdent("primaryKey"),
334
+ expr.ExprConstant(
335
+ assert_not_null(cls.get_id_property()).name
336
+ ),
337
+ )
338
+ ]
339
+ if cls.db is not None
340
+ else []
341
+ )
342
+ ).to_typescript()
343
+ + ";"
344
+ ),
345
+ )
346
+ pkg.module(cls.name + "Schema").write(program)
347
+
348
+
349
+ def export_enum_info(program: Program, enum: Enum) -> expr.Expr:
350
+ """Export enum information to
351
+
352
+ ```
353
+ {
354
+ type: <EnumType>,
355
+ label: { [value]: MultiLingualString },
356
+ description: { [value]: MultiLingualString }
357
+ }
358
+ ```
359
+ """
360
+ for key in ["Label", "Description"]:
361
+ program.import_(f"@.models.enums.{enum.name}{key}", True)
362
+
363
+ return PredefinedFn.dict(
364
+ [
365
+ (expr.ExprIdent("type"), expr.ExprIdent(enum.name)),
366
+ (expr.ExprIdent("label"), expr.ExprIdent(enum.name + "Label")),
367
+ (expr.ExprIdent("description"), expr.ExprIdent(enum.name + "Description")),
368
+ ]
369
+ )
@@ -0,0 +1,104 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ from typing import Any, Callable
5
+
6
+ from codegen.models import AST, ImportHelper, PredefinedFn, Program, expr, stmt
7
+ from codegen.models.var import DeferredVar
8
+ from loguru import logger
9
+
10
+ from sera.make.ts_frontend.make_class_schema import make_class_schema
11
+ from sera.make.ts_frontend.misc import TS_GLOBAL_IDENTS, get_normalizer
12
+ from sera.misc import (
13
+ assert_isinstance,
14
+ assert_not_null,
15
+ identity,
16
+ to_camel_case,
17
+ to_pascal_case,
18
+ to_snake_case,
19
+ )
20
+ from sera.models import (
21
+ Class,
22
+ DataProperty,
23
+ Enum,
24
+ ObjectProperty,
25
+ Package,
26
+ Schema,
27
+ TsTypeWithDep,
28
+ )
29
+ from sera.typing import is_set
30
+
31
+
32
+ def make_typescript_enums(schema: Schema, target_pkg: Package):
33
+ """Make typescript enum for the schema"""
34
+ enum_pkg = target_pkg.pkg("enums")
35
+
36
+ for enum in schema.enums.values():
37
+ make_enum(enum, enum_pkg)
38
+
39
+ program = Program()
40
+ for enum in schema.enums.values():
41
+ program.import_(f"@.models.enums.{enum.get_tsmodule_name()}.{enum.name}", True)
42
+ program.import_(
43
+ f"@.models.enums.{enum.get_tsmodule_name()}.{enum.name}Label", True
44
+ )
45
+ program.import_(
46
+ f"@.models.enums.{enum.get_tsmodule_name()}.{enum.name}Description", True
47
+ )
48
+
49
+ program.root(
50
+ stmt.LineBreak(),
51
+ stmt.TypescriptStatement(
52
+ "export { "
53
+ + ", ".join([enum.name for enum in schema.enums.values()])
54
+ + ","
55
+ + ", ".join([enum.name + "Label" for enum in schema.enums.values()])
56
+ + ","
57
+ + ", ".join([enum.name + "Description" for enum in schema.enums.values()])
58
+ + "};"
59
+ ),
60
+ )
61
+ enum_pkg.module("index").write(program)
62
+
63
+
64
+ def make_enum(enum: Enum, pkg: Package):
65
+ program = Program()
66
+ program.root(
67
+ stmt.LineBreak(),
68
+ lambda ast: ast.class_like("enum", enum.name)(
69
+ *[
70
+ stmt.DefEnumValueStatement(
71
+ name=value.name,
72
+ value=expr.ExprConstant(value.value),
73
+ )
74
+ for value in enum.values.values()
75
+ ]
76
+ ),
77
+ stmt.LineBreak(),
78
+ stmt.TypescriptStatement(
79
+ f"export const {enum.name}Label = "
80
+ + PredefinedFn.dict(
81
+ [
82
+ (
83
+ expr.ExprConstant(value.value),
84
+ expr.ExprConstant(value.label.to_dict()),
85
+ )
86
+ for value in enum.values.values()
87
+ ]
88
+ ).to_typescript()
89
+ ),
90
+ stmt.LineBreak(),
91
+ stmt.TypescriptStatement(
92
+ f"export const {enum.name}Description = "
93
+ + PredefinedFn.dict(
94
+ [
95
+ (
96
+ expr.ExprConstant(value.value),
97
+ expr.ExprConstant(value.description.to_dict()),
98
+ )
99
+ for value in enum.values.values()
100
+ ]
101
+ ).to_typescript()
102
+ ),
103
+ )
104
+ pkg.module(enum.get_tsmodule_name()).write(program)
@@ -0,0 +1,38 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Optional
4
+
5
+ from codegen.models import AST, ImportHelper, PredefinedFn, Program, expr, stmt
6
+
7
+ from sera.models import (
8
+ Class,
9
+ DataProperty,
10
+ Enum,
11
+ ObjectProperty,
12
+ Package,
13
+ Schema,
14
+ TsTypeWithDep,
15
+ )
16
+
17
+ TS_GLOBAL_IDENTS = {
18
+ "normalizers.normalizeNumber": "sera-db.normalizers",
19
+ "normalizers.normalizeOptionalNumber": "sera-db.normalizers",
20
+ "normalizers.normalizeDate": "sera-db.normalizers",
21
+ "normalizers.normalizeOptionalDate": "sera-db.normalizers",
22
+ }
23
+
24
+
25
+ def get_normalizer(
26
+ tstype: TsTypeWithDep, import_helper: ImportHelper
27
+ ) -> Optional[expr.ExprIdent]:
28
+ if tstype.type == "number":
29
+ return import_helper.use("normalizers.normalizeNumber")
30
+ if tstype.type == "number | undefined":
31
+ return import_helper.use("normalizers.normalizeOptionalNumber")
32
+ if tstype.type == "Date":
33
+ return import_helper.use("normalizers.normalizeDate")
34
+ if tstype.type == "Date | undefined":
35
+ return import_helper.use("normalizers.normalizeOptionalDate")
36
+
37
+ assert "number" not in tstype.type, tstype.type
38
+ return None
sera/models/__init__.py CHANGED
@@ -5,7 +5,13 @@ from sera.models._enum import Enum
5
5
  from sera.models._module import App, Module, Package
6
6
  from sera.models._multi_lingual_string import MultiLingualString
7
7
  from sera.models._parse import parse_schema
8
- from sera.models._property import Cardinality, DataProperty, ObjectProperty, Property
8
+ from sera.models._property import (
9
+ Cardinality,
10
+ DataProperty,
11
+ IndexType,
12
+ ObjectProperty,
13
+ Property,
14
+ )
9
15
  from sera.models._schema import Schema
10
16
 
11
17
  __all__ = [
@@ -14,6 +20,7 @@ __all__ = [
14
20
  "Property",
15
21
  "DataProperty",
16
22
  "ObjectProperty",
23
+ "IndexType",
17
24
  "Class",
18
25
  "Cardinality",
19
26
  "DataType",
sera/models/_class.py CHANGED
@@ -5,7 +5,7 @@ from typing import Optional
5
5
 
6
6
  from sera.misc import to_kebab_case, to_snake_case
7
7
  from sera.models._multi_lingual_string import MultiLingualString
8
- from sera.models._property import DataProperty, ObjectProperty
8
+ from sera.models._property import DataProperty, IndexType, ObjectProperty
9
9
 
10
10
 
11
11
  @dataclass(kw_only=True)
@@ -13,6 +13,7 @@ class Index:
13
13
  name: str
14
14
  columns: list[str]
15
15
  unique: bool = False
16
+ index_type: IndexType = IndexType.DEFAULT
16
17
 
17
18
 
18
19
  @dataclass(kw_only=True)
sera/models/_enum.py CHANGED
@@ -11,6 +11,7 @@ from sera.models._multi_lingual_string import MultiLingualString
11
11
  class EnumValue:
12
12
  name: str
13
13
  value: str | int
14
+ label: MultiLingualString
14
15
  description: MultiLingualString
15
16
 
16
17
 
@@ -44,7 +45,7 @@ class Enum:
44
45
 
45
46
  def get_tsmodule_name(self) -> str:
46
47
  """Get the typescript module name of this enum as if there is a typescript module created to store this enum only."""
47
- return to_kebab_case(self.name)
48
+ return self.name
48
49
 
49
50
  def is_str_enum(self) -> bool:
50
51
  """Check if this enum is a string enum."""
sera/models/_parse.py CHANGED
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import re
4
4
  from copy import deepcopy
5
+ from operator import index
5
6
  from pathlib import Path
6
7
  from typing import Sequence
7
8
 
@@ -30,6 +31,7 @@ from sera.models._property import (
30
31
  ForeignKeyOnDelete,
31
32
  ForeignKeyOnUpdate,
32
33
  GetSCPropValueFunc,
34
+ IndexType,
33
35
  ObjectPropDBInfo,
34
36
  ObjectProperty,
35
37
  PropDataAttrs,
@@ -91,13 +93,17 @@ def _parse_enum(schema: Schema, enum_name: str, enum: dict) -> Enum:
91
93
  for k, v in enum.items():
92
94
  if isinstance(v, (str, int)):
93
95
  values[k] = EnumValue(
94
- name=k, value=v, description=MultiLingualString.en("")
96
+ name=k,
97
+ value=v,
98
+ label=MultiLingualString.en(""),
99
+ description=MultiLingualString.en(""),
95
100
  )
96
101
  else:
97
102
  try:
98
103
  values[k] = EnumValue(
99
104
  name=k,
100
105
  value=v["value"],
106
+ label=_parse_multi_lingual_string(v.get("label", "")),
101
107
  description=_parse_multi_lingual_string(v.get("desc", "")),
102
108
  )
103
109
  except KeyError as e:
@@ -146,7 +152,7 @@ def _parse_property(
146
152
 
147
153
  assert isinstance(prop, dict), prop
148
154
  if "datatype" in prop:
149
- return DataProperty(
155
+ return_prop = DataProperty(
150
156
  name=prop_name,
151
157
  label=_parse_multi_lingual_string(prop.get("label", prop_name)),
152
158
  description=_parse_multi_lingual_string(prop.get("desc", "")),
@@ -160,6 +166,9 @@ def _parse_property(
160
166
  is_indexed=db.get("is_indexed", False)
161
167
  or db.get("is_unique", False)
162
168
  or db.get("is_primary_key", False),
169
+ index_type=(
170
+ IndexType(db["index_type"]) if "index_type" in db else None
171
+ ),
163
172
  foreign_key=schema.classes.get(db.get("foreign_key")),
164
173
  )
165
174
  if "db" in prop
@@ -169,6 +178,10 @@ def _parse_property(
169
178
  default_value=_parse_default_value(prop.get("default_value", None)),
170
179
  default_factory=_parse_default_factory(prop.get("default_factory", None)),
171
180
  )
181
+ if return_prop.db is not None and return_prop.db.is_indexed:
182
+ if return_prop.db.index_type is None:
183
+ return_prop.db.index_type = IndexType.DEFAULT
184
+ return return_prop
172
185
 
173
186
  assert "target" in prop, prop
174
187
  return ObjectProperty(
sera/models/_property.py CHANGED
@@ -16,6 +16,12 @@ if TYPE_CHECKING:
16
16
  from sera.models._class import Class
17
17
 
18
18
 
19
+ class IndexType(str, Enum):
20
+ DEFAULT = "default"
21
+ POSTGRES_FTS_SEVI = "postgres_fts_sevi"
22
+ POSTGRES_TRIGRAM = "postgres_trigram"
23
+
24
+
19
25
  class ForeignKeyOnDelete(str, Enum):
20
26
  CASCADE = "cascade"
21
27
  SET_NULL = "set null"
@@ -150,6 +156,8 @@ class DataPropDBInfo:
150
156
  is_unique: bool = False
151
157
  # whether this property is indexed or not
152
158
  is_indexed: bool = False
159
+ # type of the index if it is indexed --- if is_indexed is True, this must be not None
160
+ index_type: Optional[IndexType] = None
153
161
  # this is used in conjunction with is_primary_key = True for the case of
154
162
  # extending a table with frequently updated properties. The value for the `foreign_key`
155
163
  # will be a target class. The cardinality is one-to-one, on target class deletion,
sera/typing.py CHANGED
@@ -39,4 +39,5 @@ GLOBAL_IDENTS = {
39
39
  "UNSET": "sera.typing.UNSET",
40
40
  "ForeignKey": "sqlalchemy.ForeignKey",
41
41
  "Optional": "typing.Optional",
42
+ "text": "sqlalchemy.text",
42
43
  }
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: sera-2
3
- Version: 1.23.0
3
+ Version: 1.25.0
4
4
  Summary:
5
5
  Author: Binh Vu
6
6
  Author-email: bvu687@gmail.com
@@ -8,8 +8,9 @@ Requires-Python: >=3.12,<4.0
8
8
  Classifier: Programming Language :: Python :: 3
9
9
  Classifier: Programming Language :: Python :: 3.12
10
10
  Classifier: Programming Language :: Python :: 3.13
11
+ Classifier: Programming Language :: Python :: 3.14
11
12
  Requires-Dist: black (==25.1.0)
12
- Requires-Dist: codegen-2 (>=2.14.0,<3.0.0)
13
+ Requires-Dist: codegen-2 (>=2.15.1,<3.0.0)
13
14
  Requires-Dist: graph-wrapper (>=1.7.3,<2.0.0)
14
15
  Requires-Dist: isort (==6.0.1)
15
16
  Requires-Dist: litestar (>=2.15.1,<3.0.0)