sera-2 1.21.0__tar.gz → 1.21.1__tar.gz
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_2-1.21.0 → sera_2-1.21.1}/PKG-INFO +1 -1
- {sera_2-1.21.0 → sera_2-1.21.1}/pyproject.toml +1 -1
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/libs/directed_computing_graph/_dcg.py +5 -14
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/misc/__init__.py +2 -0
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/misc/_utils.py +38 -8
- {sera_2-1.21.0 → sera_2-1.21.1}/README.md +0 -0
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/__init__.py +0 -0
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/constants.py +0 -0
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/exports/__init__.py +0 -0
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/exports/schema.py +0 -0
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/exports/test.py +0 -0
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/libs/__init__.py +0 -0
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/libs/api_helper.py +0 -0
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/libs/api_test_helper.py +0 -0
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/libs/base_orm.py +0 -0
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/libs/base_service.py +0 -0
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/libs/directed_computing_graph/__init__.py +0 -0
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/libs/directed_computing_graph/_edge.py +0 -0
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/libs/directed_computing_graph/_flow.py +0 -0
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/libs/directed_computing_graph/_fn_signature.py +0 -0
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/libs/directed_computing_graph/_node.py +0 -0
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/libs/directed_computing_graph/_runtime.py +0 -0
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/libs/directed_computing_graph/_type_conversion.py +0 -0
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/libs/middlewares/__init__.py +0 -0
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/libs/middlewares/auth.py +0 -0
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/libs/middlewares/uscp.py +0 -0
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/make/__init__.py +0 -0
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/make/__main__.py +0 -0
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/make/make_app.py +0 -0
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/make/make_python_api.py +0 -0
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/make/make_python_model.py +0 -0
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/make/make_python_services.py +0 -0
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/make/make_typescript_model.py +0 -0
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/misc/_formatter.py +0 -0
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/models/__init__.py +0 -0
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/models/_class.py +0 -0
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/models/_collection.py +0 -0
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/models/_constraints.py +0 -0
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/models/_datatype.py +0 -0
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/models/_default.py +0 -0
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/models/_enum.py +0 -0
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/models/_module.py +0 -0
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/models/_multi_lingual_string.py +0 -0
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/models/_parse.py +0 -0
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/models/_property.py +0 -0
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/models/_schema.py +0 -0
- {sera_2-1.21.0 → sera_2-1.21.1}/sera/typing.py +0 -0
@@ -1,18 +1,6 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
import
|
4
|
-
import inspect
|
5
|
-
from dataclasses import dataclass
|
6
|
-
from enum import Enum
|
7
|
-
from typing import (
|
8
|
-
Annotated,
|
9
|
-
Any,
|
10
|
-
Awaitable,
|
11
|
-
Callable,
|
12
|
-
MutableSequence,
|
13
|
-
Optional,
|
14
|
-
Sequence,
|
15
|
-
)
|
3
|
+
from typing import Annotated, Any, Callable, MutableSequence, Optional, Sequence
|
16
4
|
|
17
5
|
from graph.retworkx import RetworkXStrDiGraph
|
18
6
|
|
@@ -296,7 +284,7 @@ class DirectedComputingGraph:
|
|
296
284
|
async def execute_async(
|
297
285
|
self,
|
298
286
|
input: dict[ComputeFnId, tuple],
|
299
|
-
output: set[str],
|
287
|
+
output: Optional[set[str]] = None,
|
300
288
|
context: Optional[
|
301
289
|
dict[str, Callable | Any] | Callable[[], dict[str, Any]]
|
302
290
|
] = None,
|
@@ -321,6 +309,9 @@ class DirectedComputingGraph:
|
|
321
309
|
else:
|
322
310
|
context = {k: v() if callable(v) else v for k, v in context.items()}
|
323
311
|
|
312
|
+
if output is None:
|
313
|
+
output = set()
|
314
|
+
|
324
315
|
# This is a quick reactive algorithm, we may be able to do it better.
|
325
316
|
# The idea is when all inputs of a function is available, we can execute a function.
|
326
317
|
# We assume that the memory is large enough to hold all the functions and their inputs
|
@@ -4,6 +4,7 @@ from sera.misc._utils import (
|
|
4
4
|
RelTableIndex,
|
5
5
|
assert_isinstance,
|
6
6
|
assert_not_null,
|
7
|
+
auto_import,
|
7
8
|
filter_duplication,
|
8
9
|
get_classpath,
|
9
10
|
identity,
|
@@ -32,4 +33,5 @@ __all__ = [
|
|
32
33
|
"RelTableIndex",
|
33
34
|
"load_data_from_dir",
|
34
35
|
"replay_events",
|
36
|
+
"auto_import",
|
35
37
|
]
|
@@ -1,9 +1,9 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
import importlib
|
4
3
|
import inspect
|
5
4
|
import re
|
6
5
|
from collections import defaultdict
|
6
|
+
from importlib import import_module
|
7
7
|
from pathlib import Path
|
8
8
|
from typing import (
|
9
9
|
TYPE_CHECKING,
|
@@ -23,7 +23,8 @@ import orjson
|
|
23
23
|
import serde.csv
|
24
24
|
import serde.json
|
25
25
|
from loguru import logger
|
26
|
-
from sqlalchemy import Engine, text
|
26
|
+
from sqlalchemy import Engine, select, text
|
27
|
+
from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession
|
27
28
|
from sqlalchemy.orm import Session
|
28
29
|
from tqdm import tqdm
|
29
30
|
|
@@ -77,7 +78,7 @@ reserved_keywords = {
|
|
77
78
|
def import_attr(attr_ident: str):
|
78
79
|
lst = attr_ident.rsplit(".", 1)
|
79
80
|
module, cls = lst
|
80
|
-
module =
|
81
|
+
module = import_module(module)
|
81
82
|
return getattr(module, cls)
|
82
83
|
|
83
84
|
|
@@ -147,7 +148,7 @@ def identity(x: T) -> T:
|
|
147
148
|
|
148
149
|
|
149
150
|
def get_classpath(type: Type | Callable) -> str:
|
150
|
-
if type.__module__ == "builtins":
|
151
|
+
if hasattr(type, "__module__") and type.__module__ == "builtins":
|
151
152
|
return type.__qualname__
|
152
153
|
|
153
154
|
if hasattr(type, "__qualname__"):
|
@@ -174,7 +175,7 @@ def get_dbclass_deser_func(type: type[T]) -> Callable[[dict], T]:
|
|
174
175
|
.replace(".models.db.", ".models.data.")
|
175
176
|
.rsplit(".", maxsplit=1)
|
176
177
|
)
|
177
|
-
StructType = getattr(
|
178
|
+
StructType = getattr(import_module(module), f"Create{clsname}")
|
178
179
|
|
179
180
|
def deser_func(obj: dict):
|
180
181
|
record = msgspec.json.decode(orjson.dumps(obj), type=StructType)
|
@@ -187,6 +188,29 @@ def get_dbclass_deser_func(type: type[T]) -> Callable[[dict], T]:
|
|
187
188
|
return deser_func
|
188
189
|
|
189
190
|
|
191
|
+
def auto_import(module: type):
|
192
|
+
"""Auto-import all submodules of a given module."""
|
193
|
+
mdir = Path(module.__path__[0])
|
194
|
+
for py_file in mdir.rglob("*.py"):
|
195
|
+
if py_file.name == "__init__.py":
|
196
|
+
continue
|
197
|
+
|
198
|
+
# Get the path of the submodule relative to the parent module's directory
|
199
|
+
relative_path = py_file.relative_to(mdir)
|
200
|
+
|
201
|
+
# Create the module import string from the file path
|
202
|
+
# e.g., for a file like `sub/module.py`, this creates `sub.module`
|
203
|
+
module_parts = list(relative_path.parts)
|
204
|
+
module_parts[-1] = relative_path.stem # remove .py extension
|
205
|
+
relative_module_name = ".".join(module_parts)
|
206
|
+
|
207
|
+
# Construct the full module path
|
208
|
+
full_module_path = f"{module.__name__}.{relative_module_name}"
|
209
|
+
|
210
|
+
# Dynamically import the module
|
211
|
+
import_module(full_module_path)
|
212
|
+
|
213
|
+
|
190
214
|
class LoadTableDataArgs(TypedDict, total=False):
|
191
215
|
table: type
|
192
216
|
tables: Sequence[type]
|
@@ -374,7 +398,10 @@ def load_data_from_dir(
|
|
374
398
|
|
375
399
|
|
376
400
|
async def replay_events(
|
377
|
-
engine: AsyncEngine,
|
401
|
+
engine: AsyncEngine,
|
402
|
+
dcg: DirectedComputingGraph,
|
403
|
+
tables: Sequence[type],
|
404
|
+
verbose: bool = False,
|
378
405
|
):
|
379
406
|
"""Replay the events in the DirectedComputingGraph. This is useful to re-run the workflows
|
380
407
|
that computes derived data after initial data loading.
|
@@ -383,7 +410,10 @@ async def replay_events(
|
|
383
410
|
for tbl in tables:
|
384
411
|
innode = f"{tbl.__tablename__}.create"
|
385
412
|
for record in tqdm(
|
386
|
-
session.execute(select(tbl)).scalars(),
|
413
|
+
(await session.execute(select(tbl))).scalars(),
|
387
414
|
desc=f"Replaying events for {tbl.__tablename__}",
|
415
|
+
disable=not verbose,
|
388
416
|
):
|
389
|
-
await dcg.execute_async(
|
417
|
+
await dcg.execute_async(
|
418
|
+
input={innode: (record,)}, context={"session": session}
|
419
|
+
)
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|