sera-2 1.6.0__py3-none-any.whl → 1.6.2__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/base_orm.py +9 -1
- sera/libs/base_service.py +1 -1
- sera/make/__main__.py +2 -1
- sera/make/make_app.py +11 -3
- sera/make/make_python_model.py +30 -5
- sera/make/make_typescript_model.py +87 -11
- sera/misc/__init__.py +4 -0
- sera/misc/_formatter.py +63 -0
- sera/models/__init__.py +3 -2
- sera/models/_enum.py +5 -1
- sera/models/_module.py +10 -15
- sera/typing.py +19 -1
- {sera_2-1.6.0.dist-info → sera_2-1.6.2.dist-info}/METADATA +2 -2
- {sera_2-1.6.0.dist-info → sera_2-1.6.2.dist-info}/RECORD +15 -14
- {sera_2-1.6.0.dist-info → sera_2-1.6.2.dist-info}/WHEEL +0 -0
sera/libs/base_orm.py
CHANGED
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|
3
3
|
from typing import Optional
|
4
4
|
|
5
5
|
import orjson
|
6
|
+
from sera.typing import UNSET
|
6
7
|
from sqlalchemy import LargeBinary, TypeDecorator
|
7
8
|
from sqlalchemy import create_engine as sqlalchemy_create_engine
|
8
9
|
from sqlalchemy import update
|
@@ -15,6 +16,8 @@ class BaseORM:
|
|
15
16
|
args = {}
|
16
17
|
for col in self.__table__.columns: # type: ignore
|
17
18
|
val = getattr(self, col.name)
|
19
|
+
if val is UNSET:
|
20
|
+
continue
|
18
21
|
if col.primary_key:
|
19
22
|
q = q.where(getattr(self.__class__, col.name) == val)
|
20
23
|
args[col.name] = val
|
@@ -22,7 +25,12 @@ class BaseORM:
|
|
22
25
|
return q.values(**args)
|
23
26
|
|
24
27
|
def get_update_args(self):
|
25
|
-
|
28
|
+
table = self.__table__ # type: ignore
|
29
|
+
return {
|
30
|
+
col.name: val
|
31
|
+
for col in table.columns
|
32
|
+
if (val := getattr(self, col.name)) is not UNSET
|
33
|
+
}
|
26
34
|
|
27
35
|
@classmethod
|
28
36
|
def from_dict(cls, data: dict):
|
sera/libs/base_service.py
CHANGED
sera/make/__main__.py
CHANGED
@@ -4,8 +4,9 @@ from pathlib import Path
|
|
4
4
|
from typing import Annotated
|
5
5
|
|
6
6
|
import typer
|
7
|
+
|
7
8
|
from sera.make.make_app import make_app
|
8
|
-
from sera.
|
9
|
+
from sera.typing import Language
|
9
10
|
|
10
11
|
app = typer.Typer(pretty_exceptions_short=True, pretty_exceptions_enable=False)
|
11
12
|
|
sera/make/make_app.py
CHANGED
@@ -14,8 +14,13 @@ from sera.make.make_python_model import (
|
|
14
14
|
make_python_relational_model,
|
15
15
|
)
|
16
16
|
from sera.make.make_python_services import make_python_service_structure
|
17
|
-
from sera.make.make_typescript_model import
|
18
|
-
|
17
|
+
from sera.make.make_typescript_model import (
|
18
|
+
make_typescript_data_model,
|
19
|
+
make_typescript_enum,
|
20
|
+
)
|
21
|
+
from sera.misc import Formatter
|
22
|
+
from sera.models import App, DataCollection, parse_schema
|
23
|
+
from sera.typing import Language
|
19
24
|
|
20
25
|
|
21
26
|
def make_config(app: App):
|
@@ -77,6 +82,7 @@ def make_config(app: App):
|
|
77
82
|
expr.ExprFuncCall(
|
78
83
|
expr.ExprIdent("parse_schema"),
|
79
84
|
[
|
85
|
+
expr.ExprConstant(app.name),
|
80
86
|
PredefinedFn.list(
|
81
87
|
[
|
82
88
|
expr.ExprDivision(
|
@@ -87,7 +93,7 @@ def make_config(app: App):
|
|
87
93
|
)
|
88
94
|
for path in app.schema_files
|
89
95
|
]
|
90
|
-
)
|
96
|
+
),
|
91
97
|
],
|
92
98
|
),
|
93
99
|
),
|
@@ -164,8 +170,10 @@ def make_app(
|
|
164
170
|
# generate services
|
165
171
|
make_python_service_structure(app, collections)
|
166
172
|
elif language == Language.Typescript:
|
173
|
+
make_typescript_enum(schema, app.models)
|
167
174
|
make_typescript_data_model(schema, app.models)
|
168
175
|
|
176
|
+
Formatter.get_instance().process()
|
169
177
|
return app
|
170
178
|
|
171
179
|
|
sera/make/make_python_model.py
CHANGED
@@ -159,11 +159,24 @@ def make_python_data_model(
|
|
159
159
|
else:
|
160
160
|
value = expr.ExprMethodCall(value, "to_db", [])
|
161
161
|
elif isinstance(prop, DataProperty) and prop.is_diff_data_model_datatype():
|
162
|
-
# convert the value to the python type used in db
|
163
|
-
|
162
|
+
# convert the value to the python type used in db
|
163
|
+
converted_value = get_data_conversion(
|
164
164
|
prop.get_data_model_datatype().get_python_type().type,
|
165
165
|
prop.datatype.get_python_type().type,
|
166
166
|
)(value)
|
167
|
+
|
168
|
+
if prop.data.is_private:
|
169
|
+
# if the property is private and it's UNSET, we cannot transform it to the database type
|
170
|
+
# and has to use the UNSET value (the update query will ignore this field)
|
171
|
+
program.import_("sera.typing.UNSET", True)
|
172
|
+
program.import_("sera.typing.is_set", True)
|
173
|
+
value = expr.ExprTernary(
|
174
|
+
expr.ExprFuncCall(expr.ExprIdent("is_set"), [value]),
|
175
|
+
converted_value,
|
176
|
+
expr.ExprIdent("UNSET"),
|
177
|
+
)
|
178
|
+
else:
|
179
|
+
value = converted_value
|
167
180
|
return value
|
168
181
|
|
169
182
|
def make_upsert(program: Program, cls: Class):
|
@@ -177,11 +190,13 @@ def make_python_data_model(
|
|
177
190
|
alias=f"{cls.name}DB",
|
178
191
|
)
|
179
192
|
cls_ast = program.root.class_(
|
180
|
-
"Upsert" + cls.name,
|
193
|
+
"Upsert" + cls.name,
|
194
|
+
[expr.ExprIdent("msgspec.Struct"), expr.ExprIdent("kw_only=True")],
|
181
195
|
)
|
182
196
|
for prop in cls.properties.values():
|
183
197
|
# this is a create object, so users can create private field
|
184
|
-
# hence, we do not check for prop.is_private
|
198
|
+
# hence, we do not check for prop.is_private -- however, this field can be omitted
|
199
|
+
# during update, so we need to mark it as optional
|
185
200
|
# if prop.data.is_private:
|
186
201
|
# continue
|
187
202
|
|
@@ -189,7 +204,14 @@ def make_python_data_model(
|
|
189
204
|
pytype = prop.get_data_model_datatype().get_python_type()
|
190
205
|
if pytype.dep is not None:
|
191
206
|
program.import_(pytype.dep, True)
|
207
|
+
|
192
208
|
pytype_type = pytype.type
|
209
|
+
if prop.data.is_private:
|
210
|
+
program.import_("typing.Union", True)
|
211
|
+
program.import_("sera.typing.UnsetType", True)
|
212
|
+
program.import_("sera.typing.UNSET", True)
|
213
|
+
pytype_type = f"Union[{pytype_type}, UnsetType]"
|
214
|
+
|
193
215
|
if len(prop.data.constraints) > 0:
|
194
216
|
# if the property has constraints, we need to figure out
|
195
217
|
program.import_("typing.Annotated", True)
|
@@ -202,7 +224,9 @@ def make_python_data_model(
|
|
202
224
|
raise NotImplementedError(prop.data.constraints)
|
203
225
|
|
204
226
|
prop_default_value = None
|
205
|
-
if prop.
|
227
|
+
if prop.data.is_private:
|
228
|
+
prop_default_value = expr.ExprIdent("UNSET")
|
229
|
+
elif prop.default_value is not None:
|
206
230
|
prop_default_value = expr.ExprConstant(prop.default_value)
|
207
231
|
elif prop.default_factory is not None:
|
208
232
|
program.import_(prop.default_factory.pyfunc, True)
|
@@ -215,6 +239,7 @@ def make_python_data_model(
|
|
215
239
|
)
|
216
240
|
],
|
217
241
|
)
|
242
|
+
|
218
243
|
cls_ast(
|
219
244
|
stmt.DefClassVarStatement(
|
220
245
|
prop.name, pytype_type, prop_default_value
|
@@ -5,6 +5,7 @@ from typing import Any, Callable
|
|
5
5
|
from codegen.models import AST, PredefinedFn, Program, expr, stmt
|
6
6
|
from codegen.models.var import DeferredVar
|
7
7
|
from loguru import logger
|
8
|
+
|
8
9
|
from sera.misc import (
|
9
10
|
assert_isinstance,
|
10
11
|
assert_not_null,
|
@@ -15,6 +16,7 @@ from sera.misc import (
|
|
15
16
|
from sera.models import (
|
16
17
|
Class,
|
17
18
|
DataProperty,
|
19
|
+
Enum,
|
18
20
|
ObjectProperty,
|
19
21
|
Package,
|
20
22
|
Schema,
|
@@ -26,6 +28,15 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
|
|
26
28
|
"""Generate TypeScript data model from the schema. The data model aligns with the public data model in Python, not the database model."""
|
27
29
|
app = target_pkg.app
|
28
30
|
|
31
|
+
# mapping from type alias of idprop to its real type
|
32
|
+
idprop_aliases = {}
|
33
|
+
for cls in schema.classes.values():
|
34
|
+
idprop = cls.get_id_property()
|
35
|
+
if idprop is not None:
|
36
|
+
idprop_aliases[f"{cls.name}Id"] = (
|
37
|
+
idprop.get_data_model_datatype().get_typescript_type()
|
38
|
+
)
|
39
|
+
|
29
40
|
def clone_prop(prop: DataProperty | ObjectProperty, value: expr.Expr):
|
30
41
|
# detect all complex types is hard, we can assume that any update to this does not mutate
|
31
42
|
# the original object, then it's okay.
|
@@ -38,7 +49,6 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
|
|
38
49
|
return
|
39
50
|
|
40
51
|
idprop = cls.get_id_property()
|
41
|
-
|
42
52
|
program = Program()
|
43
53
|
|
44
54
|
prop_defs = []
|
@@ -161,7 +171,8 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
|
|
161
171
|
else None
|
162
172
|
),
|
163
173
|
stmt.LineBreak(),
|
164
|
-
lambda ast00: ast00.
|
174
|
+
lambda ast00: ast00.class_like(
|
175
|
+
"interface",
|
165
176
|
cls.name + "ConstructorArgs",
|
166
177
|
)(*prop_defs),
|
167
178
|
stmt.LineBreak(),
|
@@ -230,9 +241,9 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
|
|
230
241
|
update_field_funcs: list[Callable[[AST], Any]] = []
|
231
242
|
|
232
243
|
for prop in cls.properties.values():
|
233
|
-
if prop.data.is_private:
|
234
|
-
|
235
|
-
|
244
|
+
# if prop.data.is_private:
|
245
|
+
# # skip private fields as this is for APIs exchange
|
246
|
+
# continue
|
236
247
|
|
237
248
|
propname = to_camel_case(prop.name)
|
238
249
|
|
@@ -291,15 +302,23 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
|
|
291
302
|
):
|
292
303
|
create_propvalue = expr.ExprConstant(-1)
|
293
304
|
else:
|
294
|
-
|
305
|
+
if tstype.type in idprop_aliases:
|
306
|
+
create_propvalue = idprop_aliases[tstype.type].get_default()
|
307
|
+
else:
|
308
|
+
create_propvalue = tstype.get_default()
|
295
309
|
|
296
310
|
if prop.db is not None and prop.db.is_primary_key:
|
297
311
|
# for checking if the primary key is from the database or default (create_propvalue)
|
298
312
|
cls_pk = (expr.ExprIdent(propname), create_propvalue)
|
299
313
|
|
300
|
-
|
301
|
-
|
302
|
-
|
314
|
+
# if this field is private, we cannot get it from the normal record
|
315
|
+
# we have to create a default value for it.
|
316
|
+
if prop.data.is_private:
|
317
|
+
update_propvalue = create_propvalue
|
318
|
+
else:
|
319
|
+
update_propvalue = PredefinedFn.attr_getter(
|
320
|
+
expr.ExprIdent("record"), expr.ExprIdent(propname)
|
321
|
+
)
|
303
322
|
|
304
323
|
ser_args.append(
|
305
324
|
(
|
@@ -333,7 +352,22 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
|
|
333
352
|
)
|
334
353
|
if prop.cardinality.is_star_to_many():
|
335
354
|
tstype = tstype.as_list_type()
|
336
|
-
|
355
|
+
create_propvalue = expr.ExprConstant([])
|
356
|
+
else:
|
357
|
+
# if target class has an auto-increment primary key, we set a different default value
|
358
|
+
# to be -1 to avoid start from 0
|
359
|
+
target_idprop = prop.target.get_id_property()
|
360
|
+
if (
|
361
|
+
target_idprop is not None
|
362
|
+
and target_idprop.db is not None
|
363
|
+
and target_idprop.db.is_primary_key
|
364
|
+
and target_idprop.db.is_auto_increment
|
365
|
+
):
|
366
|
+
create_propvalue = expr.ExprConstant(-1)
|
367
|
+
else:
|
368
|
+
assert tstype.type in idprop_aliases
|
369
|
+
create_propvalue = idprop_aliases[tstype.type].get_default()
|
370
|
+
|
337
371
|
update_propvalue = PredefinedFn.attr_getter(
|
338
372
|
expr.ExprIdent("record"), expr.ExprIdent(propname)
|
339
373
|
)
|
@@ -474,7 +508,8 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
|
|
474
508
|
observable_args.sort(key=lambda x: {"observable": 0, "action": 1}[x[1].ident])
|
475
509
|
|
476
510
|
program.root(
|
477
|
-
lambda ast00: ast00.
|
511
|
+
lambda ast00: ast00.class_like(
|
512
|
+
"interface",
|
478
513
|
draft_clsname + "ConstructorArgs",
|
479
514
|
)(*prop_defs),
|
480
515
|
stmt.LineBreak(),
|
@@ -861,3 +896,44 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
|
|
861
896
|
make_definition(cls, pkg)
|
862
897
|
|
863
898
|
make_index(pkg)
|
899
|
+
|
900
|
+
|
901
|
+
def make_typescript_enum(schema: Schema, target_pkg: Package):
|
902
|
+
"""Make typescript enum for the schema"""
|
903
|
+
enum_pkg = target_pkg.pkg("enums")
|
904
|
+
|
905
|
+
def make_enum(enum: Enum, pkg: Package):
|
906
|
+
program = Program()
|
907
|
+
program.root(
|
908
|
+
stmt.LineBreak(),
|
909
|
+
lambda ast: ast.class_like("enum", enum.name)(
|
910
|
+
*[
|
911
|
+
stmt.DefClassVarStatement(
|
912
|
+
name=value.name,
|
913
|
+
type=None,
|
914
|
+
value=expr.ExprConstant(value.value),
|
915
|
+
)
|
916
|
+
for value in enum.values.values()
|
917
|
+
]
|
918
|
+
),
|
919
|
+
stmt.LineBreak(),
|
920
|
+
stmt.TypescriptStatement("export " + enum.name + ";"),
|
921
|
+
)
|
922
|
+
pkg.module(enum.get_tsmodule_name()).write(program)
|
923
|
+
|
924
|
+
for enum in schema.enums.values():
|
925
|
+
make_enum(enum, enum_pkg)
|
926
|
+
|
927
|
+
program = Program()
|
928
|
+
for enum in schema.enums.values():
|
929
|
+
program.import_(f"@.models.enums.{enum.get_tsmodule_name()}.{enum.name}", True)
|
930
|
+
|
931
|
+
program.root(
|
932
|
+
stmt.LineBreak(),
|
933
|
+
stmt.TypescriptStatement(
|
934
|
+
"export { "
|
935
|
+
+ ", ".join([enum.name for enum in schema.enums.values()])
|
936
|
+
+ "};"
|
937
|
+
),
|
938
|
+
)
|
939
|
+
enum_pkg.module("index").write(program)
|
sera/misc/__init__.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
from sera.misc._formatter import File, Formatter
|
1
2
|
from sera.misc._utils import (
|
2
3
|
assert_isinstance,
|
3
4
|
assert_not_null,
|
@@ -14,4 +15,7 @@ __all__ = [
|
|
14
15
|
"assert_not_null",
|
15
16
|
"to_snake_case",
|
16
17
|
"to_camel_case",
|
18
|
+
"to_pascal_case",
|
19
|
+
"Formatter",
|
20
|
+
"File",
|
17
21
|
]
|
sera/misc/_formatter.py
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import subprocess
|
4
|
+
from concurrent.futures import ThreadPoolExecutor
|
5
|
+
from dataclasses import dataclass
|
6
|
+
from pathlib import Path
|
7
|
+
|
8
|
+
from sera.typing import Language
|
9
|
+
from tqdm import tqdm
|
10
|
+
|
11
|
+
|
12
|
+
@dataclass
|
13
|
+
class File:
|
14
|
+
path: Path
|
15
|
+
language: Language
|
16
|
+
|
17
|
+
|
18
|
+
class Formatter:
|
19
|
+
instance = None
|
20
|
+
|
21
|
+
def __init__(self):
|
22
|
+
self.pending_files: list[File] = []
|
23
|
+
|
24
|
+
@staticmethod
|
25
|
+
def get_instance():
|
26
|
+
if Formatter.instance is None:
|
27
|
+
Formatter.instance = Formatter()
|
28
|
+
return Formatter.instance
|
29
|
+
|
30
|
+
def register(self, file: File):
|
31
|
+
self.pending_files.append(file)
|
32
|
+
|
33
|
+
def process(self):
|
34
|
+
"""Format pending files in parallel"""
|
35
|
+
|
36
|
+
def format_file(file: File):
|
37
|
+
if file.language == Language.Typescript:
|
38
|
+
try:
|
39
|
+
subprocess.check_output(
|
40
|
+
["npx", "prettier", "--write", str(file.path.absolute())],
|
41
|
+
cwd=file.path.parent,
|
42
|
+
)
|
43
|
+
except subprocess.CalledProcessError as e:
|
44
|
+
print(f"Error formatting {file.path}: {e}")
|
45
|
+
raise
|
46
|
+
else:
|
47
|
+
raise NotImplementedError(
|
48
|
+
f"Formatting not implemented for {file.language}"
|
49
|
+
)
|
50
|
+
|
51
|
+
if len(self.pending_files) == 0:
|
52
|
+
return
|
53
|
+
|
54
|
+
with ThreadPoolExecutor() as executor:
|
55
|
+
list(
|
56
|
+
tqdm(
|
57
|
+
executor.map(format_file, self.pending_files),
|
58
|
+
total=len(self.pending_files),
|
59
|
+
desc="Formatting files",
|
60
|
+
)
|
61
|
+
)
|
62
|
+
|
63
|
+
self.pending_files.clear()
|
sera/models/__init__.py
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
from sera.models._class import Class
|
2
2
|
from sera.models._collection import DataCollection
|
3
3
|
from sera.models._datatype import DataType, PyTypeWithDep, TsTypeWithDep
|
4
|
-
from sera.models.
|
4
|
+
from sera.models._enum import Enum
|
5
|
+
from sera.models._module import App, Module, Package
|
5
6
|
from sera.models._multi_lingual_string import MultiLingualString
|
6
7
|
from sera.models._parse import parse_schema
|
7
8
|
from sera.models._property import Cardinality, DataProperty, ObjectProperty, Property
|
@@ -21,7 +22,7 @@ __all__ = [
|
|
21
22
|
"DataCollection",
|
22
23
|
"Module",
|
23
24
|
"App",
|
24
|
-
"Language",
|
25
25
|
"PyTypeWithDep",
|
26
26
|
"TsTypeWithDep",
|
27
|
+
"Enum",
|
27
28
|
]
|
sera/models/_enum.py
CHANGED
@@ -39,9 +39,13 @@ class Enum:
|
|
39
39
|
)
|
40
40
|
|
41
41
|
def get_pymodule_name(self) -> str:
|
42
|
-
"""Get the python module name of this
|
42
|
+
"""Get the python module name of this enum as if there is a python module created to store this enum only."""
|
43
43
|
return to_snake_case(self.name)
|
44
44
|
|
45
|
+
def get_tsmodule_name(self) -> str:
|
46
|
+
"""Get the typescript module name of this enum as if there is a typescript module created to store this enum only."""
|
47
|
+
return self.name[0].lower() + self.name[1:]
|
48
|
+
|
45
49
|
def is_str_enum(self) -> bool:
|
46
50
|
"""Check if this enum is a string enum."""
|
47
51
|
return all(isinstance(value.value, str) for value in self.values.values())
|
sera/models/_module.py
CHANGED
@@ -2,7 +2,6 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
import subprocess
|
4
4
|
from dataclasses import dataclass
|
5
|
-
from enum import Enum
|
6
5
|
from pathlib import Path
|
7
6
|
from typing import Sequence
|
8
7
|
|
@@ -11,11 +10,8 @@ import black.mode
|
|
11
10
|
import isort
|
12
11
|
from codegen.models import Program
|
13
12
|
from loguru import logger
|
14
|
-
|
15
|
-
|
16
|
-
class Language(str, Enum):
|
17
|
-
Python = "python"
|
18
|
-
Typescript = "typescript"
|
13
|
+
from sera.misc import File, Formatter
|
14
|
+
from sera.typing import Language
|
19
15
|
|
20
16
|
|
21
17
|
@dataclass
|
@@ -106,15 +102,9 @@ class Module:
|
|
106
102
|
outfile.write_text(copyright_statement + code)
|
107
103
|
|
108
104
|
if self.language == Language.Typescript:
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
["npx", "prettier", "--write", str(outfile.absolute())],
|
113
|
-
cwd=self.package.app.root.dir,
|
114
|
-
)
|
115
|
-
except Exception as e:
|
116
|
-
logger.error("Error formatting Typescript file: {}", e)
|
117
|
-
raise
|
105
|
+
Formatter.get_instance().register(
|
106
|
+
File(path=outfile, language=self.language)
|
107
|
+
)
|
118
108
|
|
119
109
|
def exists(self) -> bool:
|
120
110
|
"""Check if the module exists"""
|
@@ -178,3 +168,8 @@ class App:
|
|
178
168
|
self.schema_files = schema_files
|
179
169
|
|
180
170
|
self.language = language
|
171
|
+
|
172
|
+
@property
|
173
|
+
def name(self) -> str:
|
174
|
+
"""Get the name of the application"""
|
175
|
+
return self.root.dir.name
|
sera/typing.py
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
from
|
3
|
+
from enum import Enum
|
4
|
+
from typing import Annotated, Any, TypeGuard, TypeVar, Union
|
5
|
+
|
6
|
+
import msgspec
|
4
7
|
|
5
8
|
|
6
9
|
class doc(str):
|
@@ -12,3 +15,18 @@ FieldName = Annotated[str, doc("field name of a class")]
|
|
12
15
|
ObjectPath = Annotated[
|
13
16
|
str, doc("path of an object (e.g., can be function, class, etc.)")
|
14
17
|
]
|
18
|
+
|
19
|
+
|
20
|
+
class Language(str, Enum):
|
21
|
+
Python = "python"
|
22
|
+
Typescript = "typescript"
|
23
|
+
|
24
|
+
|
25
|
+
# re-export msgspec.UnsetType & msgspec.UNSET, so that we are consistent with ORM & data modules
|
26
|
+
UnsetType = msgspec.UnsetType
|
27
|
+
UNSET: Any = msgspec.UNSET
|
28
|
+
|
29
|
+
|
30
|
+
def is_set(value: Union[T, UnsetType]) -> TypeGuard[T]:
|
31
|
+
"""Typeguard to check if a value is set (not UNSET)"""
|
32
|
+
return value is not UNSET
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: sera-2
|
3
|
-
Version: 1.6.
|
3
|
+
Version: 1.6.2
|
4
4
|
Summary:
|
5
5
|
Author: Binh Vu
|
6
6
|
Author-email: bvu687@gmail.com
|
@@ -9,7 +9,7 @@ Classifier: Programming Language :: Python :: 3
|
|
9
9
|
Classifier: Programming Language :: Python :: 3.12
|
10
10
|
Classifier: Programming Language :: Python :: 3.13
|
11
11
|
Requires-Dist: black (>=25.1.0,<26.0.0)
|
12
|
-
Requires-Dist: codegen-2 (>=2.
|
12
|
+
Requires-Dist: codegen-2 (>=2.6.0,<3.0.0)
|
13
13
|
Requires-Dist: isort (>=6.0.1,<7.0.0)
|
14
14
|
Requires-Dist: litestar (>=2.15.1,<3.0.0)
|
15
15
|
Requires-Dist: loguru (>=0.7.0,<0.8.0)
|
@@ -2,30 +2,31 @@ sera/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
2
|
sera/constants.py,sha256=mzAaMyIx8TJK0-RYYJ5I24C4s0Uvj26OLMJmBo0pxHI,123
|
3
3
|
sera/libs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
4
4
|
sera/libs/api_helper.py,sha256=hUEy0INHM18lxTQ348tgbXNceOHcjiAnqmuL_8CRpLQ,2509
|
5
|
-
sera/libs/base_orm.py,sha256=
|
6
|
-
sera/libs/base_service.py,sha256=
|
5
|
+
sera/libs/base_orm.py,sha256=sTiHvbvLALSFygCITKFTXK1w-8UtxXGT_Te-1PEAiCI,3094
|
6
|
+
sera/libs/base_service.py,sha256=l5D4IjxIiz8LBRranUYddb8J0Y6SwSyetKYTLrCUdQA,4098
|
7
7
|
sera/make/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
|
-
sera/make/__main__.py,sha256=
|
9
|
-
sera/make/make_app.py,sha256=
|
8
|
+
sera/make/__main__.py,sha256=G5O7s6135-708honwqMFn2yPTs06WbGQTHpupID0eZ4,1417
|
9
|
+
sera/make/make_app.py,sha256=n9NtW73O3s_5Q31VHIRmnd-jEIcpDO7ksAsOdovde2s,5999
|
10
10
|
sera/make/make_python_api.py,sha256=RuJUm9z-4plBEtjobeOPr12o27OT-0tSeXI4ZlM3IY0,29433
|
11
|
-
sera/make/make_python_model.py,sha256=
|
11
|
+
sera/make/make_python_model.py,sha256=xf4revAwVWEnF6QhxbbqPyUGgXOOB--Gu3jPxsESg0Y,36593
|
12
12
|
sera/make/make_python_services.py,sha256=RsinYZdfkrTlTn9CT50VgqGs9w6IZawsJx-KEmqfnEY,2062
|
13
|
-
sera/make/make_typescript_model.py,sha256=
|
14
|
-
sera/misc/__init__.py,sha256=
|
13
|
+
sera/make/make_typescript_model.py,sha256=dT1ZHpsDvegX07jxDRncd_iu4FAiaV_7xyioWBJdbes,36999
|
14
|
+
sera/misc/__init__.py,sha256=Dh4uDq0D4N53h3zhvmwfa5a0TPVRSUvLzb0hkFuPirk,411
|
15
|
+
sera/misc/_formatter.py,sha256=aCGYL08l8f3aLODHxSocxBBwkRYEo3K1QzCDEn3suj0,1685
|
15
16
|
sera/misc/_utils.py,sha256=V5g4oLGHOhUCR75Kkcn1w01pAvGvaepK-T8Z3pIgHjI,1450
|
16
|
-
sera/models/__init__.py,sha256=
|
17
|
+
sera/models/__init__.py,sha256=vJC5Kzo_N7wd16ocNPy1VvAZDGNiWeiAhWJ4ihATKvA,780
|
17
18
|
sera/models/_class.py,sha256=Wf0e8x6-szG9TzoFkAlqj7_dG0SCICMBw_333n3paxk,2514
|
18
19
|
sera/models/_collection.py,sha256=ZnQEriKC4X88Zz48Kn1AVZKH-1_l8OgWa-zf2kcQOOE,1414
|
19
20
|
sera/models/_constraints.py,sha256=lZmCh6Py0UVMdhTR7zUOPPzGqJGbmDCzf7xH7yITcbQ,1278
|
20
21
|
sera/models/_datatype.py,sha256=uMxK_8wBLowaIMIAYCb3V17YmkzJrKKc5orjImzqWbA,5818
|
21
22
|
sera/models/_default.py,sha256=ABggW6qdPR4ZDqIPJdJ0GCGQ-7kfsfZmQ_DchgZEa-I,137
|
22
|
-
sera/models/_enum.py,sha256=
|
23
|
-
sera/models/_module.py,sha256=
|
23
|
+
sera/models/_enum.py,sha256=sy0q7E646F-APsqrVQ52r1fAQ_DCAeaNq5YM5QN3zIk,2070
|
24
|
+
sera/models/_module.py,sha256=8QRSCubZmdDP9rL58rGAS6X5VCrkc1ZHvuMu1I1KrWk,5043
|
24
25
|
sera/models/_multi_lingual_string.py,sha256=JETN6k00VH4wrA4w5vAHMEJV8fp3SY9bJebskFTjQLA,1186
|
25
26
|
sera/models/_parse.py,sha256=sJYfQtwek96ltpgxExG4xUbiLnU3qvNYhTP1CeyXGjs,9746
|
26
27
|
sera/models/_property.py,sha256=CmEmgOShtSyNFq05YW3tGupwCIVRzPMKudXWld8utPk,5530
|
27
28
|
sera/models/_schema.py,sha256=r-Gqg9Lb_wR3UrbNvfXXgt_qs5bts0t2Ve7aquuF_OI,1155
|
28
|
-
sera/typing.py,sha256=
|
29
|
-
sera_2-1.6.
|
30
|
-
sera_2-1.6.
|
31
|
-
sera_2-1.6.
|
29
|
+
sera/typing.py,sha256=Q4QMfbtfrCjC9tFfsZPhsAnbNX4lm4NHQ9lmjNXYdV0,772
|
30
|
+
sera_2-1.6.2.dist-info/METADATA,sha256=B_Brp3KBIu2C0iqX-5cjwdWAfen1DttoeUyj0ms_7-4,856
|
31
|
+
sera_2-1.6.2.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
|
32
|
+
sera_2-1.6.2.dist-info/RECORD,,
|
File without changes
|