tinyfan 0.0.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 (44) hide show
  1. tinyfan-0.0.2/.github/workflows/publish.yml +33 -0
  2. tinyfan-0.0.2/.gitignore +10 -0
  3. tinyfan-0.0.2/.python-version +1 -0
  4. tinyfan-0.0.2/PKG-INFO +103 -0
  5. tinyfan-0.0.2/README.md +61 -0
  6. tinyfan-0.0.2/examples/getting-started.py +23 -0
  7. tinyfan-0.0.2/examples/realworld/.python-version +1 -0
  8. tinyfan-0.0.2/examples/realworld/README.md +0 -0
  9. tinyfan-0.0.2/examples/realworld/pyproject.toml +7 -0
  10. tinyfan-0.0.2/pyproject.toml +82 -0
  11. tinyfan-0.0.2/src/tinyfan/__init__.py +5 -0
  12. tinyfan-0.0.2/src/tinyfan/argo_typing.py +204 -0
  13. tinyfan-0.0.2/src/tinyfan/cli.py +24 -0
  14. tinyfan-0.0.2/src/tinyfan/codegen.py +326 -0
  15. tinyfan-0.0.2/src/tinyfan/flow.py +121 -0
  16. tinyfan-0.0.2/src/tinyfan/flowrundata.py +25 -0
  17. tinyfan-0.0.2/src/tinyfan/stores/__init__.py +0 -0
  18. tinyfan-0.0.2/src/tinyfan/stores/base.py +30 -0
  19. tinyfan-0.0.2/src/tinyfan/stores/bigquery.py +68 -0
  20. tinyfan-0.0.2/src/tinyfan/stores/gcs_pickle.py +0 -0
  21. tinyfan-0.0.2/src/tinyfan/stores/naive.py +38 -0
  22. tinyfan-0.0.2/src/tinyfan/utils/__init__.py +0 -0
  23. tinyfan-0.0.2/src/tinyfan/utils/embed.py +129 -0
  24. tinyfan-0.0.2/src/tinyfan/utils/exjson.py +33 -0
  25. tinyfan-0.0.2/src/tinyfan/utils/merge.py +17 -0
  26. tinyfan-0.0.2/src/tinyfan/utils/typing.py +0 -0
  27. tinyfan-0.0.2/src/tinyfan/utils/yaml.py +37 -0
  28. tinyfan-0.0.2/tests/__init__.py +0 -0
  29. tinyfan-0.0.2/tests/codegen_samples/directory/__init__.py +0 -0
  30. tinyfan-0.0.2/tests/codegen_samples/directory/asset.py +9 -0
  31. tinyfan-0.0.2/tests/codegen_samples/directory/asset2.py +7 -0
  32. tinyfan-0.0.2/tests/codegen_samples/singlefile.py +14 -0
  33. tinyfan-0.0.2/tests/conftest.py +63 -0
  34. tinyfan-0.0.2/tests/helpers.py +21 -0
  35. tinyfan-0.0.2/tests/openapi2jsonschema.py +275 -0
  36. tinyfan-0.0.2/tests/test_codegen.py +98 -0
  37. tinyfan-0.0.2/tests/test_decorators.py +9 -0
  38. tinyfan-0.0.2/tests/utils/__init__.py +0 -0
  39. tinyfan-0.0.2/tests/utils/embed_samples/directory/__init__.py +3 -0
  40. tinyfan-0.0.2/tests/utils/embed_samples/directory/dep.py +2 -0
  41. tinyfan-0.0.2/tests/utils/embed_samples/singlefile.py +1 -0
  42. tinyfan-0.0.2/tests/utils/test_embed.py +55 -0
  43. tinyfan-0.0.2/tests/utils/test_exjson.py +11 -0
  44. tinyfan-0.0.2/uv.lock +922 -0
