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.
@@ -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, SystemControlledMode
9
+ from sera.models import App, DataCollection, Module, Package
10
10
  from sera.typing import GLOBAL_IDENTS
11
11
 
12
12
 
@@ -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
- pytype_type = f"Annotated[%s, %s]" % (
389
- pytype_type,
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, pytype_type, prop_default_value
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, pytype_type))
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
- pytype_type = f"Annotated[%s, %s]" % (
577
- pytype_type,
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
- pytype_type = f"Union[{pytype_type}, UnsetType]"
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, pytype_type, prop_default_value
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, pytype_type))
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"Mapping[str, {custom_type.target.name}]"
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"Sequence[str, {custom_type.target.name}]"
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
- cls_ast = program.root.class_(
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
- proptype = f"Mapped[{prop.target.name}]"
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
- ), "This class is stored in the database and thus, must have a primary key"
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:
@@ -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
- values[k] = EnumValue(
98
- name=k,
99
- value=v["value"],
100
- description=_parse_multi_lingual_string(v.get("desc", "")),
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
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: sera-2
3
- Version: 1.14.2
3
+ Version: 1.14.4
4
4
  Summary:
5
5
  Author: Binh Vu
6
6
  Author-email: bvu687@gmail.com
@@ -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=9Pr5IxhC5iFkNq8DEh8Wfqa01AGm9YUxf9qdLmMdf_Y,26875
20
- sera/make/make_python_model.py,sha256=Q9lirILQS_J00FfRBE8JZvMZTsfkZ6iziHknwxpU-fo,61420
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=VcC7HvqXuYrkgXwzs2vOH6LJPpzFBkeDvYVNrd3P-6E,855
27
- sera/models/_class.py,sha256=Wf0e8x6-szG9TzoFkAlqj7_dG0SCICMBw_333n3paxk,2514
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=PsSOX94b9-73wZTcUif9KTzV9uTfoF0WN87g4GZXQmU,1827
30
- sera/models/_datatype.py,sha256=y5kfim0G3gLhnGjiokFBr8leU1Y6as_Vw7oK-caOo68,7140
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=OpTnLC8tg_gCzsjMAAqgR_TbYNS7kmPjkHGRNERziBQ,12015
36
- sera/models/_property.py,sha256=ZAxTuQ4BVf1Vsfk4dFkYwH_UsP9mFLTPyUkTcBaTajY,7183
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.2.dist-info/METADATA,sha256=YoTV3zBXfa_8HQ9WU0dOZ2U5fGIIWzu_F70HsJTIqxg,852
40
- sera_2-1.14.2.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
41
- sera_2-1.14.2.dist-info/RECORD,,
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,,