tinyfan 0.2.0__tar.gz → 0.2.2__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 (57) hide show
  1. {tinyfan-0.2.0 → tinyfan-0.2.2}/PKG-INFO +10 -1
  2. {tinyfan-0.2.0 → tinyfan-0.2.2}/pyproject.toml +5 -2
  3. {tinyfan-0.2.0 → tinyfan-0.2.2}/src/tinyfan/codegen.py +38 -10
  4. tinyfan-0.2.2/src/tinyfan/dagviz.py +54 -0
  5. tinyfan-0.2.2/tests/utils/__init__.py +0 -0
  6. {tinyfan-0.2.0 → tinyfan-0.2.2}/uv.lock +23 -2
  7. {tinyfan-0.2.0 → tinyfan-0.2.2}/.github/workflows/publish.yml +0 -0
  8. {tinyfan-0.2.0 → tinyfan-0.2.2}/.gitignore +0 -0
  9. {tinyfan-0.2.0 → tinyfan-0.2.2}/.python-version +0 -0
  10. {tinyfan-0.2.0 → tinyfan-0.2.2}/README.md +0 -0
  11. {tinyfan-0.2.0 → tinyfan-0.2.2}/examples/README.md +0 -0
  12. {tinyfan-0.2.0 → tinyfan-0.2.2}/examples/configmap.yaml +0 -0
  13. {tinyfan-0.2.0 → tinyfan-0.2.2}/examples/configs.py +0 -0
  14. {tinyfan-0.2.0 → tinyfan-0.2.2}/examples/getting-started.py +0 -0
  15. {tinyfan-0.2.0 → tinyfan-0.2.2}/examples/packaged/.python-version +0 -0
  16. {tinyfan-0.2.0 → tinyfan-0.2.2}/examples/packaged/Dockerfile +0 -0
  17. {tinyfan-0.2.0 → tinyfan-0.2.2}/examples/packaged/README.md +0 -0
  18. {tinyfan-0.2.0 → tinyfan-0.2.2}/examples/packaged/pyproject.toml +0 -0
  19. {tinyfan-0.2.0 → tinyfan-0.2.2}/examples/packaged/src/tinyfan_examples_packaged/__init__.py +0 -0
  20. {tinyfan-0.2.0 → tinyfan-0.2.2}/examples/packaged/src/tinyfan_examples_packaged/asset.py +0 -0
  21. {tinyfan-0.2.0 → tinyfan-0.2.2}/examples/resource.py +0 -0
  22. {tinyfan-0.2.0 → tinyfan-0.2.2}/examples/secret.yaml +0 -0
  23. {tinyfan-0.2.0 → tinyfan-0.2.2}/src/tinyfan/__init__.py +0 -0
  24. {tinyfan-0.2.0 → tinyfan-0.2.2}/src/tinyfan/argo_typing.py +0 -0
  25. {tinyfan-0.2.0 → tinyfan-0.2.2}/src/tinyfan/cli.py +0 -0
  26. {tinyfan-0.2.0 → tinyfan-0.2.2}/src/tinyfan/config.py +0 -0
  27. {tinyfan-0.2.0 → tinyfan-0.2.2}/src/tinyfan/flow.py +0 -0
  28. {tinyfan-0.2.0 → tinyfan-0.2.2}/src/tinyfan/flowrundata.py +0 -0
  29. /tinyfan-0.2.0/src/tinyfan/resources/__init__.py → /tinyfan-0.2.2/src/tinyfan/py.typed +0 -0
  30. {tinyfan-0.2.0/src/tinyfan/stores → tinyfan-0.2.2/src/tinyfan/resources}/__init__.py +0 -0
  31. {tinyfan-0.2.0 → tinyfan-0.2.2}/src/tinyfan/resources/base.py +0 -0
  32. {tinyfan-0.2.0/src/tinyfan/utils → tinyfan-0.2.2/src/tinyfan/stores}/__init__.py +0 -0
  33. {tinyfan-0.2.0 → tinyfan-0.2.2}/src/tinyfan/stores/base.py +0 -0
  34. {tinyfan-0.2.0 → tinyfan-0.2.2}/src/tinyfan/stores/bigquery.py +0 -0
  35. {tinyfan-0.2.0 → tinyfan-0.2.2}/src/tinyfan/stores/gcs_pickle.py +0 -0
  36. {tinyfan-0.2.0 → tinyfan-0.2.2}/src/tinyfan/stores/naive.py +0 -0
  37. {tinyfan-0.2.0/tests → tinyfan-0.2.2/src/tinyfan/utils}/__init__.py +0 -0
  38. {tinyfan-0.2.0 → tinyfan-0.2.2}/src/tinyfan/utils/embed.py +0 -0
  39. {tinyfan-0.2.0 → tinyfan-0.2.2}/src/tinyfan/utils/exjson.py +0 -0
  40. {tinyfan-0.2.0 → tinyfan-0.2.2}/src/tinyfan/utils/merge.py +0 -0
  41. {tinyfan-0.2.0 → tinyfan-0.2.2}/src/tinyfan/utils/typing.py +0 -0
  42. {tinyfan-0.2.0 → tinyfan-0.2.2}/src/tinyfan/utils/yaml.py +0 -0
  43. {tinyfan-0.2.0/tests/codegen_samples/directory → tinyfan-0.2.2/tests}/__init__.py +0 -0
  44. {tinyfan-0.2.0/tests/utils → tinyfan-0.2.2/tests/codegen_samples/directory}/__init__.py +0 -0
  45. {tinyfan-0.2.0 → tinyfan-0.2.2}/tests/codegen_samples/directory/asset.py +0 -0
  46. {tinyfan-0.2.0 → tinyfan-0.2.2}/tests/codegen_samples/directory/asset2.py +0 -0
  47. {tinyfan-0.2.0 → tinyfan-0.2.2}/tests/codegen_samples/singlefile.py +0 -0
  48. {tinyfan-0.2.0 → tinyfan-0.2.2}/tests/conftest.py +0 -0
  49. {tinyfan-0.2.0 → tinyfan-0.2.2}/tests/helpers.py +0 -0
  50. {tinyfan-0.2.0 → tinyfan-0.2.2}/tests/openapi2jsonschema.py +0 -0
  51. {tinyfan-0.2.0 → tinyfan-0.2.2}/tests/test_codegen.py +0 -0
  52. {tinyfan-0.2.0 → tinyfan-0.2.2}/tests/test_decorators.py +0 -0
  53. {tinyfan-0.2.0 → tinyfan-0.2.2}/tests/utils/embed_samples/directory/__init__.py +0 -0
  54. {tinyfan-0.2.0 → tinyfan-0.2.2}/tests/utils/embed_samples/directory/dep.py +0 -0
  55. {tinyfan-0.2.0 → tinyfan-0.2.2}/tests/utils/embed_samples/singlefile.py +0 -0
  56. {tinyfan-0.2.0 → tinyfan-0.2.2}/tests/utils/test_embed.py +0 -0
  57. {tinyfan-0.2.0 → tinyfan-0.2.2}/tests/utils/test_exjson.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tinyfan
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: Tiny Argo Workflows python devkit
5
5
  Project-URL: Documentation, https://github.com/eunchuldev/tinyfan#readme
