sera-2 1.14.3__py3-none-any.whl → 1.14.5__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/__main__.py CHANGED
@@ -30,7 +30,7 @@ def cli(
30
30
  "--collection",
31
31
  help="API collections to generate.",
32
32
  ),
33
- ],
33
+ ] = [],
34
34
  language: Annotated[
35
35
  Language,
36
36
  typer.Option("-l", "--language", help="Language of the generated application"),
@@ -370,25 +370,24 @@ def make_python_data_model(
370
370
  continue
371
371
 
372
372
  if isinstance(prop, DataProperty):
373
- pytype = prop.get_data_model_datatype().get_python_type()
374
- if prop.is_optional:
375
- pytype = pytype.as_optional_type()
376
-
377
- for dep in pytype.deps:
378
- program.import_(dep, True)
379
-
380
- pytype_type = pytype.type
373
+ pytype = prop.get_data_model_datatype().get_python_type().clone()
381
374
  if len(prop.data.constraints) > 0:
382
375
  # if the property has constraints, we need to figure out
383
376
  program.import_("typing.Annotated", True)
384
377
  if len(prop.data.constraints) == 1:
385
- pytype_type = f"Annotated[%s, %s]" % (
386
- pytype_type,
378
+ pytype.type = "Annotated[%s, %s]" % (
379
+ pytype.type,
387
380
  prop.data.constraints[0].get_msgspec_constraint(),
388
381
  )
389
382
  else:
390
383
  raise NotImplementedError(prop.data.constraints)
391
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
+
392
391
  # private property are available for creating, but not for updating.
393
392
  # so we do not need to skip it.
394
393
  # if prop.data.is_private:
@@ -416,7 +415,7 @@ def make_python_data_model(
416
415
 
417
416
  cls_ast(
418
417
  stmt.DefClassVarStatement(
419
- prop.name, pytype_type, prop_default_value
418
+ prop.name, pytype.type, prop_default_value
420
419
  )
421
420
  )
422
421
  elif isinstance(prop, ObjectProperty):
@@ -440,12 +439,10 @@ def make_python_data_model(
440
439
  elif prop.is_optional:
441
440
  pytype = pytype.as_optional_type()
442
441
 
443
- pytype_type = pytype.type
444
-
445
442
  for dep in pytype.deps:
446
443
  program.import_(dep, True)
447
444
 
448
- cls_ast(stmt.DefClassVarStatement(prop.name, pytype_type))
445
+ cls_ast(stmt.DefClassVarStatement(prop.name, pytype.type))
449
446
 
450
447
  if is_on_create_value_updated:
451
448
  program.import_("typing.Optional", True)
@@ -558,30 +555,30 @@ def make_python_data_model(
558
555
  continue
559
556
 
560
557
  if isinstance(prop, DataProperty):
561
- pytype = prop.get_data_model_datatype().get_python_type()
562
- if prop.is_optional:
563
- pytype = pytype.as_optional_type()
558
+ pytype = prop.get_data_model_datatype().get_python_type().clone()
564
559
 
565
- for dep in pytype.deps:
566
- program.import_(dep, True)
567
-
568
- pytype_type = pytype.type
569
560
  if len(prop.data.constraints) > 0:
570
561
  # if the property has constraints, we need to figure out
571
562
  program.import_("typing.Annotated", True)
572
563
  if len(prop.data.constraints) == 1:
573
- pytype_type = f"Annotated[%s, %s]" % (
574
- pytype_type,
564
+ pytype.type = "Annotated[%s, %s]" % (
565
+ pytype.type,
575
566
  prop.data.constraints[0].get_msgspec_constraint(),
576
567
  )
577
568
  else:
578
569
  raise NotImplementedError(prop.data.constraints)
579
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
+
580
577
  if prop.data.is_private:
581
578
  program.import_("typing.Union", True)
582
579
  program.import_("sera.typing.UnsetType", True)
583
580
  program.import_("sera.typing.UNSET", True)
584
- pytype_type = f"Union[{pytype_type}, UnsetType]"
581
+ pytype.type = f"Union[{pytype.type}, UnsetType]"
585
582
 
586
583
  prop_default_value = None
587
584
  if prop.data.is_private:
@@ -602,7 +599,7 @@ def make_python_data_model(
602
599
 
603
600
  cls_ast(
604
601
  stmt.DefClassVarStatement(
605
- prop.name, pytype_type, prop_default_value
602
+ prop.name, pytype.type, prop_default_value
606
603
  )
607
604
  )
608
605
  elif isinstance(prop, ObjectProperty):
@@ -626,12 +623,10 @@ def make_python_data_model(
626
623
  elif prop.is_optional:
627
624
  pytype = pytype.as_optional_type()
628
625
 
629
- pytype_type = pytype.type
630
-
631
626
  for dep in pytype.deps:
632
627
  program.import_(dep, True)
633
628
 
634
- cls_ast(stmt.DefClassVarStatement(prop.name, pytype_type))
629
+ cls_ast(stmt.DefClassVarStatement(prop.name, pytype.type))
635
630
 
636
631
  if is_on_update_value_updated:
637
632
  program.import_("typing.Optional", True)
@@ -23,6 +23,7 @@ from sera.models import (
23
23
  Schema,
24
24
  TsTypeWithDep,
25
25
  )
26
+ from sera.typing import is_set
26
27
 
27
28
 
28
29
  def make_typescript_data_model(schema: Schema, target_pkg: Package):
@@ -336,8 +337,8 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
336
337
  f"{cls.name}Id",
337
338
  deps=[f"@.models.{pkg.dir.name}.{cls.name}.{cls.name}Id"],
338
339
  )
339
- else:
340
- # for none id properties, we need to include a type for "invalid" value
340
+ elif tstype.type not in schema.enums:
341
+ # for none id & none enum properties, we need to include a type for "invalid" value
341
342
  tstype = _inject_type_for_invalid_value(tstype)
342
343
 
343
344
  if prop.is_optional:
@@ -355,9 +356,15 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
355
356
  and prop.db.is_auto_increment
356
357
  ):
357
358
  create_propvalue = expr.ExprConstant(-1)
359
+ elif is_set(prop.data.default_value):
360
+ create_propvalue = expr.ExprConstant(prop.data.default_value)
358
361
  else:
359
362
  if tstype.type in idprop_aliases:
360
363
  create_propvalue = idprop_aliases[tstype.type].get_default()
364
+ elif tstype.type in schema.enums:
365
+ create_propvalue = expr.ExprConstant(
366
+ next(iter(schema.enums[tstype.type].values.values())).value
367
+ )
361
368
  else:
362
369
  create_propvalue = tstype.get_default()
363
370
 
@@ -537,7 +544,6 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
537
544
  ],
538
545
  )
539
546
  if prop.cardinality.is_star_to_many():
540
- tstype = tstype.as_list_type()
541
547
  create_propvalue = expr.ExprConstant([])
542
548
  update_propvalue = PredefinedFn.map_list(
543
549
  PredefinedFn.attr_getter(
@@ -578,6 +584,7 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
578
584
  ),
579
585
  )
580
586
  )
587
+
581
588
  if not prop.data.is_private:
582
589
  # private property does not include in the public record
583
590
  to_record_args.append(
@@ -612,11 +619,9 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
612
619
  ),
613
620
  )
614
621
  )
622
+
623
+ tstype = tstype.as_list_type()
615
624
  else:
616
- if prop.is_optional:
617
- # convert type to optional - for list type, we don't need to do this
618
- # as we will use empty list as no value
619
- tstype = tstype.as_optional_type()
620
625
  create_propvalue = expr.ExprMethodCall(
621
626
  expr.ExprIdent(tstype.type),
622
627
  "create",
@@ -729,6 +734,11 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
729
734
  )
730
735
  )
731
736
 
737
+ if prop.is_optional:
738
+ # convert type to optional - for list type, we don't need to do this
739
+ # as we will use empty list as no value
740
+ tstype = tstype.as_optional_type()
741
+
732
742
  for dep in tstype.deps:
733
743
  program.import_(
734
744
  dep,
@@ -1407,16 +1417,13 @@ def make_typescript_enum(schema: Schema, target_pkg: Package):
1407
1417
  stmt.LineBreak(),
1408
1418
  lambda ast: ast.class_like("enum", enum.name)(
1409
1419
  *[
1410
- stmt.DefClassVarStatement(
1420
+ stmt.DefEnumValueStatement(
1411
1421
  name=value.name,
1412
- type=None,
1413
1422
  value=expr.ExprConstant(value.value),
1414
1423
  )
1415
1424
  for value in enum.values.values()
1416
1425
  ]
1417
1426
  ),
1418
- stmt.LineBreak(),
1419
- stmt.TypescriptStatement("export " + enum.name + ";"),
1420
1427
  )
1421
1428
  pkg.module(enum.get_tsmodule_name()).write(program)
1422
1429
 
sera/models/_class.py CHANGED
@@ -51,6 +51,7 @@ class Class:
51
51
  Get the ID property of this class.
52
52
  The ID property is the one tagged with is_primary_key
53
53
  """
54
+ id_props = []
54
55
  for prop in self.properties.values():
55
56
  if (
56
57
  isinstance(prop, DataProperty)
@@ -60,7 +61,14 @@ class Class:
60
61
  assert (
61
62
  self.db is not None
62
63
  ), "This class is not stored in the database and thus, cannot have a primary key"
63
- return prop
64
+ id_props.append(prop)
65
+ if len(id_props) > 1:
66
+ raise ValueError(
67
+ f"Class {self.name} has more than one primary key property: {', '.join(p.name for p in id_props)}"
68
+ )
69
+ if len(id_props) == 1:
70
+ return id_props[0]
71
+ # if there is no primary key, we return None
64
72
  assert (
65
73
  self.db is None
66
74
  ), f"The class {self.name} is stored in the database and thus, must have a primary key"
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:
@@ -79,7 +82,7 @@ class TsTypeWithDep:
79
82
  return expr.ExprConstant(0)
80
83
  if self.type == "boolean":
81
84
  return expr.ExprConstant(False)
82
- if self.type == "string | undefined":
85
+ if self.type.endswith("| undefined"):
83
86
  return expr.ExprConstant("undefined")
84
87
  if self.type.endswith("| string)") or self.type.endswith("| string"):
85
88
  return expr.ExprConstant("")
sera/models/_parse.py CHANGED
@@ -36,6 +36,7 @@ from sera.models._property import (
36
36
  SystemControlledAttrs,
37
37
  )
38
38
  from sera.models._schema import Schema
39
+ from sera.typing import UNSET
39
40
 
40
41
 
41
42
  def parse_schema(name: str, files: Sequence[Path | str]) -> Schema:
@@ -140,6 +141,7 @@ def _parse_property(
140
141
  system_controlled=_parse_system_controlled_attrs(
141
142
  _data.get("system_controlled")
142
143
  ),
144
+ default_value=UNSET if "default_value" not in _data else _data["default_value"],
143
145
  )
144
146
 
145
147
  assert isinstance(prop, dict), prop
@@ -237,7 +239,9 @@ def _parse_datatype(schema: Schema, datatype: dict | str) -> DataType:
237
239
  sqltype=SQLTypeWithDep(
238
240
  type="String", mapped_pytype="str", deps=["sqlalchemy.String"]
239
241
  ),
240
- tstype=TsTypeWithDep(type=enum.name, deps=["@/models/enums"]),
242
+ tstype=TsTypeWithDep(
243
+ type=enum.name, deps=[f"@.models.enums.{enum.name}"]
244
+ ),
241
245
  is_list=is_list,
242
246
  )
243
247
 
sera/models/_property.py CHANGED
@@ -2,12 +2,15 @@ from __future__ import annotations
2
2
 
3
3
  from dataclasses import dataclass, field
4
4
  from enum import Enum
5
- from typing import TYPE_CHECKING, Literal, Optional
5
+ from typing import TYPE_CHECKING, Any, Literal, Optional
6
+
7
+ from msgspec import UNSET
6
8
 
7
9
  from sera.models._constraints import Constraint
8
10
  from sera.models._datatype import DataType
9
11
  from sera.models._default import DefaultFactory
10
12
  from sera.models._multi_lingual_string import MultiLingualString
13
+ from sera.typing import UnsetType
11
14
 
12
15
  if TYPE_CHECKING:
13
16
  from sera.models._class import Class
@@ -111,6 +114,9 @@ class PropDataAttrs:
111
114
  # if this property is controlled by the system, the attributes for the system-controlled property
112
115
  system_controlled: Optional[SystemControlledAttrs] = None
113
116
 
117
+ # default value of this property if it is not provided (then optional is false by default)
118
+ default_value: Any | UnsetType = UNSET
119
+
114
120
 
115
121
  @dataclass(kw_only=True)
116
122
  class Property:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: sera-2
3
- Version: 1.14.3
3
+ Version: 1.14.5
4
4
  Summary:
5
5
  Author: Binh Vu
6
6
  Author-email: bvu687@gmail.com
@@ -14,28 +14,28 @@ sera/libs/middlewares/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3h
14
14
  sera/libs/middlewares/auth.py,sha256=r6aix1ZBwxMd1Jv5hMCTB8a_gFOJQ6egvxIrf3DWEOs,2323
15
15
  sera/libs/middlewares/uscp.py,sha256=H5umW8iEQSCdb_MJ5Im49kxg1E7TpxSg1p2_2A5zI1U,2600
16
16
  sera/make/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
- sera/make/__main__.py,sha256=bt-gDF8E026OWc2zqr9_a3paMOiDkFd3ybWn8ltL2g0,1448
17
+ sera/make/__main__.py,sha256=HRfOR53p351h6KblVvYm3DLhDIfEtk6R0kjl78_S_S8,1453
18
18
  sera/make/make_app.py,sha256=n9NtW73O3s_5Q31VHIRmnd-jEIcpDO7ksAsOdovde2s,5999
19
19
  sera/make/make_python_api.py,sha256=iXGbKQ3IJvsY1ur_fhurr_THFNnH66E3Wl85o0emUbw,26853
20
- sera/make/make_python_model.py,sha256=pv8gkWE4kIexjTfmtc5GG4Pg2kR9yA3Urnqal1RJ6WI,61430
20
+ sera/make/make_python_model.py,sha256=cRb-fuHX0WH7XPAAliu6lycC0iEjE5kcKg4bBU40GwQ,61275
21
21
  sera/make/make_python_services.py,sha256=0ZpWLwQ7Nwfn8BXAikAB4JRpNknpSJyJgY5b1cjtxV4,2073
22
- sera/make/make_typescript_model.py,sha256=ugDdSTw_1ayHLuL--92RQ8hf_D-dpJtnvmUZNxcwcDs,63687
22
+ sera/make/make_typescript_model.py,sha256=4yLCLIzP2jeXfmOeee6nNS_qd7_7iPinV2vQh1IfbLs,64000
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
26
  sera/models/__init__.py,sha256=vJC5Kzo_N7wd16ocNPy1VvAZDGNiWeiAhWJ4ihATKvA,780
27
- sera/models/_class.py,sha256=9XSnJli2SGF-jm-v0swsfY6-omiQ5Xeh33R2pM9Kg_g,2526
27
+ sera/models/_class.py,sha256=1J4Bd_LanzhhDWwZFHWGtFYD7lupe_alaB3D02ebNDI,2862
28
28
  sera/models/_collection.py,sha256=ZnQEriKC4X88Zz48Kn1AVZKH-1_l8OgWa-zf2kcQOOE,1414
29
29
  sera/models/_constraints.py,sha256=RpWDU-TfCslXaMUaTG9utWbl5z8Z6nzvF_fhqlek6ew,1987
30
- sera/models/_datatype.py,sha256=y5kfim0G3gLhnGjiokFBr8leU1Y6as_Vw7oK-caOo68,7140
30
+ sera/models/_datatype.py,sha256=cC6Wm0IvobDF5Tq9Jy_ncbjPWBRl3ECGBy0wJyDDMzU,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=ohexBbjFv6A1wZsEtokfw0zMVoR-RGXYrXa_tAub_Vs,12147
36
- sera/models/_property.py,sha256=Y98bYIjuCQDcSWpDxERNIFgmxXIaJiwBNFtYeGbDYRE,6812
35
+ sera/models/_parse.py,sha256=MaGZty29lsVUFumWqNanIjAwptBNOMbVZMXLZ2A9_2g,12317
36
+ sera/models/_property.py,sha256=2rSLs9JjSesrxrEugE7krYaBQOivKU882I8mZN94FlI,7017
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.3.dist-info/METADATA,sha256=-PIatY73pPVkjplx7zJAXHmtRcyQFIMEMIu9Manov6M,852
40
- sera_2-1.14.3.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
41
- sera_2-1.14.3.dist-info/RECORD,,
39
+ sera_2-1.14.5.dist-info/METADATA,sha256=vwWtB6bjalDopfemBjaZj3CI2D4S9seb-diDw98smxY,852
40
+ sera_2-1.14.5.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
41
+ sera_2-1.14.5.dist-info/RECORD,,