sera-2 1.21.2__py3-none-any.whl → 1.24.1__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.
- sera/libs/api_helper.py +1 -100
- sera/libs/api_test_helper.py +1 -1
- sera/libs/base_service.py +168 -80
- sera/libs/search_helper.py +359 -0
- sera/make/make_app.py +4 -5
- sera/make/make_python_api.py +65 -113
- sera/make/make_python_model.py +184 -17
- sera/make/make_python_services.py +3 -2
- sera/make/make_typescript_model.py +17 -337
- sera/make/ts_frontend/__init__.py +0 -0
- sera/make/ts_frontend/make_class_schema.py +369 -0
- sera/make/ts_frontend/make_enums.py +104 -0
- sera/make/ts_frontend/misc.py +38 -0
- sera/misc/__init__.py +2 -0
- sera/misc/_utils.py +17 -2
- sera/models/_class.py +2 -2
- sera/models/_collection.py +15 -11
- sera/models/_constraints.py +1 -1
- sera/models/_datatype.py +8 -29
- sera/models/_enum.py +3 -2
- sera/models/_module.py +7 -0
- sera/models/_parse.py +5 -1
- {sera_2-1.21.2.dist-info → sera_2-1.24.1.dist-info}/METADATA +2 -2
- {sera_2-1.21.2.dist-info → sera_2-1.24.1.dist-info}/RECORD +25 -20
- {sera_2-1.21.2.dist-info → sera_2-1.24.1.dist-info}/WHEEL +0 -0
sera/make/make_python_model.py
CHANGED
@@ -883,6 +883,35 @@ def make_python_data_model(
|
|
883
883
|
),
|
884
884
|
)
|
885
885
|
|
886
|
+
def make_data_schema_export():
|
887
|
+
program = Program()
|
888
|
+
program.import_("__future__.annotations", True)
|
889
|
+
|
890
|
+
expose_vars = [expr.ExprConstant(cls.name) for cls in schema.classes.values()]
|
891
|
+
expose_vars.append(expr.ExprConstant("dataschema"))
|
892
|
+
|
893
|
+
output = []
|
894
|
+
for cls in schema.classes.values():
|
895
|
+
program.import_(
|
896
|
+
f"{target_pkg.path}.{cls.get_pymodule_name()}.{cls.name}",
|
897
|
+
True,
|
898
|
+
)
|
899
|
+
output.append((expr.ExprConstant(cls.name), expr.ExprIdent(cls.name)))
|
900
|
+
|
901
|
+
program.root(
|
902
|
+
stmt.LineBreak(),
|
903
|
+
lambda ast: ast.assign(
|
904
|
+
DeferredVar.simple("dataschema"), PredefinedFn.dict(output)
|
905
|
+
),
|
906
|
+
stmt.LineBreak(),
|
907
|
+
lambda ast: ast.assign(
|
908
|
+
DeferredVar.simple("__all__"),
|
909
|
+
PredefinedFn.list(expose_vars),
|
910
|
+
),
|
911
|
+
)
|
912
|
+
|
913
|
+
target_pkg.parent().module("data_schema").write(program)
|
914
|
+
|
886
915
|
for cls in schema.topological_sort():
|
887
916
|
if cls.name in reference_classes:
|
888
917
|
continue
|
@@ -895,6 +924,8 @@ def make_python_data_model(
|
|
895
924
|
make_normal(program, cls)
|
896
925
|
target_pkg.module(cls.get_pymodule_name()).write(program)
|
897
926
|
|
927
|
+
make_data_schema_export()
|
928
|
+
|
898
929
|
|
899
930
|
def make_python_relational_model(
|
900
931
|
schema: Schema,
|
@@ -1098,6 +1129,66 @@ def make_python_relational_model(
|
|
1098
1129
|
|
1099
1130
|
target_pkg.module("base").write(program)
|
1100
1131
|
|
1132
|
+
def make_db_schema_export():
|
1133
|
+
program = Program()
|
1134
|
+
program.import_("__future__.annotations", True)
|
1135
|
+
|
1136
|
+
expose_vars = [
|
1137
|
+
expr.ExprConstant(cls.name)
|
1138
|
+
for cls in schema.classes.values()
|
1139
|
+
if cls.db is not None
|
1140
|
+
]
|
1141
|
+
expose_vars.append(expr.ExprConstant("dbschema"))
|
1142
|
+
|
1143
|
+
for name in ["engine", "async_engine", "get_session", "get_async_session"]:
|
1144
|
+
program.import_(f"{target_pkg.path}.base.{name}", True)
|
1145
|
+
expose_vars.append(expr.ExprConstant(name))
|
1146
|
+
|
1147
|
+
output = []
|
1148
|
+
for cls in schema.classes.values():
|
1149
|
+
if cls.db is None:
|
1150
|
+
continue
|
1151
|
+
program.import_(
|
1152
|
+
f"{target_pkg.path}.{cls.get_pymodule_name()}.{cls.name}",
|
1153
|
+
True,
|
1154
|
+
)
|
1155
|
+
output.append((expr.ExprConstant(cls.name), expr.ExprIdent(cls.name)))
|
1156
|
+
|
1157
|
+
# if there is a MANY-TO-MANY relationship, we need to add an association table as well
|
1158
|
+
for prop in cls.properties.values():
|
1159
|
+
if (
|
1160
|
+
not isinstance(prop, ObjectProperty)
|
1161
|
+
or prop.target.db is None
|
1162
|
+
or prop.cardinality != Cardinality.MANY_TO_MANY
|
1163
|
+
):
|
1164
|
+
continue
|
1165
|
+
|
1166
|
+
program.import_(
|
1167
|
+
f"{target_pkg.path}.{to_snake_case(cls.name + prop.target.name)}.{cls.name}{prop.target.name}",
|
1168
|
+
True,
|
1169
|
+
)
|
1170
|
+
output.append(
|
1171
|
+
(
|
1172
|
+
expr.ExprConstant(f"{cls.name}{prop.target.name}"),
|
1173
|
+
expr.ExprIdent(f"{cls.name}{prop.target.name}"),
|
1174
|
+
)
|
1175
|
+
)
|
1176
|
+
expose_vars.append(expr.ExprConstant(f"{cls.name}{prop.target.name}"))
|
1177
|
+
|
1178
|
+
program.root(
|
1179
|
+
stmt.LineBreak(),
|
1180
|
+
lambda ast: ast.assign(
|
1181
|
+
DeferredVar.simple("dbschema"), PredefinedFn.dict(output)
|
1182
|
+
),
|
1183
|
+
stmt.LineBreak(),
|
1184
|
+
lambda ast: ast.assign(
|
1185
|
+
DeferredVar.simple("__all__"),
|
1186
|
+
PredefinedFn.list(expose_vars),
|
1187
|
+
),
|
1188
|
+
)
|
1189
|
+
|
1190
|
+
target_pkg.module("__init__").write(program)
|
1191
|
+
|
1101
1192
|
def make_orm(cls: Class):
|
1102
1193
|
if cls.db is None or cls.name in reference_classes:
|
1103
1194
|
# skip classes that are not stored in the database
|
@@ -1242,10 +1333,44 @@ def make_python_relational_model(
|
|
1242
1333
|
expr.ExprIdent("mapped_column"), propvalargs
|
1243
1334
|
)
|
1244
1335
|
cls_ast(stmt.DefClassVarStatement(propname, proptype, propval))
|
1336
|
+
|
1337
|
+
if prop.db.foreign_key is not None:
|
1338
|
+
# add a relationship property for foreign key primary key so that we can do eager join in SQLAlchemy
|
1339
|
+
program.import_("sqlalchemy.orm.relationship", True)
|
1340
|
+
if prop.db.foreign_key.name != cls.name:
|
1341
|
+
ident_manager.python_import_for_hint(
|
1342
|
+
target_pkg.path
|
1343
|
+
+ f".{prop.db.foreign_key.get_pymodule_name()}.{prop.db.foreign_key.name}",
|
1344
|
+
True,
|
1345
|
+
)
|
1346
|
+
cls_ast(
|
1347
|
+
stmt.DefClassVarStatement(
|
1348
|
+
propname + "_relobj",
|
1349
|
+
f"Mapped[{prop.db.foreign_key.name}]",
|
1350
|
+
expr.ExprFuncCall(
|
1351
|
+
expr.ExprIdent("relationship"),
|
1352
|
+
[
|
1353
|
+
PredefinedFn.keyword_assignment(
|
1354
|
+
"lazy",
|
1355
|
+
expr.ExprConstant("raise_on_sql"),
|
1356
|
+
),
|
1357
|
+
PredefinedFn.keyword_assignment(
|
1358
|
+
"foreign_keys",
|
1359
|
+
expr.ExprIdent(propname),
|
1360
|
+
),
|
1361
|
+
PredefinedFn.keyword_assignment(
|
1362
|
+
"init",
|
1363
|
+
expr.ExprConstant(False),
|
1364
|
+
),
|
1365
|
+
],
|
1366
|
+
),
|
1367
|
+
)
|
1368
|
+
)
|
1245
1369
|
else:
|
1246
1370
|
assert isinstance(prop, ObjectProperty)
|
1247
1371
|
make_python_relational_object_property(
|
1248
1372
|
program=program,
|
1373
|
+
ident_manager=ident_manager,
|
1249
1374
|
target_pkg=target_pkg,
|
1250
1375
|
target_data_pkg=target_data_pkg,
|
1251
1376
|
cls_ast=cls_ast,
|
@@ -1267,9 +1392,13 @@ def make_python_relational_model(
|
|
1267
1392
|
)
|
1268
1393
|
make_base(custom_types)
|
1269
1394
|
|
1395
|
+
# export the db classes in the __init__ file
|
1396
|
+
make_db_schema_export()
|
1397
|
+
|
1270
1398
|
|
1271
1399
|
def make_python_relational_object_property(
|
1272
1400
|
program: Program,
|
1401
|
+
ident_manager: ImportHelper,
|
1273
1402
|
target_pkg: Package,
|
1274
1403
|
target_data_pkg: Package,
|
1275
1404
|
cls_ast: AST,
|
@@ -1291,6 +1420,14 @@ def make_python_relational_object_property(
|
|
1291
1420
|
if prop.cardinality.is_star_to_many():
|
1292
1421
|
raise NotImplementedError((cls.name, prop.name))
|
1293
1422
|
|
1423
|
+
program.import_("sqlalchemy.orm.relationship", True)
|
1424
|
+
if prop.target.name != cls.name:
|
1425
|
+
ident_manager.python_import_for_hint(
|
1426
|
+
target_pkg.path
|
1427
|
+
+ f".{prop.target.get_pymodule_name()}.{prop.target.name}",
|
1428
|
+
True,
|
1429
|
+
)
|
1430
|
+
|
1294
1431
|
# we store this class in the database
|
1295
1432
|
propname = f"{prop.name}_id"
|
1296
1433
|
idprop = prop.target.get_id_property()
|
@@ -1329,7 +1466,30 @@ def make_python_relational_object_property(
|
|
1329
1466
|
],
|
1330
1467
|
)
|
1331
1468
|
|
1332
|
-
cls_ast(
|
1469
|
+
cls_ast(
|
1470
|
+
stmt.DefClassVarStatement(propname, proptype, propval),
|
1471
|
+
stmt.DefClassVarStatement(
|
1472
|
+
prop.name,
|
1473
|
+
f"Mapped[{prop.target.name}]",
|
1474
|
+
expr.ExprFuncCall(
|
1475
|
+
expr.ExprIdent("relationship"),
|
1476
|
+
[
|
1477
|
+
PredefinedFn.keyword_assignment(
|
1478
|
+
"lazy",
|
1479
|
+
expr.ExprConstant("raise_on_sql"),
|
1480
|
+
),
|
1481
|
+
PredefinedFn.keyword_assignment(
|
1482
|
+
"foreign_keys",
|
1483
|
+
expr.ExprIdent(propname),
|
1484
|
+
),
|
1485
|
+
PredefinedFn.keyword_assignment(
|
1486
|
+
"init",
|
1487
|
+
expr.ExprConstant(False),
|
1488
|
+
),
|
1489
|
+
],
|
1490
|
+
),
|
1491
|
+
),
|
1492
|
+
)
|
1333
1493
|
return
|
1334
1494
|
|
1335
1495
|
# if the target class is not in the database,
|
@@ -1352,16 +1512,16 @@ def make_python_relational_object_property(
|
|
1352
1512
|
program.import_("sqlalchemy.orm.composite", True)
|
1353
1513
|
propvalargs: list[expr.Expr] = [expr.ExprIdent(prop.target.name)]
|
1354
1514
|
for p in prop.target.properties.values():
|
1515
|
+
pdtype = assert_isinstance(p, DataProperty).datatype.get_sqlalchemy_type()
|
1516
|
+
for dep in pdtype.deps:
|
1517
|
+
program.import_(dep, True)
|
1518
|
+
|
1355
1519
|
propvalargs.append(
|
1356
1520
|
expr.ExprFuncCall(
|
1357
1521
|
expr.ExprIdent("mapped_column"),
|
1358
1522
|
[
|
1359
1523
|
expr.ExprConstant(f"{prop.name}_{p.name}"),
|
1360
|
-
expr.ExprIdent(
|
1361
|
-
assert_isinstance(p, DataProperty)
|
1362
|
-
.datatype.get_sqlalchemy_type()
|
1363
|
-
.type
|
1364
|
-
),
|
1524
|
+
expr.ExprIdent(pdtype.type),
|
1365
1525
|
PredefinedFn.keyword_assignment(
|
1366
1526
|
"nullable",
|
1367
1527
|
expr.ExprConstant(prop.is_optional or p.is_optional),
|
@@ -1391,7 +1551,11 @@ def make_python_relational_object_property(
|
|
1391
1551
|
|
1392
1552
|
|
1393
1553
|
def make_python_relational_object_property_many_to_many(
|
1394
|
-
program: Program,
|
1554
|
+
program: Program,
|
1555
|
+
ast: AST,
|
1556
|
+
target_pkg: Package,
|
1557
|
+
cls: Class,
|
1558
|
+
prop: ObjectProperty,
|
1395
1559
|
):
|
1396
1560
|
assert cls.db is not None
|
1397
1561
|
assert prop.db is not None and prop.target.db is not None
|
@@ -1415,16 +1579,19 @@ def make_python_relational_object_property_many_to_many(
|
|
1415
1579
|
newprogram.import_("sqlalchemy.orm.Mapped", True)
|
1416
1580
|
newprogram.import_("sqlalchemy.orm.relationship", True)
|
1417
1581
|
newprogram.import_(f"{target_pkg.path}.base.Base", True)
|
1418
|
-
|
1419
|
-
|
1420
|
-
|
1421
|
-
|
1422
|
-
|
1423
|
-
|
1424
|
-
|
1425
|
-
|
1426
|
-
|
1427
|
-
|
1582
|
+
|
1583
|
+
ident_manager = ImportHelper(
|
1584
|
+
newprogram,
|
1585
|
+
GLOBAL_IDENTS,
|
1586
|
+
)
|
1587
|
+
|
1588
|
+
ident_manager.python_import_for_hint(
|
1589
|
+
target_pkg.path + f".{cls.get_pymodule_name()}.{cls.name}",
|
1590
|
+
is_import_attr=True,
|
1591
|
+
)
|
1592
|
+
ident_manager.python_import_for_hint(
|
1593
|
+
target_pkg.path + f".{prop.target.get_pymodule_name()}.{prop.target.name}",
|
1594
|
+
is_import_attr=True,
|
1428
1595
|
)
|
1429
1596
|
|
1430
1597
|
newprogram.root(
|
@@ -32,11 +32,12 @@ def make_python_service(collection: DataCollection, target_pkg: Package):
|
|
32
32
|
program = Program()
|
33
33
|
program.import_("__future__.annotations", True)
|
34
34
|
program.import_(
|
35
|
-
app.models.db.path + f".{collection.
|
35
|
+
app.models.db.path + f".{collection.name}",
|
36
36
|
True,
|
37
37
|
)
|
38
38
|
program.import_(app.config.path + f".schema", True)
|
39
39
|
program.import_("sera.libs.base_service.BaseAsyncService", True)
|
40
|
+
program.import_(app.models.db.path + ".dbschema", True)
|
40
41
|
|
41
42
|
program.root(
|
42
43
|
stmt.LineBreak(),
|
@@ -55,7 +56,7 @@ def make_python_service(collection: DataCollection, target_pkg: Package):
|
|
55
56
|
expr.ExprIdent("super().__init__"),
|
56
57
|
[
|
57
58
|
expr.ExprRawPython(f"schema.classes['{cls.name}']"),
|
58
|
-
expr.ExprIdent(
|
59
|
+
expr.ExprIdent("dbschema"),
|
59
60
|
],
|
60
61
|
)
|
61
62
|
),
|