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.
@@ -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(stmt.DefClassVarStatement(propname, proptype, propval))
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, ast: AST, target_pkg: Package, cls: Class, prop: ObjectProperty
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
- newprogram.import_("typing.TYPE_CHECKING", True)
1419
- newprogram.import_area.if_(expr.ExprIdent("TYPE_CHECKING"))(
1420
- lambda ast00: ast00.import_(
1421
- target_pkg.path + f".{cls.get_pymodule_name()}.{cls.name}",
1422
- is_import_attr=True,
1423
- ),
1424
- lambda ast10: ast10.import_(
1425
- target_pkg.path + f".{prop.target.get_pymodule_name()}.{prop.target.name}",
1426
- is_import_attr=True,
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.get_pymodule_name()}.{collection.name}",
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(cls.name),
59
+ expr.ExprIdent("dbschema"),
59
60
  ],
60
61
  )
61
62
  ),