6
6
  Project-URL: Issues, https://github.com/eunchuldev/tinyfan/issues
@@ -12,6 +12,7 @@ Classifier: Operating System :: OS Independent
12
12
  Classifier: Programming Language :: Python :: 3.10
13
13
  Classifier: Programming Language :: Python :: 3.11
14
14
  Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Typing :: Typed
15
16
  Requires-Python: >=3.12
16
17
  Requires-Dist: croniter>=3.0.3
17
18
  Requires-Dist: pyyaml>=6.0.2
@@ -19,6 +20,7 @@ Requires-Dist: typer-slim>=0.15.1
19
20
  Provides-Extra: all
20
21
  Requires-Dist: google-cloud-bigquery[pandas]; extra == 'all'
21
22
  Requires-Dist: google-cloud-storage; extra == 'all'
23
+ Requires-Dist: networkx; extra == 'all'
22
24
  Requires-Dist: pandas; extra == 'all'
23
25
  Requires-Dist: pyarrow; extra == 'all'
24
26
  Provides-Extra: bigquery
@@ -26,9 +28,14 @@ Requires-Dist: google-cloud-bigquery[pandas]; extra == 'bigquery'
26
28
  Requires-Dist: pandas; extra == 'bigquery'
27
29
  Requires-Dist: pyarrow; extra == 'bigquery'
28
30
  Provides-Extra: dev