@@ -0,0 +1,33 @@
1
+ name: Publish Pypi
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+
8
+ jobs:
9
+ publish:
10
+ name: python
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+ - name: Install uv
15
+ uses: astral-sh/setup-uv@v4
16
+ with:
17
+ enable-cache: true
18
+ - name: Set up Python
19
+ uses: actions/setup-python@v5
20
+ with:
21
+ python-version-file: "pyproject.toml"
22
+ - name: Install the Project
23
+ run: uv sync --all-extras --dev
24
+ - name: Run tests
25
+ run: uv run pytest tests
26
+ - name: Build Package
27
+ run: uv build
28
+ - name: Publish Project
29
+ run: uv publish
30
+ env:
31
+ UV_PUBLISH_PASSWORD: ${{ secrets.UV_PUBLISH_PASSWORD }}
32
+ UV_PUBLISH_USERNAME: __token__
33
+
@@ -0,0 +1,10 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
@@ -0,0 +1 @@
1
+ 3.12
tinyfan-0.0.2/PKG-INFO ADDED
@@ -0,0 +1,103 @@
1
+ Metadata-Version: 2.4
2
+ Name: tinyfan
3
+ Version: 0.0.2
4
+ Summary: Tiny Argo Workflows python devkit
5
+ Project-URL: Documentation, https://github.com/eunchuldev/tinyfan#readme
6
+ Project-URL: Issues, https://github.com/eunchuldev/tinyfan/issues
7
+ Project-URL: Source, https://github.com/eunchuldev/tinyfan
8
+ Author-email: eunchuldev <eunchulsong@gmail.com>
9
+ Keywords: argo-workflow,pipeline,tinyfan,workflow
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Requires-Python: >=3.12
16
+ Requires-Dist: croniter>=3.0.3
17
+ Requires-Dist: pyyaml>=6.0.2
18
+ Requires-Dist: typer-slim>=0.15.1
19
+ Provides-Extra: all
20
+ Requires-Dist: google-cloud-bigquery[pandas]; extra == 'all'
21
+ Requires-Dist: google-cloud-storage; extra == 'all'
22
+ Requires-Dist: pandas; extra == 'all'
23
+ Requires-Dist: pyarrow; extra == 'all'
24
+ Provides-Extra: bigquery
25
+ Requires-Dist: google-cloud-bigquery[pandas]; extra == 'bigquery'
26
+ Requires-Dist: pandas; extra == 'bigquery'
27
+ Requires-Dist: pyarrow; extra == 'bigquery'
28
+ Provides-Extra: dev
29
+ Requires-Dist: jsonpatch>=1; extra == 'dev'
30
+ Requires-Dist: jsonschema[format]; extra == 'dev'
31
+ Requires-Dist: mypy>=1.14; extra == 'dev'
32
+ Requires-Dist: pytest>=8.3.3; extra == 'dev'
33
+ Requires-Dist: ruff; extra == 'dev'
34
+ Requires-Dist: types-jsonschema; extra == 'dev'
35
+ Requires-Dist: types-pyyaml; extra == 'dev'
36
+ Provides-Extra: gcs
37
+ Requires-Dist: google-cloud-storage; extra == 'gcs'
38
+ Provides-Extra: pandas
39
+ Requires-Dist: pandas; extra == 'pandas'
40
+ Requires-Dist: pyarrow; extra == 'pandas'
41
+ Description-Content-Type: text/markdown
42
+
43
+ # Tinyfan
44
+
45
+ Tinyfan: Minimalist Data Pipeline Kit - Generate Argo Workflows with Python
46
+
47
+ # Features
48
+
49
+ * Generate Argo Workflows manifests from Python data pipeline definitions.
50
+ * Ease of Use and highly extendable
51
+ * Intuitive data model abstraction: let Argo handle orchestration—we focus on the data.
52
+ * Argo Workflows is notably lightweight and powerful – and so are we!
53
+ * Enhanced DevOps Experience: easly testable, Cloud Native and GitOps-ready.
54
+
55
+ # Our Goal
56
+
57
+ * **Minimize mental overhead** when building data pipelines.
58
+
59
+ # Not Our Goal
60
+
61
+ * **Full-featured orchestration framework:** We don't aim to be a battery-powered, comprehensive data pipeline orchestration solution.
62
+ No databases, web servers, or controllers—just a data pipeline compiler. Let's Algo Workflows handle all the complexity.
63
+
64
+ # Installation
65
+
66
+ ```
67
+ # Requires Python 3.10+
68
+ pipx install tinyfan
69
+ ```
70
+
71
+ # Tiny Example
72
+
73
+ ```python
74
+ # main.py
75
+
76
+ # Asset definitions
77
+
78
+ from tinyfan import asset
79
+
80
+ @asset(schedule="*/3 * * * *")
81
+ def world() -> str:
82
+ return "world"
83
+
84
+ @asset()
85
+ def greeting(world: str):
86
+ print("hello " + world)
87
+ ```
88
+
89
+ ```shell
90
+ # Apply the changes to argo workflow
91
+
92
+ tinyfan template main.py | kubectl apply -f -
93
+ ```
94
+
95
+ # Real World Example (still tiny though!)
96
+
97
+ Comming soon
98
+
99
+
100
+
101
+ # License
102
+
103
+ This project is licensed under the MIT License.
@@ -0,0 +1,61 @@
1
+ # Tinyfan
2
+
3
+ Tinyfan: Minimalist Data Pipeline Kit - Generate Argo Workflows with Python
4
+
5
+ # Features
6
+
7
+ * Generate Argo Workflows manifests from Python data pipeline definitions.
8
+ * Ease of Use and highly extendable
9
+ * Intuitive data model abstraction: let Argo handle orchestration—we focus on the data.
10
+ * Argo Workflows is notably lightweight and powerful – and so are we!
11
+ * Enhanced DevOps Experience: easly testable, Cloud Native and GitOps-ready.
12
+
13
+ # Our Goal
14
+
15
+ * **Minimize mental overhead** when building data pipelines.
16
+
17
+ # Not Our Goal
18
+
19
+ * **Full-featured orchestration framework:** We don't aim to be a battery-powered, comprehensive data pipeline orchestration solution.
20
+ No databases, web servers, or controllers—just a data pipeline compiler. Let's Algo Workflows handle all the complexity.
21
+
22
+ # Installation
23
+
24
+ ```
25
+ # Requires Python 3.10+
26
+ pipx install tinyfan
27
+ ```
28
+
29
+ # Tiny Example
30
+
31
+ ```python
32
+ # main.py
33
+
34
+ # Asset definitions
35
+
36
+ from tinyfan import asset
37
+
38
+ @asset(schedule="*/3 * * * *")
39
+ def world() -> str:
40
+ return "world"
41
+
42
+ @asset()
43
+ def greeting(world: str):
44
+ print("hello " + world)
45
+ ```
46
+
47
+ ```shell
48
+ # Apply the changes to argo workflow
49
+
50
+ tinyfan template main.py | kubectl apply -f -
51
+ ```
52
+
53
+ # Real World Example (still tiny though!)
54
+
55
+ Comming soon
56
+
57
+
58
+
59
+ # License
60
+
61
+ This project is licensed under the MIT License.
@@ -0,0 +1,23 @@
1
+ import tinyfan
2
+ from datetime import datetime
3
+
4
+
5
+ @tinyfan.asset(
6
+ schedule="*/1 * * * *",
7
+ )
8
+ def target() -> str:
9
+ return "world"
10
+
11
+
12
+ @tinyfan.asset()
13
+ def greeting(target: str):
14
+ print("hello " + target)
15
+
16
+
17
+ @tinyfan.asset(
18
+ schedule="*/1 * * * *",
19
+ )
20
+ def check_variables(ds: str, ts: str, data_interval_start: datetime, data_interval_end: datetime) -> None:
21
+ print(f"ds: {ds}, ts: {ts}")
22
+ print(f"data_interval_start: {data_interval_start}")
23
+ print(f"data_interval_end: {data_interval_end}")
@@ -0,0 +1 @@
1
+ 3.12
File without changes
@@ -0,0 +1,7 @@
1
+ [project]
2
+ name = "code"
3
+ version = "0.0.1"
4
+ description = "vtown"
5
+ readme = "README.md"
6
+ requires-python = ">=3.10"
7
+ dependencies = []
@@ -0,0 +1,82 @@
1
+ [project]
2
+ name = "tinyfan"
3
+ version = "0.0.2"
4
+ description = 'Tiny Argo Workflows python devkit'
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ keywords = [
8
+ "argo-workflow",
9
+ "tinyfan",
10
+ "workflow",
11
+ "pipeline",
12
+ ]
13
+ authors = [
14
+ { name = "eunchuldev", email = "eunchulsong@gmail.com" },
15
+ ]
16
+ classifiers = [
17
+ "Operating System :: OS Independent",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Programming Language :: Python :: 3.10",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ ]
23
+ dependencies = [
24
+ "croniter>=3.0.3",
25
+ "pyyaml>=6.0.2",
26
+ "typer-slim>=0.15.1",
27
+ ]
28
+
29
+ [project.scripts]
30
+ tinyfan = "tinyfan.cli:main"
31
+
32
+ [project.optional-dependencies]
33
+ all = [
34
+ "tinyfan[gcs]",
35
+ "tinyfan[bigquery]",
36
+ "tinyfan[pandas]",
37
+ ]
38
+ gcs = [
39
+ "google-cloud-storage",
40
+ ]
41
+ bigquery = [
42
+ "google-cloud-bigquery[pandas]",
43
+ "tinyfan[pandas]",
44
+ ]
45
+ pandas = [
46
+ "pandas",
47
+ "pyarrow",
48
+ ]
49
+ dev = [
50
+ "pytest >= 8.3.3",
51
+ "jsonpatch>=1",
52
+ "jsonschema[format]",
53
+ "mypy>=1.14",
54
+ "ruff",
55
+ "types-PyYAML",
56
+ "types-jsonschema",
57
+ ]
58
+
59
+ [project.urls]
60
+ Documentation = "https://github.com/eunchuldev/tinyfan#readme"
61
+ Issues = "https://github.com/eunchuldev/tinyfan/issues"
62
+ Source = "https://github.com/eunchuldev/tinyfan"
63
+
64
+ [build-system]
65
+ requires = ["hatchling"]
66
+ build-backend = "hatchling.build"
67
+
68
+ [tool.pytest.ini_options]
69
+ addopts = ["--import-mode=importlib"]
70
+
71
+ [tool.mypy]
72
+ exclude = ['build']
73
+
74
+ [[tool.mypy.overrides]]
75
+ module = ["lark.*", "jsonpatch.*"]
76
+ ignore_missing_imports = true
77
+
78
+ [tool.ruff]
79
+ line-length = 120
80
+
81
+ [tool.uv.workspace]
82
+ members = ["exampels/realworld"]
@@ -0,0 +1,5 @@
1
+ from .flow import Flow, Asset, asset
2
+ from .stores.base import StoreBase
3
+ from .stores.naive import NaiveStore
4
+
5
+ __all__ = ["Flow", "Asset", "asset", "StoreBase", "NaiveStore"]
@@ -0,0 +1,204 @@
1
+ from typing import TypedDict, NotRequired
2
+
3
+
4
+ class ExecAction(TypedDict):
5
+ command: list[str]
6
+
7
+
8
+ class SecretKeySelector(TypedDict):
9
+ key: str
10
+ name: str
11
+ optional: NotRequired[bool]
12
+
13
+
14
+ class HTTPHeaderSource(TypedDict):
15
+ secretKeyRef: SecretKeySelector
16
+
17
+
18
+ class HTTPHeader(TypedDict):
19
+ name: str
20
+ value: NotRequired[str]
21
+ valueFrom: NotRequired[HTTPHeaderSource]
22
+
23
+
24
+ class HTTPGetAction(TypedDict):
25
+ host: str
26
+ httpHeaders: NotRequired[list[HTTPHeader]]
27
+ path: NotRequired[str]
28
+ port: NotRequired[str | int]
29
+ scheme: NotRequired[str]
30
+
31
+
32
+ class TCPSocketAction(TypedDict):
33
+ host: str
34
+ port: str | int
35
+
36
+
37
+ class SleepAction(TypedDict):
38
+ seconds: int
39
+
40
+
41
+ class LifecycleHandler(TypedDict, total=False):
42
+ exec: ExecAction
43
+ httpGet: HTTPGetAction
44
+ sleep: SleepAction
45
+ tcpSocket: TCPSocketAction
46
+
47
+
48
+ class GRPCAction(TypedDict):
49
+ port: int
50
+ service: str
51
+
52
+
53
+ class Probe(TypedDict, total=False):
54
+ exec: ExecAction
55
+ failureThreshold: int
56
+ grpc: GRPCAction
57
+ httpGet: HTTPGetAction
58
+ initialDelaySeconds: int
59
+ periodSeconds: int
60
+ successThreshold: int
61
+ tcpSocket: TCPSocketAction
62
+ terminationGracePeriodSeconds: int
63
+ timeoutSeconds: int
64
+
65
+
66
+ class ConfigMapKeySelector(TypedDict):
67
+ key: str
68
+ name: str
69
+ optional: NotRequired[bool]
70
+
71
+
72
+ class ObjectFieldSelector(TypedDict):
73
+ apiVersion: str
74
+ fieldPath: str
75
+
76
+
77
+ class ResourceFieldSelector(TypedDict):
78
+ containerName: str
79
+ divisor: str
80
+ resource: str
81
+
82
+
83
+ class EnvVarSource(TypedDict, total=False):
84
+ configMapKeyRef: ConfigMapKeySelector
85
+ fieldRef: ObjectFieldSelector
86
+ resourceFieldRef: ResourceFieldSelector
87
+ secretKeyRef: SecretKeySelector
88
+
89
+
90
+ class EnvVar(TypedDict):
91
+ name: str
92
+ value: NotRequired[str]
93
+ valueFrom: NotRequired[EnvVarSource]
94
+
95
+
96
+ class Lifecycle(TypedDict, total=False):
97
+ postStart: LifecycleHandler
98
+ preStop: LifecycleHandler
99
+
100
+
101
+ class ContainerResizePolicy(TypedDict):
102
+ resourceName: str
103
+ restartPolicy: str
104
+
105
+
106
+ class ResourceQuantity(TypedDict, total=False):
107
+ memory: str
108
+ cpu: str
109
+
110
+
111
+ class ResourceClaim(TypedDict):
112
+ name: str
113
+ request: ResourceQuantity
114
+
115
+
116
+ class ResourceRequirements(TypedDict, total=False):
117
+ claims: list[ResourceClaim]
118
+ limits: ResourceQuantity
119
+ requests: ResourceQuantity
120
+
121
+
122
+ class AppArmorProfile(TypedDict, total=False):
123
+ localhostProfile: str
124
+ type: str
125
+
126
+
127
+ class Capabilities(TypedDict, total=False):
128
+ add: list[str]
129
+ drop: list[str]
130
+
131
+
132
+ class SELinuxOptions(TypedDict, total=False):
133
+ level: str
134
+ role: str
135
+ type: str
136
+ user: str
137
+
138
+
139
+ class SeccompProfile(TypedDict, total=False):
140
+ localhostProfile: str
141
+ type: str
142
+
143
+
144
+ class WindowOptions(TypedDict, total=False):
145
+ gmsaCredentialSpec: str
146
+ gmsaCredentialSpecName: str
147
+ hostProcess: bool
148
+ runAsUserName: str
149
+
150
+
151
+ class SecurityContext(TypedDict, total=False):
152
+ allowPrivilegeEscalation: bool
153
+ appArmorProfile: AppArmorProfile
154
+ capabilities: Capabilities
155
+ privileged: bool
156
+ procMount: str
157
+ readOnlyRootFilesystem: bool
158
+ runAsGroup: int
159
+ runAsNonRoot: bool
160
+ runAsUser: int
161
+ seLinuxOptions: SELinuxOptions
162
+ seccompProfile: SeccompProfile
163
+ windowOptions: WindowOptions
164
+
165
+
166
+ class VolumeDevice(TypedDict):
167
+ devicePath: str
168
+ name: str
169
+
170
+
171
+ class VolumeMount(TypedDict, total=False):
172
+ mounstPath: str
173
+ mountPropagation: str
174
+ name: str
175
+ readOnly: bool
176
+ recursiveReadOnly: str
177
+ subPath: str
178
+ subPathExpr: str
179
+
180
+
181
+ # Ref: https://argo-workflows.readthedocs.io/en/latest/fields/#scripttemplate
182
+ class ScriptTemplate(TypedDict, total=False):
183
+ args: list[str]
184
+ command: list[str]
185
+ env: list[EnvVar]
186
+ envFrom: list[EnvVar]
187
+ image: str
188
+ imagePullPolicy: str
189
+ lifecycle: Lifecycle
190
+ livenessProbe: Probe
191
+ readinessProbe: Probe
192
+ resizePolicy: list[ContainerResizePolicy]
193
+ resources: ResourceRequirements
194
+ restartPolicy: str
195
+ securityContext: SecurityContext
196
+ statusProbe: Probe
197
+ stdin: bool
198
+ stdinOnce: bool
199
+ terminationMessagePath: str
200
+ terminationMessagePolicy: str
201
+ tty: bool
202
+ volumeDevices: list[VolumeDevice]
203
+ volumeMounts: list[VolumeMount]
204
+ workingDir: str
@@ -0,0 +1,24 @@
1
+ import typer
2
+
3
+ from .codegen import codegen
4
+
5
+
6
+ def tinyfan(
7
+ location: str,
8
+ embedded: bool = False,
9
+ ):
10
+ """
11
+ Generate argocd workflow resource as yaml from tinyfan definitions
12
+ """
13
+ if location.endswith(".py"):
14
+ print(codegen(location=location, embedded=True))
15
+ else:
16
+ print(codegen(location=location, embedded=embedded))
17
+
18
+
19
+ def main():
20
+ return typer.run(tinyfan)
21
+
22
+
23
+ if __name__ == "__main__":
24
+ main()