tinyfan 0.2.2__tar.gz → 0.2.3__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.
- {tinyfan-0.2.2 → tinyfan-0.2.3}/PKG-INFO +1 -1
- {tinyfan-0.2.2 → tinyfan-0.2.3}/pyproject.toml +1 -1
- {tinyfan-0.2.2 → tinyfan-0.2.3}/src/tinyfan/codegen.py +7 -7
- {tinyfan-0.2.2 → tinyfan-0.2.3}/src/tinyfan/dagviz.py +3 -1
- {tinyfan-0.2.2 → tinyfan-0.2.3}/src/tinyfan/flow.py +9 -4
- tinyfan-0.2.3/tests/test_decorators.py +32 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/uv.lock +1 -1
- tinyfan-0.2.2/tests/test_decorators.py +0 -9
- {tinyfan-0.2.2 → tinyfan-0.2.3}/.github/workflows/publish.yml +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/.gitignore +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/.python-version +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/README.md +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/examples/README.md +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/examples/configmap.yaml +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/examples/configs.py +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/examples/getting-started.py +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/examples/packaged/.python-version +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/examples/packaged/Dockerfile +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/examples/packaged/README.md +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/examples/packaged/pyproject.toml +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/examples/packaged/src/tinyfan_examples_packaged/__init__.py +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/examples/packaged/src/tinyfan_examples_packaged/asset.py +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/examples/resource.py +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/examples/secret.yaml +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/src/tinyfan/__init__.py +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/src/tinyfan/argo_typing.py +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/src/tinyfan/cli.py +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/src/tinyfan/config.py +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/src/tinyfan/flowrundata.py +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/src/tinyfan/py.typed +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/src/tinyfan/resources/__init__.py +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/src/tinyfan/resources/base.py +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/src/tinyfan/stores/__init__.py +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/src/tinyfan/stores/base.py +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/src/tinyfan/stores/bigquery.py +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/src/tinyfan/stores/gcs_pickle.py +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/src/tinyfan/stores/naive.py +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/src/tinyfan/utils/__init__.py +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/src/tinyfan/utils/embed.py +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/src/tinyfan/utils/exjson.py +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/src/tinyfan/utils/merge.py +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/src/tinyfan/utils/typing.py +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/src/tinyfan/utils/yaml.py +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/tests/__init__.py +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/tests/codegen_samples/directory/__init__.py +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/tests/codegen_samples/directory/asset.py +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/tests/codegen_samples/directory/asset2.py +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/tests/codegen_samples/singlefile.py +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/tests/conftest.py +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/tests/helpers.py +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/tests/openapi2jsonschema.py +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/tests/test_codegen.py +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/tests/utils/__init__.py +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/tests/utils/embed_samples/directory/__init__.py +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/tests/utils/embed_samples/directory/dep.py +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/tests/utils/embed_samples/singlefile.py +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/tests/utils/test_embed.py +0 -0
- {tinyfan-0.2.2 → tinyfan-0.2.3}/tests/utils/test_exjson.py +0 -0
|
@@ -191,13 +191,13 @@ class AssetTree:
|
|
|
191
191
|
|
|
192
192
|
relatives_by_schedules = self.relatives_by_schedules()
|
|
193
193
|
g: nx.DiGraph = nx.DiGraph()
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
g.add_edge(self.flow.name,
|
|
199
|
-
g.add_edges_from([(
|
|
200
|
-
g.add_edges_from([(
|
|
194
|
+
g.add_node(self.flow.name, bullet="■")
|
|
195
|
+
for (tz, schedule), relatives in relatives_by_schedules.items():
|
|
196
|
+
schedule_node_name = schedule + f" ({tz})"
|
|
197
|
+
g.add_node(schedule_node_name, bullet="○")
|
|
198
|
+
g.add_edge(self.flow.name, schedule_node_name)
|
|
199
|
+
g.add_edges_from([(schedule_node_name, r.asset.name) for r in relatives])
|
|
200
|
+
g.add_edges_from([(d.asset.name, r.asset.name) for r in relatives for d in r.parents])
|
|
201
201
|
return dagviz(g, round_angle=True)
|
|
202
202
|
|
|
203
203
|
def compile(
|
|
@@ -15,6 +15,7 @@ def dagviz(g: nx.DiGraph, round_angle: bool = False) -> str:
|
|
|
15
15
|
assert nx.is_directed_acyclic_graph(g), "Graph contains cycles"
|
|
16
16
|
|
|
17
17
|
rows: List[Hashable] = []
|
|
18
|
+
bullets: List[str] = []
|
|
18
19
|
node_to_row: Dict[Hashable, int] = {}
|
|
19
20
|
indents: List[int] = []
|
|
20
21
|
|
|
@@ -27,6 +28,7 @@ def dagviz(g: nx.DiGraph, round_angle: bool = False) -> str:
|
|
|
27
28
|
for i in range(len(sources)):
|
|
28
29
|
node_to_row[sources[i]] = len(rows)
|
|
29
30
|
rows.append(sources[i])
|
|
31
|
+
bullets.append(g.nodes[sources[i]].get("bullet", "•"))
|
|
30
32
|
indents.append(indent + i)
|
|
31
33
|
_process_dag(nx.DiGraph(g.subgraph(set(g.nodes).difference(sources))), indent=indent + len(sources))
|
|
32
34
|
|
|
@@ -50,5 +52,5 @@ def dagviz(g: nx.DiGraph, round_angle: bool = False) -> str:
|
|
|
50
52
|
for k in range(indents[i] * 2 + 1, indents[n] * 2):
|
|
51
53
|
a[n][k] = "─"
|
|
52
54
|
|
|
53
|
-
lines: List[str] = [x.tounicode() + "
|
|
55
|
+
lines: List[str] = [x.tounicode() + f"{b} " + str(i).replace("\n", " ") for x, b, i in zip(a, bullets, rows)]
|
|
54
56
|
return "\n".join(lines)
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import inspect
|
|
2
|
+
import asyncio
|
|
2
3
|
from .stores.base import StoreBase
|
|
3
4
|
from .stores.naive import NaiveStore
|
|
4
5
|
from .flowrundata import FlowRunData, StoreIdx, UMeta
|
|
5
6
|
from dataclasses import dataclass, field
|
|
6
|
-
from typing import Callable, Any, Generic, TypeVar
|
|
7
|
+
from typing import Callable, Any, Generic, TypeVar, Awaitable, Coroutine, cast
|
|
7
8
|
from .resources.base import ResourceBase
|
|
8
9
|
from .argo_typing import ScriptTemplate
|
|
9
10
|
from .config import Config, ConfigValue
|
|
@@ -41,7 +42,7 @@ DEFAULT_FLOW: Flow = Flow(
|
|
|
41
42
|
@dataclass
|
|
42
43
|
class Asset(Generic[Ret, UMeta, StoreIdx]):
|
|
43
44
|
flow: Flow
|
|
44
|
-
func: Callable[..., Ret]
|
|
45
|
+
func: Callable[..., Ret | Awaitable[Ret]]
|
|
45
46
|
store: StoreBase[Ret, UMeta, StoreIdx]
|
|
46
47
|
schedule: str | None = None
|
|
47
48
|
tz: str | None = None
|
|
@@ -84,11 +85,15 @@ class Asset(Generic[Ret, UMeta, StoreIdx]):
|
|
|
84
85
|
if name in func_param_names:
|
|
85
86
|
params[name] = data
|
|
86
87
|
ret = self.func(**params)
|
|
87
|
-
|
|
88
|
+
if inspect.isawaitable(ret):
|
|
89
|
+
res: Ret = asyncio.run(cast(Coroutine[Any, Any, Ret], ret))
|
|
90
|
+
else:
|
|
91
|
+
res = cast(Ret, ret)
|
|
92
|
+
store_entry_idx = self.store.store(res, rundata)
|
|
88
93
|
rundata["store_entry_idx"] = store_entry_idx
|
|
89
94
|
if "parents" in rundata:
|
|
90
95
|
del rundata["parents"]
|
|
91
|
-
return (
|
|
96
|
+
return (res, rundata)
|
|
92
97
|
|
|
93
98
|
|
|
94
99
|
class AssetFunc(Generic[Ret, UMeta, StoreIdx]):
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from tinyfan import asset
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def test_preserve_original_functionality():
|
|
5
|
+
@asset()
|
|
6
|
+
def test(arg: str):
|
|
7
|
+
return arg
|
|
8
|
+
|
|
9
|
+
assert test(arg="test") == "test"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_asset_run():
|
|
13
|
+
@asset()
|
|
14
|
+
def test(ds: str):
|
|
15
|
+
return ds
|
|
16
|
+
|
|
17
|
+
actual, _ = test.asset.run({"ds": "2020-01-01"})
|
|
18
|
+
expected = "2020-01-01"
|
|
19
|
+
assert actual == expected
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_async_asset_run():
|
|
23
|
+
async def awaitable() -> str:
|
|
24
|
+
return " awaited"
|
|
25
|
+
|
|
26
|
+
@asset()
|
|
27
|
+
async def test(ds: str):
|
|
28
|
+
return ds + await awaitable()
|
|
29
|
+
|
|
30
|
+
actual, _ = test.asset.run({"ds": "2020-01-01"})
|
|
31
|
+
expected = "2020-01-01 awaited"
|
|
32
|
+
assert actual == expected
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|