31
+ Requires-Dist: google-cloud-bigquery[pandas]; extra == 'dev'
32
+ Requires-Dist: google-cloud-storage; extra == 'dev'
29
33
  Requires-Dist: jsonpatch>=1; extra == 'dev'
30
34
  Requires-Dist: jsonschema[format]; extra == 'dev'
31
35
  Requires-Dist: mypy>=1.14; extra == 'dev'
36
+ Requires-Dist: networkx; extra == 'dev'
37
+ Requires-Dist: pandas; extra == 'dev'
38
+ Requires-Dist: pyarrow; extra == 'dev'
32
39
  Requires-Dist: pytest>=8.3.3; extra == 'dev'
33
40
  Requires-Dist: ruff; extra == 'dev'
34
41
  Requires-Dist: types-croniter; extra == 'dev'
@@ -39,6 +46,8 @@ Requires-Dist: google-cloud-storage; extra == 'gcs'
39
46
  Provides-Extra: pandas
40
47
  Requires-Dist: pandas; extra == 'pandas'
41
48
  Requires-Dist: pyarrow; extra == 'pandas'
49
+ Provides-Extra: viz
50
+ Requires-Dist: networkx; extra == 'viz'
42
51
  Description-Content-Type: text/markdown
43
52
 
44
53
  # Tinyfan
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "tinyfan"
3
- version = "0.2.0"
3
+ version = "0.2.2"
4
4
  description = 'Tiny Argo Workflows python devkit'
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -12,6 +12,7 @@ classifiers = [
12
12
  "Programming Language :: Python :: 3.10",
13
13
  "Programming Language :: Python :: 3.11",
14
14
  "Programming Language :: Python :: 3.12",
15
+ "Typing :: Typed",
15
16
  ]
16
17
  dependencies = ["croniter>=3.0.3", "pyyaml>=6.0.2", "typer-slim>=0.15.1"]
17
18
 
@@ -19,8 +20,9 @@ dependencies = ["croniter>=3.0.3", "pyyaml>=6.0.2", "typer-slim>=0.15.1"]
19
20
  tinyfan = "tinyfan.cli:main"
20
21
 
21
22
  [project.optional-dependencies]
22
- all = ["tinyfan[gcs]", "tinyfan[bigquery]", "tinyfan[pandas]"]
23
+ all = ["tinyfan[gcs]", "tinyfan[bigquery]", "tinyfan[pandas]", "tinyfan[viz]"]
23
24
  gcs = ["google-cloud-storage"]
25
+ viz = ["networkx"]
24
26
  bigquery = ["google-cloud-bigquery[pandas]", "tinyfan[pandas]"]
25
27
  pandas = ["pandas", "pyarrow"]
