codeflowhub 1.3.0__tar.gz → 1.4.0__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.
- {codeflowhub-1.3.0 → codeflowhub-1.4.0}/PKG-INFO +1 -1
- {codeflowhub-1.3.0 → codeflowhub-1.4.0}/codeflowhub/flow.py +15 -1
- {codeflowhub-1.3.0 → codeflowhub-1.4.0}/codeflowhub/service/airflow_exporter.py +7 -1
- {codeflowhub-1.3.0 → codeflowhub-1.4.0}/codeflowhub.egg-info/PKG-INFO +1 -1
- {codeflowhub-1.3.0 → codeflowhub-1.4.0}/codeflowhub.egg-info/SOURCES.txt +2 -1
- {codeflowhub-1.3.0 → codeflowhub-1.4.0}/setup.py +1 -1
- codeflowhub-1.4.0/tests/test_flow_concurrency.py +55 -0
- {codeflowhub-1.3.0 → codeflowhub-1.4.0}/LICENSE +0 -0
- {codeflowhub-1.3.0 → codeflowhub-1.4.0}/README.md +0 -0
- {codeflowhub-1.3.0 → codeflowhub-1.4.0}/codeflowhub/__init__.py +0 -0
- {codeflowhub-1.3.0 → codeflowhub-1.4.0}/codeflowhub/action.py +0 -0
- {codeflowhub-1.3.0 → codeflowhub-1.4.0}/codeflowhub/airflow/__init__.py +0 -0
- {codeflowhub-1.3.0 → codeflowhub-1.4.0}/codeflowhub/airflow/xcom.py +0 -0
- {codeflowhub-1.3.0 → codeflowhub-1.4.0}/codeflowhub/base.py +0 -0
- {codeflowhub-1.3.0 → codeflowhub-1.4.0}/codeflowhub/model.py +0 -0
- {codeflowhub-1.3.0 → codeflowhub-1.4.0}/codeflowhub/service/__init__.py +0 -0
- {codeflowhub-1.3.0 → codeflowhub-1.4.0}/codeflowhub/storage/__init__.py +0 -0
- {codeflowhub-1.3.0 → codeflowhub-1.4.0}/codeflowhub/storage/local_storage.py +0 -0
- {codeflowhub-1.3.0 → codeflowhub-1.4.0}/codeflowhub/storage/s3_storage.py +0 -0
- {codeflowhub-1.3.0 → codeflowhub-1.4.0}/codeflowhub/storage/storage.py +0 -0
- {codeflowhub-1.3.0 → codeflowhub-1.4.0}/codeflowhub/task.py +0 -0
- {codeflowhub-1.3.0 → codeflowhub-1.4.0}/codeflowhub/template/__init__.py +0 -0
- {codeflowhub-1.3.0 → codeflowhub-1.4.0}/codeflowhub/template/analyze_speaker_pkg/__init__.py +0 -0
- {codeflowhub-1.3.0 → codeflowhub-1.4.0}/codeflowhub/template/analyze_speaker_pkg/main.py +0 -0
- {codeflowhub-1.3.0 → codeflowhub-1.4.0}/codeflowhub/template/extract_voice_pkg/__init__.py +0 -0
- {codeflowhub-1.3.0 → codeflowhub-1.4.0}/codeflowhub/template/extract_voice_pkg/main.py +0 -0
- {codeflowhub-1.3.0 → codeflowhub-1.4.0}/codeflowhub/template/read_pdf_pkg/__init__.py +0 -0
- {codeflowhub-1.3.0 → codeflowhub-1.4.0}/codeflowhub/template/read_pdf_pkg/main.py +0 -0
- {codeflowhub-1.3.0 → codeflowhub-1.4.0}/codeflowhub/template/transcript_pkg/__init__.py +0 -0
- {codeflowhub-1.3.0 → codeflowhub-1.4.0}/codeflowhub/template/transcript_pkg/main.py +0 -0
- {codeflowhub-1.3.0 → codeflowhub-1.4.0}/codeflowhub.egg-info/dependency_links.txt +0 -0
- {codeflowhub-1.3.0 → codeflowhub-1.4.0}/codeflowhub.egg-info/top_level.txt +0 -0
- {codeflowhub-1.3.0 → codeflowhub-1.4.0}/setup.cfg +0 -0
|
@@ -31,6 +31,8 @@ class FlowDecorator(BaseDecorator):
|
|
|
31
31
|
default_args:dict # Airflow DAG default_args (owner, retries, retry_delay 등 override)
|
|
32
32
|
schedule:str # Airflow DAG 스케줄 (cron 문자열 / preset / timedelta / None=수동 트리거)
|
|
33
33
|
doc_md:str # Airflow DAG doc_md (UI에 표시될 Markdown 문서)
|
|
34
|
+
max_active_runs:int # DAG 동시 실행(run) 상한 (None=Airflow 기본값)
|
|
35
|
+
max_active_tasks:int # DAG 내 동시 실행 task 상한 (None=Airflow 기본값)
|
|
34
36
|
start_date:datetime # Airflow DAG start_date (None이면 export 시점)
|
|
35
37
|
secrets:list # 모든 task에 공통 주입할 K8s Secret(env) 목록
|
|
36
38
|
on_failure: 'BaseDecorator' = None # 모든 task의 기본 failure handler
|
|
@@ -39,7 +41,8 @@ class FlowDecorator(BaseDecorator):
|
|
|
39
41
|
tags=None, annotations=None, service_account_name=None, volumes=None,
|
|
40
42
|
airflow_sidecar_image=None, airflow_connection_id=None, repo=None, path=None,
|
|
41
43
|
export_destination='dags', default_args=None, secrets=None, on_failure=None,
|
|
42
|
-
schedule=None, start_date=None, doc_md=None,
|
|
44
|
+
schedule=None, start_date=None, doc_md=None, max_active_runs=None, max_active_tasks=None,
|
|
45
|
+
schedule_interval=None, **kwargs):
|
|
43
46
|
# CLI 속성 먼저 초기화 (init()에서 사용됨)
|
|
44
47
|
self._cli_export = None
|
|
45
48
|
self._cli_job_dir = None
|
|
@@ -67,6 +70,8 @@ class FlowDecorator(BaseDecorator):
|
|
|
67
70
|
)
|
|
68
71
|
self.start_date = start_date
|
|
69
72
|
self.doc_md = self._normalize_doc_md(doc_md)
|
|
73
|
+
self.max_active_runs = self._positive_int('max_active_runs', max_active_runs)
|
|
74
|
+
self.max_active_tasks = self._positive_int('max_active_tasks', max_active_tasks)
|
|
70
75
|
# Design Ref: §3.2 — flow-level 공통 Secret(env)
|
|
71
76
|
self.secrets = secrets or []
|
|
72
77
|
self.on_failure = on_failure
|
|
@@ -77,6 +82,15 @@ class FlowDecorator(BaseDecorator):
|
|
|
77
82
|
# super().__init__()는 마지막에 호출 (init()이 실행됨)
|
|
78
83
|
super().__init__(*args, name=name, **kwargs)
|
|
79
84
|
|
|
85
|
+
@staticmethod
|
|
86
|
+
def _positive_int(name, value):
|
|
87
|
+
"""None 또는 양의 정수만 허용 (선언 시점에 조기 실패)"""
|
|
88
|
+
if value is None:
|
|
89
|
+
return None
|
|
90
|
+
if isinstance(value, bool) or not isinstance(value, int) or value < 1:
|
|
91
|
+
raise ValueError(f'{name}는 1 이상의 정수 또는 None만 가능합니다: {value!r}')
|
|
92
|
+
return value
|
|
93
|
+
|
|
80
94
|
@staticmethod
|
|
81
95
|
def _normalize_doc_md(doc_md):
|
|
82
96
|
"""doc_md 정규화 (선언 시점에 조기 실패)
|
|
@@ -280,13 +280,19 @@ DAG_DOC_MD = {self._render_doc_md(doc_md)}
|
|
|
280
280
|
'''
|
|
281
281
|
doc_md_line = "\n doc_md=DAG_DOC_MD,"
|
|
282
282
|
|
|
283
|
+
concurrency_lines = ""
|
|
284
|
+
for key in ('max_active_runs', 'max_active_tasks'):
|
|
285
|
+
value = getattr(self.flow, key, None)
|
|
286
|
+
if value is not None:
|
|
287
|
+
concurrency_lines += f"\n {key}={value},"
|
|
288
|
+
|
|
283
289
|
dag_definition = f'''{doc_md_definition}dag = DAG(
|
|
284
290
|
'{self.dag_id}',
|
|
285
291
|
default_args={default_args_str},
|
|
286
292
|
description='{description}',
|
|
287
293
|
schedule={self._render_schedule(schedule)},
|
|
288
294
|
catchup=False,
|
|
289
|
-
tags={tags_str},{params_line}{doc_md_line}
|
|
295
|
+
tags={tags_str},{params_line}{doc_md_line}{concurrency_lines}
|
|
290
296
|
)
|
|
291
297
|
|
|
292
298
|
'''
|
|
@@ -27,4 +27,5 @@ codeflowhub/template/extract_voice_pkg/main.py
|
|
|
27
27
|
codeflowhub/template/read_pdf_pkg/__init__.py
|
|
28
28
|
codeflowhub/template/read_pdf_pkg/main.py
|
|
29
29
|
codeflowhub/template/transcript_pkg/__init__.py
|
|
30
|
-
codeflowhub/template/transcript_pkg/main.py
|
|
30
|
+
codeflowhub/template/transcript_pkg/main.py
|
|
31
|
+
tests/test_flow_concurrency.py
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""max_active_runs / max_active_tasks 검증·렌더 체크. 실행: python tests/test_flow_concurrency.py
|
|
2
|
+
export 는 끝나면 프로세스를 종료하므로 하위 프로세스로 돌린다."""
|
|
3
|
+
import os
|
|
4
|
+
import subprocess
|
|
5
|
+
import sys
|
|
6
|
+
import tempfile
|
|
7
|
+
|
|
8
|
+
ROOT = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..')
|
|
9
|
+
sys.path.insert(0, ROOT)
|
|
10
|
+
from codeflowhub.flow import FlowDecorator # noqa: E402
|
|
11
|
+
|
|
12
|
+
WF = '''
|
|
13
|
+
import sys
|
|
14
|
+
sys.path.insert(0, {root!r})
|
|
15
|
+
from codeflowhub import flow, task
|
|
16
|
+
|
|
17
|
+
@task(image='busybox')
|
|
18
|
+
def t1(args):
|
|
19
|
+
return args
|
|
20
|
+
|
|
21
|
+
@flow(name='concurrency-check', repo='git@example.com:x/y.git', path='.', export_destination={out!r}{kw})
|
|
22
|
+
def wf(args):
|
|
23
|
+
t1(args)
|
|
24
|
+
'''
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _export(kw=''):
|
|
28
|
+
out = tempfile.mkdtemp()
|
|
29
|
+
src = os.path.join(out, 'wf.py')
|
|
30
|
+
with open(src, 'w') as fh:
|
|
31
|
+
fh.write(WF.format(root=ROOT, out=out, kw=kw))
|
|
32
|
+
subprocess.run([sys.executable, src, '--export', 'airflow'], check=True, capture_output=True)
|
|
33
|
+
with open(os.path.join(out, 'concurrency-check_dag.py')) as fh:
|
|
34
|
+
return fh.read()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def main():
|
|
38
|
+
for bad in (0, -1, '8', True, 1.5):
|
|
39
|
+
try:
|
|
40
|
+
FlowDecorator._positive_int('x', bad)
|
|
41
|
+
raise AssertionError(f'not rejected: {bad!r}')
|
|
42
|
+
except ValueError:
|
|
43
|
+
pass
|
|
44
|
+
assert FlowDecorator._positive_int('x', None) is None
|
|
45
|
+
assert FlowDecorator._positive_int('x', 32) == 32
|
|
46
|
+
|
|
47
|
+
dag = _export(', max_active_runs=32, max_active_tasks=8')
|
|
48
|
+
assert 'max_active_runs=32,' in dag and 'max_active_tasks=8,' in dag, dag
|
|
49
|
+
dag = _export()
|
|
50
|
+
assert 'max_active_runs' not in dag and 'max_active_tasks' not in dag, dag
|
|
51
|
+
print('test_flow_concurrency OK')
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
if __name__ == '__main__':
|
|
55
|
+
main()
|
|
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
|
{codeflowhub-1.3.0 → codeflowhub-1.4.0}/codeflowhub/template/analyze_speaker_pkg/__init__.py
RENAMED
|
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
|