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.
Files changed (47) hide show
  1. {sera_2-1.21.0 → sera_2-1.21.1}/PKG-INFO +1 -1
  2. {sera_2-1.21.0 → sera_2-1.21.1}/pyproject.toml +1 -1
  3. {sera_2-1.21.0 → sera_2-1.21.1}/sera/libs/directed_computing_graph/_dcg.py +5 -14
  4. {sera_2-1.21.0 → sera_2-1.21.1}/sera/misc/__init__.py +2 -0
  5. {sera_2-1.21.0 → sera_2-1.21.1}/sera/misc/_utils.py +38 -8
  6. {sera_2-1.21.0 → sera_2-1.21.1}/README.md +0 -0
  7. {sera_2-1.21.0 → sera_2-1.21.1}/sera/__init__.py +0 -0
  8. {sera_2-1.21.0 → sera_2-1.21.1}/sera/constants.py +0 -0
  9. {sera_2-1.21.0 → sera_2-1.21.1}/sera/exports/__init__.py +0 -0
  10. {sera_2-1.21.0 → sera_2-1.21.1}/sera/exports/schema.py +0 -0
  11. {sera_2-1.21.0 → sera_2-1.21.1}/sera/exports/test.py +0 -0
  12. {sera_2-1.21.0 → sera_2-1.21.1}/sera/libs/__init__.py +0 -0
  13. {sera_2-1.21.0 → sera_2-1.21.1}/sera/libs/api_helper.py +0 -0
  14. {sera_2-1.21.0 → sera_2-1.21.1}/sera/libs/api_test_helper.py +0 -0
  15. {sera_2-1.21.0 → sera_2-1.21.1}/sera/libs/base_orm.py +0 -0
  16. {sera_2-1.21.0 → sera_2-1.21.1}/sera/libs/base_service.py +0 -0
  17. {sera_2-1.21.0 → sera_2-1.21.1}/sera/libs/directed_computing_graph/__init__.py +0 -0
  18. {sera_2-1.21.0 → sera_2-1.21.1}/sera/libs/directed_computing_graph/_edge.py +0 -0
  19. {sera_2-1.21.0 → sera_2-1.21.1}/sera/libs/directed_computing_graph/_flow.py +0 -0
  20. {sera_2-1.21.0 → sera_2-1.21.1}/sera/libs/directed_computing_graph/_fn_signature.py +0 -0
  21. {sera_2-1.21.0 → sera_2-1.21.1}/sera/libs/directed_computing_graph/_node.py +0 -0
  22. {sera_2-1.21.0 → sera_2-1.21.1}/sera/libs/directed_computing_graph/_runtime.py +0 -0
  23. {sera_2-1.21.0 → sera_2-1.21.1}/sera/libs/directed_computing_graph/_type_conversion.py +0 -0
  24. {sera_2-1.21.0 → sera_2-1.21.1}/sera/libs/middlewares/__init__.py +0 -0
  25. {sera_2-1.21.0 → sera_2-1.21.1}/sera/libs/middlewares/auth.py +0 -0
  26. {sera_2-1.21.0 → sera_2-1.21.1}/sera/libs/middlewares/uscp.py +0 -0
  27. {sera_2-1.21.0 → sera_2-1.21.1}/sera/make/__init__.py +0 -0
  28. {sera_2-1.21.0 → sera_2-1.21.1}/sera/make/__main__.py +0 -0
  29. {sera_2-1.21.0 → sera_2-1.21.1}/sera/make/make_app.py +0 -0
  30. {sera_2-1.21.0 → sera_2-1.21.1}/sera/make/make_python_api.py +0 -0
  31. {sera_2-1.21.0 → sera_2-1.21.1}/sera/make/make_python_model.py +0 -0
  32. {sera_2-1.21.0 → sera_2-1.21.1}/sera/make/make_python_services.py +0 -0
  33. {sera_2-1.21.0 → sera_2-1.21.1}/sera/make/make_typescript_model.py +0 -0
  34. {sera_2-1.21.0 → sera_2-1.21.1}/sera/misc/_formatter.py +0 -0
  35. {sera_2-1.21.0 → sera_2-1.21.1}/sera/models/__init__.py +0 -0
  36. {sera_2-1.21.0 → sera_2-1.21.1}/sera/models/_class.py +0 -0
  37. {sera_2-1.21.0 → sera_2-1.21.1}/sera/models/_collection.py +0 -0
  38. {sera_2-1.21.0 → sera_2-1.21.1}/sera/models/_constraints.py +0 -0
  39. {sera_2-1.21.0 → sera_2-1.21.1}/sera/models/_datatype.py +0 -0
  40. {sera_2-1.21.0 → sera_2-1.21.1}/sera/models/_default.py +0 -0
  41. {sera_2-1.21.0 → sera_2-1.21.1}/sera/models/_enum.py +0 -0
  42. {sera_2-1.21.0 → sera_2-1.21.1}/sera/models/_module.py +0 -0
  43. {sera_2-1.21.0 → sera_2-1.21.1}/sera/models/_multi_lingual_string.py +0 -0
  44. {sera_2-1.21.0 → sera_2-1.21.1}/sera/models/_parse.py +0 -0
  45. {sera_2-1.21.0 → sera_2-1.21.1}/sera/models/_property.py +0 -0
  46. {sera_2-1.21.0 → sera_2-1.21.1}/sera/models/_schema.py +0 -0
  47. {sera_2-1.21.0 → sera_2-1.21.1}/sera/typing.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: sera-2
3
- Version: 1.21.0
3
+ Version: 1.21.1
4
4
  Summary:
5
5
  Author: Binh Vu
6
6
  Author-email: bvu687@gmail.com
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "sera-2"
3
- version = "1.21.0"
3
+ version = "1.21.1"
4
4
  description = ""
5
5
  authors = ["Binh Vu <bvu687@gmail.com>"]
6
6
  readme = "README.md"
@@ -1,18 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- import asyncio
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 = importlib.import_module(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(importlib.import_module(module), f"Create{clsname}")
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, dcg: DirectedComputingGraph, tables: Sequence[type]
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(input={innode: record})
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