sera-2 1.14.2__py3-none-any.whl → 1.14.4__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/make/make_python_api.py +1 -1
- sera/make/make_python_model.py +34 -38
- sera/models/__init__.py +1 -8
- sera/models/_class.py +1 -1
- sera/models/_constraints.py +4 -0
- sera/models/_datatype.py +4 -1
- sera/models/_parse.py +8 -6
- sera/models/_property.py +0 -13
- {sera_2-1.14.2.dist-info → sera_2-1.14.4.dist-info}/METADATA +1 -1
- {sera_2-1.14.2.dist-info → sera_2-1.14.4.dist-info}/RECORD +11 -11
- {sera_2-1.14.2.dist-info → sera_2-1.14.4.dist-info}/WHEEL +0 -0
sera/make/make_python_api.py
CHANGED
@@ -6,7 +6,7 @@ from codegen.models import DeferredVar, ImportHelper, PredefinedFn, Program, exp
|
|
6
6
|
from loguru import logger
|
7
7
|
|
8
8
|
from sera.misc import assert_not_null, to_snake_case
|
9
|
-
from sera.models import App, DataCollection, Module, Package
|
9
|
+
from sera.models import App, DataCollection, Module, Package
|
10
10
|
from sera.typing import GLOBAL_IDENTS
|
11
11
|
|
12
12
|
|
sera/make/make_python_model.py
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
import sys
|
4
|
-
from ast import Not
|
5
3
|
from typing import Callable, Literal, Optional, Sequence
|
6
4
|
|
7
5
|
from codegen.models import (
|
@@ -29,7 +27,6 @@ from sera.models import (
|
|
29
27
|
PyTypeWithDep,
|
30
28
|
Schema,
|
31
29
|
)
|
32
|
-
from sera.models._property import SystemControlledMode
|
33
30
|
from sera.typing import GLOBAL_IDENTS, ObjectPath
|
34
31
|
|
35
32
|
|
@@ -373,25 +370,24 @@ def make_python_data_model(
|
|
373
370
|
continue
|
374
371
|
|
375
372
|
if isinstance(prop, DataProperty):
|
376
|
-
pytype = prop.get_data_model_datatype().get_python_type()
|
377
|
-
if prop.is_optional:
|
378
|
-
pytype = pytype.as_optional_type()
|
379
|
-
|
380
|
-
for dep in pytype.deps:
|
381
|
-
program.import_(dep, True)
|
382
|
-
|
383
|
-
pytype_type = pytype.type
|
373
|
+
pytype = prop.get_data_model_datatype().get_python_type().clone()
|
384
374
|
if len(prop.data.constraints) > 0:
|
385
375
|
# if the property has constraints, we need to figure out
|
386
376
|
program.import_("typing.Annotated", True)
|
387
377
|
if len(prop.data.constraints) == 1:
|
388
|
-
|
389
|
-
|
378
|
+
pytype.type = "Annotated[%s, %s]" % (
|
379
|
+
pytype.type,
|
390
380
|
prop.data.constraints[0].get_msgspec_constraint(),
|
391
381
|
)
|
392
382
|
else:
|
393
383
|
raise NotImplementedError(prop.data.constraints)
|
394
384
|
|
385
|
+
if prop.is_optional:
|
386
|
+
pytype = pytype.as_optional_type()
|
387
|
+
|
388
|
+
for dep in pytype.deps:
|
389
|
+
program.import_(dep, True)
|
390
|
+
|
395
391
|
# private property are available for creating, but not for updating.
|
396
392
|
# so we do not need to skip it.
|
397
393
|
# if prop.data.is_private:
|
@@ -419,7 +415,7 @@ def make_python_data_model(
|
|
419
415
|
|
420
416
|
cls_ast(
|
421
417
|
stmt.DefClassVarStatement(
|
422
|
-
prop.name,
|
418
|
+
prop.name, pytype.type, prop_default_value
|
423
419
|
)
|
424
420
|
)
|
425
421
|
elif isinstance(prop, ObjectProperty):
|
@@ -443,12 +439,10 @@ def make_python_data_model(
|
|
443
439
|
elif prop.is_optional:
|
444
440
|
pytype = pytype.as_optional_type()
|
445
441
|
|
446
|
-
pytype_type = pytype.type
|
447
|
-
|
448
442
|
for dep in pytype.deps:
|
449
443
|
program.import_(dep, True)
|
450
444
|
|
451
|
-
cls_ast(stmt.DefClassVarStatement(prop.name,
|
445
|
+
cls_ast(stmt.DefClassVarStatement(prop.name, pytype.type))
|
452
446
|
|
453
447
|
if is_on_create_value_updated:
|
454
448
|
program.import_("typing.Optional", True)
|
@@ -561,30 +555,30 @@ def make_python_data_model(
|
|
561
555
|
continue
|
562
556
|
|
563
557
|
if isinstance(prop, DataProperty):
|
564
|
-
pytype = prop.get_data_model_datatype().get_python_type()
|
565
|
-
if prop.is_optional:
|
566
|
-
pytype = pytype.as_optional_type()
|
567
|
-
|
568
|
-
for dep in pytype.deps:
|
569
|
-
program.import_(dep, True)
|
558
|
+
pytype = prop.get_data_model_datatype().get_python_type().clone()
|
570
559
|
|
571
|
-
pytype_type = pytype.type
|
572
560
|
if len(prop.data.constraints) > 0:
|
573
561
|
# if the property has constraints, we need to figure out
|
574
562
|
program.import_("typing.Annotated", True)
|
575
563
|
if len(prop.data.constraints) == 1:
|
576
|
-
|
577
|
-
|
564
|
+
pytype.type = "Annotated[%s, %s]" % (
|
565
|
+
pytype.type,
|
578
566
|
prop.data.constraints[0].get_msgspec_constraint(),
|
579
567
|
)
|
580
568
|
else:
|
581
569
|
raise NotImplementedError(prop.data.constraints)
|
582
570
|
|
571
|
+
if prop.is_optional:
|
572
|
+
pytype = pytype.as_optional_type()
|
573
|
+
|
574
|
+
for dep in pytype.deps:
|
575
|
+
program.import_(dep, True)
|
576
|
+
|
583
577
|
if prop.data.is_private:
|
584
578
|
program.import_("typing.Union", True)
|
585
579
|
program.import_("sera.typing.UnsetType", True)
|
586
580
|
program.import_("sera.typing.UNSET", True)
|
587
|
-
|
581
|
+
pytype.type = f"Union[{pytype.type}, UnsetType]"
|
588
582
|
|
589
583
|
prop_default_value = None
|
590
584
|
if prop.data.is_private:
|
@@ -605,7 +599,7 @@ def make_python_data_model(
|
|
605
599
|
|
606
600
|
cls_ast(
|
607
601
|
stmt.DefClassVarStatement(
|
608
|
-
prop.name,
|
602
|
+
prop.name, pytype.type, prop_default_value
|
609
603
|
)
|
610
604
|
)
|
611
605
|
elif isinstance(prop, ObjectProperty):
|
@@ -629,12 +623,10 @@ def make_python_data_model(
|
|
629
623
|
elif prop.is_optional:
|
630
624
|
pytype = pytype.as_optional_type()
|
631
625
|
|
632
|
-
pytype_type = pytype.type
|
633
|
-
|
634
626
|
for dep in pytype.deps:
|
635
627
|
program.import_(dep, True)
|
636
628
|
|
637
|
-
cls_ast(stmt.DefClassVarStatement(prop.name,
|
629
|
+
cls_ast(stmt.DefClassVarStatement(prop.name, pytype.type))
|
638
630
|
|
639
631
|
if is_on_update_value_updated:
|
640
632
|
program.import_("typing.Optional", True)
|
@@ -937,14 +929,12 @@ def make_python_relational_model(
|
|
937
929
|
|
938
930
|
if custom_type.cardinality.is_star_to_many():
|
939
931
|
if custom_type.is_map:
|
940
|
-
program.import_("typing.Mapping", True)
|
941
932
|
program.import_("sera.libs.base_orm.DictDataclassType", True)
|
942
|
-
type = f"
|
933
|
+
type = f"dict[str, {custom_type.target.name}]"
|
943
934
|
maptype = f"DictDataclassType({custom_type.target.name})"
|
944
935
|
else:
|
945
|
-
program.import_("typing.Sequence", True)
|
946
936
|
program.import_("sera.libs.base_orm.ListDataclassType", True)
|
947
|
-
type = f"
|
937
|
+
type = f"list[{custom_type.target.name}]"
|
948
938
|
maptype = f"ListDataclassType({custom_type.target.name})"
|
949
939
|
else:
|
950
940
|
program.import_("sera.libs.base_orm.DataclassType", True)
|
@@ -957,7 +947,7 @@ def make_python_relational_model(
|
|
957
947
|
|
958
948
|
type_map.append((expr.ExprIdent(type), expr.ExprIdent(maptype)))
|
959
949
|
|
960
|
-
|
950
|
+
program.root.class_(
|
961
951
|
"Base", [expr.ExprIdent("DeclarativeBase"), expr.ExprIdent("BaseORM")]
|
962
952
|
)(
|
963
953
|
stmt.DefClassVarStatement(
|
@@ -1246,7 +1236,7 @@ def make_python_relational_object_property(
|
|
1246
1236
|
return
|
1247
1237
|
|
1248
1238
|
if prop.cardinality.is_star_to_many():
|
1249
|
-
raise NotImplementedError()
|
1239
|
+
raise NotImplementedError((cls.name, prop.name))
|
1250
1240
|
|
1251
1241
|
# we store this class in the database
|
1252
1242
|
propname = f"{prop.name}_id"
|
@@ -1291,7 +1281,13 @@ def make_python_relational_object_property(
|
|
1291
1281
|
is_import_attr=True,
|
1292
1282
|
)
|
1293
1283
|
propname = prop.name
|
1294
|
-
|
1284
|
+
if prop.cardinality.is_star_to_many():
|
1285
|
+
if prop.is_map:
|
1286
|
+
proptype = f"Mapped[dict[str, {prop.target.name}]]"
|
1287
|
+
else:
|
1288
|
+
proptype = f"Mapped[list[{prop.target.name}]]"
|
1289
|
+
else:
|
1290
|
+
proptype = f"Mapped[{prop.target.name}]"
|
1295
1291
|
|
1296
1292
|
# we have two choices, one is to create a composite class, one is to create a custom field
|
1297
1293
|
if prop.db.is_embedded == "composite":
|
sera/models/__init__.py
CHANGED
@@ -5,13 +5,7 @@ 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
|
9
|
-
Cardinality,
|
10
|
-
DataProperty,
|
11
|
-
ObjectProperty,
|
12
|
-
Property,
|
13
|
-
SystemControlledMode,
|
14
|
-
)
|
8
|
+
from sera.models._property import Cardinality, DataProperty, ObjectProperty, Property
|
15
9
|
from sera.models._schema import Schema
|
16
10
|
|
17
11
|
__all__ = [
|
@@ -31,5 +25,4 @@ __all__ = [
|
|
31
25
|
"PyTypeWithDep",
|
32
26
|
"TsTypeWithDep",
|
33
27
|
"Enum",
|
34
|
-
"SystemControlledMode",
|
35
28
|
]
|
sera/models/_class.py
CHANGED
@@ -63,7 +63,7 @@ class Class:
|
|
63
63
|
return prop
|
64
64
|
assert (
|
65
65
|
self.db is None
|
66
|
-
), "
|
66
|
+
), f"The class {self.name} is stored in the database and thus, must have a primary key"
|
67
67
|
return None
|
68
68
|
|
69
69
|
def get_pymodule_name(self) -> str:
|
sera/models/_constraints.py
CHANGED
@@ -4,6 +4,7 @@ from dataclasses import dataclass
|
|
4
4
|
from typing import Literal
|
5
5
|
|
6
6
|
ConstraintName = Literal[
|
7
|
+
"url",
|
7
8
|
"phone_number",
|
8
9
|
"email",
|
9
10
|
"not_empty",
|
@@ -37,6 +38,8 @@ class Constraint:
|
|
37
38
|
return "msgspec.Meta(ge=0)"
|
38
39
|
elif self.name == "positive_number":
|
39
40
|
return "msgspec.Meta(gt=0)"
|
41
|
+
elif self.name == "url":
|
42
|
+
return r"msgspec.Meta(pattern=r'^(https?|ftp)://[^\s/$.?#].[^\s]*$')"
|
40
43
|
|
41
44
|
raise NotImplementedError()
|
42
45
|
|
@@ -57,4 +60,5 @@ predefined_constraints: dict[ConstraintName, Constraint] = {
|
|
57
60
|
"password": Constraint("password", ()),
|
58
61
|
"whole_number": Constraint("whole_number", ()),
|
59
62
|
"positive_number": Constraint("positive_number", ()),
|
63
|
+
"url": Constraint("url", ()),
|
60
64
|
}
|
sera/models/_datatype.py
CHANGED
@@ -2,7 +2,6 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
import datetime
|
4
4
|
from dataclasses import dataclass, field
|
5
|
-
from enum import Enum
|
6
5
|
from typing import Literal
|
7
6
|
|
8
7
|
from codegen.models import expr
|
@@ -64,6 +63,10 @@ class PyTypeWithDep:
|
|
64
63
|
)
|
65
64
|
return PyTypeWithDep(type=f"Optional[{self.type}]", deps=deps)
|
66
65
|
|
66
|
+
def clone(self) -> PyTypeWithDep:
|
67
|
+
"""Clone the type with the same dependencies."""
|
68
|
+
return PyTypeWithDep(type=self.type, deps=list(self.deps))
|
69
|
+
|
67
70
|
|
68
71
|
@dataclass
|
69
72
|
class TsTypeWithDep:
|
sera/models/_parse.py
CHANGED
@@ -34,7 +34,6 @@ from sera.models._property import (
|
|
34
34
|
ObjectProperty,
|
35
35
|
PropDataAttrs,
|
36
36
|
SystemControlledAttrs,
|
37
|
-
SystemControlledMode,
|
38
37
|
)
|
39
38
|
from sera.models._schema import Schema
|
40
39
|
|
@@ -94,11 +93,14 @@ def _parse_enum(schema: Schema, enum_name: str, enum: dict) -> Enum:
|
|
94
93
|
name=k, value=v, description=MultiLingualString.en("")
|
95
94
|
)
|
96
95
|
else:
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
96
|
+
try:
|
97
|
+
values[k] = EnumValue(
|
98
|
+
name=k,
|
99
|
+
value=v["value"],
|
100
|
+
description=_parse_multi_lingual_string(v.get("desc", "")),
|
101
|
+
)
|
102
|
+
except KeyError as e:
|
103
|
+
raise ValueError(f"Invalid enum value definition for {k}: {v}") from e
|
102
104
|
return Enum(name=enum_name, values=values)
|
103
105
|
|
104
106
|
|
sera/models/_property.py
CHANGED
@@ -56,19 +56,6 @@ class Cardinality(str, Enum):
|
|
56
56
|
]
|
57
57
|
|
58
58
|
|
59
|
-
class SystemControlledMode(str, Enum):
|
60
|
-
"""Indicates if this property is controlled by the system.
|
61
|
-
|
62
|
-
There are two modes:
|
63
|
-
1. The system automatically sets the value, and users cannot modify it.
|
64
|
-
2. Users with special roles are allowed to set the value and other users cannot modify it
|
65
|
-
"""
|
66
|
-
|
67
|
-
AUTO = "auto"
|
68
|
-
RESTRICTED = "restricted"
|
69
|
-
NO = "no"
|
70
|
-
|
71
|
-
|
72
59
|
@dataclass(kw_only=True)
|
73
60
|
class GetSCPropValueFunc:
|
74
61
|
|
@@ -16,26 +16,26 @@ sera/libs/middlewares/uscp.py,sha256=H5umW8iEQSCdb_MJ5Im49kxg1E7TpxSg1p2_2A5zI1U
|
|
16
16
|
sera/make/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
17
17
|
sera/make/__main__.py,sha256=bt-gDF8E026OWc2zqr9_a3paMOiDkFd3ybWn8ltL2g0,1448
|
18
18
|
sera/make/make_app.py,sha256=n9NtW73O3s_5Q31VHIRmnd-jEIcpDO7ksAsOdovde2s,5999
|
19
|
-
sera/make/make_python_api.py,sha256=
|
20
|
-
sera/make/make_python_model.py,sha256=
|
19
|
+
sera/make/make_python_api.py,sha256=iXGbKQ3IJvsY1ur_fhurr_THFNnH66E3Wl85o0emUbw,26853
|
20
|
+
sera/make/make_python_model.py,sha256=cRb-fuHX0WH7XPAAliu6lycC0iEjE5kcKg4bBU40GwQ,61275
|
21
21
|
sera/make/make_python_services.py,sha256=0ZpWLwQ7Nwfn8BXAikAB4JRpNknpSJyJgY5b1cjtxV4,2073
|
22
22
|
sera/make/make_typescript_model.py,sha256=ugDdSTw_1ayHLuL--92RQ8hf_D-dpJtnvmUZNxcwcDs,63687
|
23
23
|
sera/misc/__init__.py,sha256=Dh4uDq0D4N53h3zhvmwfa5a0TPVRSUvLzb0hkFuPirk,411
|
24
24
|
sera/misc/_formatter.py,sha256=aCGYL08l8f3aLODHxSocxBBwkRYEo3K1QzCDEn3suj0,1685
|
25
25
|
sera/misc/_utils.py,sha256=V5g4oLGHOhUCR75Kkcn1w01pAvGvaepK-T8Z3pIgHjI,1450
|
26
|
-
sera/models/__init__.py,sha256=
|
27
|
-
sera/models/_class.py,sha256=
|
26
|
+
sera/models/__init__.py,sha256=vJC5Kzo_N7wd16ocNPy1VvAZDGNiWeiAhWJ4ihATKvA,780
|
27
|
+
sera/models/_class.py,sha256=9XSnJli2SGF-jm-v0swsfY6-omiQ5Xeh33R2pM9Kg_g,2526
|
28
28
|
sera/models/_collection.py,sha256=ZnQEriKC4X88Zz48Kn1AVZKH-1_l8OgWa-zf2kcQOOE,1414
|
29
|
-
sera/models/_constraints.py,sha256=
|
30
|
-
sera/models/_datatype.py,sha256=
|
29
|
+
sera/models/_constraints.py,sha256=RpWDU-TfCslXaMUaTG9utWbl5z8Z6nzvF_fhqlek6ew,1987
|
30
|
+
sera/models/_datatype.py,sha256=Lq1iEGqQaNHiG5NlRTcOTWROiUFjoiPoTeSJKFU6lSE,7281
|
31
31
|
sera/models/_default.py,sha256=ABggW6qdPR4ZDqIPJdJ0GCGQ-7kfsfZmQ_DchgZEa-I,137
|
32
32
|
sera/models/_enum.py,sha256=sy0q7E646F-APsqrVQ52r1fAQ_DCAeaNq5YM5QN3zIk,2070
|
33
33
|
sera/models/_module.py,sha256=I-GfnTgAa-5R87qTAvEzOt-VVEGeFBBwubGCgUkXVSw,5159
|
34
34
|
sera/models/_multi_lingual_string.py,sha256=JETN6k00VH4wrA4w5vAHMEJV8fp3SY9bJebskFTjQLA,1186
|
35
|
-
sera/models/_parse.py,sha256=
|
36
|
-
sera/models/_property.py,sha256=
|
35
|
+
sera/models/_parse.py,sha256=ohexBbjFv6A1wZsEtokfw0zMVoR-RGXYrXa_tAub_Vs,12147
|
36
|
+
sera/models/_property.py,sha256=Y98bYIjuCQDcSWpDxERNIFgmxXIaJiwBNFtYeGbDYRE,6812
|
37
37
|
sera/models/_schema.py,sha256=VxJEiqgVvbXgcSUK4UW6JnRcggk4nsooVSE6MyXmfNY,1636
|
38
38
|
sera/typing.py,sha256=Fl4-UzLJu1GdLLk_g87fA7nT9wQGelNnGzag6dg_0gs,980
|
39
|
-
sera_2-1.14.
|
40
|
-
sera_2-1.14.
|
41
|
-
sera_2-1.14.
|
39
|
+
sera_2-1.14.4.dist-info/METADATA,sha256=PTLgXrXqoFQ74mPm4mgMK3DaBGMh0A45FNuUd40tb94,852
|
40
|
+
sera_2-1.14.4.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
41
|
+
sera_2-1.14.4.dist-info/RECORD,,
|
File without changes
|