26
28
  dev = [
@@ -32,6 +34,7 @@ dev = [
32
34
  "types-PyYAML",
33
35
  "types-jsonschema",
34
36
  "types-croniter",
37
+ "tinyfan[all]",
35
38
  ]
36
39
 
37
40
  [project.urls]
@@ -169,6 +169,37 @@ class AssetTree:
169
169
  param_refs = [flow_refs[n] for n in func_param_names if n in flow_refs]
170
170
  node.refs = list(set(resource_refs + param_refs))
171
171
 
172
+ def relatives_by_schedules(self) -> dict[tuple[str, str], set[AssetNode]]:
173
+ relatives_by_schedules: dict[tuple[str, str], set[AssetNode]] = {}
174
+ for node in self.nodes.values():
175
+ if node.asset.schedule is None:
176
+ continue
177
+ schedule = (node.asset.tz or self.flow.tz, node.asset.schedule)
178
+ if schedule not in relatives_by_schedules:
179
+ relatives_by_schedules[schedule] = set()
180
+ for o in node.relatives():
181
+ relatives_by_schedules[schedule].add(o)
182
+ return relatives_by_schedules
183
+
184
+ def dagviz(self) -> str | None:
185
+ try:
186
+ import networkx as nx
187
+ except ImportError:
188
+ return None
189
+
190
+ from .dagviz import dagviz
191
+
192
+ relatives_by_schedules = self.relatives_by_schedules()
193
+ g: nx.DiGraph = nx.DiGraph()
194
+ # g.add_node(self.flow.name)
195
+ # g.add_nodes_from([k for k in relatives_by_schedules.keys()])
196
+ # g.add_nodes_from(self.nodes.keys())
197
+ for (_, schedule), relatives in relatives_by_schedules.items():
198
+ g.add_edge(self.flow.name, schedule)
199
+ g.add_edges_from([(schedule, r.asset.name) for r in relatives])
200
+ g.add_edges_from([(r.asset.name, d.asset.name) for r in relatives for d in r.parents])
201
+ return dagviz(g, round_angle=True)
202
+
172
203
  def compile(
173
204
  self,
174
205
  embedded: bool = True,
@@ -207,15 +238,7 @@ class AssetTree:
207
238
  " f.write(dumps(rundata))\n"
208
239
  )
209
240
 
210
- relatives_by_schedules: dict[tuple[str, str], set[AssetNode]] = {}
211
- for node in self.nodes.values():
212
- if node.asset.schedule is None:
213
- continue
214
- schedule = (node.asset.tz or self.flow.tz, node.asset.schedule)
215
- if schedule not in relatives_by_schedules:
216
- relatives_by_schedules[schedule] = set()
217
- for o in node.relatives():
218
- relatives_by_schedules[schedule].add(o)
241
+ relatives_by_schedules = self.relatives_by_schedules()
219
242
 
220
243
  manifests = [
221
244
  {
@@ -388,7 +411,12 @@ class AssetTree:
388
411
  }
389
412
  for (tz, schedule), relatives in relatives_by_schedules.items()
390
413
  ]
391
- return yaml_dump_all(manifests)
414
+ manifest = ""
415
+ dagviz = self.dagviz()
416
+ if dagviz:
417
+ manifest += "# " + dagviz.replace("\n", "\n# ") + "\n\n"
418
+ manifest += yaml_dump_all(manifests)
419
+ return manifest
392
420
 
393
421
 
394
422
  def codegen(
@@ -0,0 +1,54 @@
1
+ # COPIED FROM https://github.com/ctongfei/py-dagviz
2
+ from typing import Hashable, Dict, List
3
+ import array
4
+ import networkx as nx
5
+
6
+
7
+ def dagviz(g: nx.DiGraph, round_angle: bool = False) -> str:
8
+ """
9
+ Creates a text rendering of a directed acyclic graph (DAG) for visualization purposes in a terminal.
10
+
11
+ :param g: A directed acyclic graph, of type `nx.DiGraph`
12
+ :param round_angle: Whether to use a round-angled box drawing character or not
13
+ :return: A multi-line string representation of the directed acyclic graph, each line corresponding to a node
14
+ """
15
+ assert nx.is_directed_acyclic_graph(g), "Graph contains cycles"
16
+
17
+ rows: List[Hashable] = []
18
+ node_to_row: Dict[Hashable, int] = {}
19
+ indents: List[int] = []
20
+
21
+ def _process_dag(g: nx.DiGraph, indent: int):
22
+ for sg in nx.weakly_connected_components(g):
23
+ _process_component(nx.DiGraph(g.subgraph(sg)), indent=indent)
24
+
25
+ def _process_component(g: nx.DiGraph, indent: int):
26
+ sources = [v for v in g.nodes if g.in_degree(v) == 0]
27
+ for i in range(len(sources)):
28
+ node_to_row[sources[i]] = len(rows)
29
+ rows.append(sources[i])
30
+ indents.append(indent + i)
31
+ _process_dag(nx.DiGraph(g.subgraph(set(g.nodes).difference(sources))), indent=indent + len(sources))
32
+
33
+ _process_dag(g, indent=0)
34
+ a = [array.array("u", [" "] * indents[i] * 2) for i in range(len(rows))]
35
+ for i, u in enumerate(rows):
36
+ successors = sorted(g.successors(u), key=lambda v: node_to_row[v])
37
+ if len(successors) == 0:
38
+ continue
39
+ n = node_to_row[successors[-1]]
40
+ for j in range(i + 1, n):
41
+ a[j][indents[i] * 2] = "│"
42
+ for v in successors[:-1]:
43
+ j = node_to_row[v]
44
+ a[j][indents[i] * 2] = "┼" if indents[i] > 0 and a[j][indents[i] * 2 - 1] == "─" else "├"
45
+ for k in range(indents[i] * 2 + 1, indents[j] * 2):
46
+ a[j][k] = "─"
47
+ a[n][indents[i] * 2] = (
48
+ "┴" if indents[i] > 0 and a[n][indents[i] * 2 - 1] == "─" else ("╰" if round_angle else "└")
49
+ )
50
+ for k in range(indents[i] * 2 + 1, indents[n] * 2):
51
+ a[n][k] = "─"
52
+
53
+ lines: List[str] = [x.tounicode() + "• " + str(i).replace("\n", " ") for x, i in zip(a, rows)]
54
+ return "\n".join(lines)
File without changes
@@ -429,6 +429,15 @@ wheels = [
429
429
  { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 },
430
430
  ]
431
431
 
432
+ [[package]]
433
+ name = "networkx"
434
+ version = "3.4.2"
435
+ source = { registry = "https://pypi.org/simple" }
436
+ sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368 }
437
+ wheels = [
438
+ { url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263 },
439
+ ]
440
+
432
441
  [[package]]
433
442
  name = "numpy"
434
443
  version = "2.1.2"
@@ -775,7 +784,7 @@ wheels = [
775
784
 
776
785
  [[package]]
777
786
  name = "tinyfan"
778
- version = "0.2.0"
787
+ version = "0.2.2"
779
788
  source = { editable = "." }
780
789
  dependencies = [
781
790
  { name = "croniter" },
@@ -787,6 +796,7 @@ dependencies = [
787
796
  all = [
788
797
  { name = "google-cloud-bigquery", extra = ["pandas"] },
789
798
  { name = "google-cloud-storage" },
799
+ { name = "networkx" },
790
800
  { name = "pandas" },
791
801
  { name = "pyarrow" },
792
802
  ]
@@ -796,9 +806,14 @@ bigquery = [
796
806
  { name = "pyarrow" },
797
807
  ]
798
808
  dev = [
809
+ { name = "google-cloud-bigquery", extra = ["pandas"] },
810
+ { name = "google-cloud-storage" },
799
811
  { name = "jsonpatch" },
800
812
  { name = "jsonschema", extra = ["format"] },
801
813
  { name = "mypy" },
814
+ { name = "networkx" },
815
+ { name = "pandas" },
816
+ { name = "pyarrow" },
802
817
  { name = "pytest" },
803
818
  { name = "ruff" },
804
819
  { name = "types-croniter" },
@@ -812,6 +827,9 @@ pandas = [
812
827
  { name = "pandas" },
813
828
  { name = "pyarrow" },
814
829
  ]
830
+ viz = [
831
+ { name = "networkx" },
832
+ ]
815
833
 
816
834
  [package.metadata]
817
835
  requires-dist = [
@@ -821,21 +839,24 @@ requires-dist = [
821
839
  { name = "jsonpatch", marker = "extra == 'dev'", specifier = ">=1" },
822
840
  { name = "jsonschema", extras = ["format"], marker = "extra == 'dev'" },
823
841
  { name = "mypy", marker = "extra == 'dev'", specifier = ">=1.14" },
842
+ { name = "networkx", marker = "extra == 'viz'" },
824
843
  { name = "pandas", marker = "extra == 'pandas'" },
825
844
  { name = "pyarrow", marker = "extra == 'pandas'" },
826
845
  { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.3.3" },
827
846
  { name = "pyyaml", specifier = ">=6.0.2" },
828
847
  { name = "ruff", marker = "extra == 'dev'" },
848
+ { name = "tinyfan", extras = ["all"], marker = "extra == 'dev'" },
829
849
  { name = "tinyfan", extras = ["bigquery"], marker = "extra == 'all'" },
830
850
  { name = "tinyfan", extras = ["gcs"], marker = "extra == 'all'" },
831
851
  { name = "tinyfan", extras = ["pandas"], marker = "extra == 'all'" },
832
852
  { name = "tinyfan", extras = ["pandas"], marker = "extra == 'bigquery'" },
853
+ { name = "tinyfan", extras = ["viz"], marker = "extra == 'all'" },
833
854
  { name = "typer-slim", specifier = ">=0.15.1" },
834
855
  { name = "types-croniter", marker = "extra == 'dev'" },
835
856
  { name = "types-jsonschema", marker = "extra == 'dev'" },
836
857
  { name = "types-pyyaml", marker = "extra == 'dev'" },
837
858
  ]
838
- provides-extras = ["all", "gcs", "bigquery", "pandas", "dev"]
859
+ provides-extras = ["all", "gcs", "viz", "bigquery", "pandas", "dev"]
839
860
 
840
861
  [[package]]
841
862
  name = "typer-slim"
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