adag 0.1.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.
- adag-0.1.1/.gitignore +42 -0
- adag-0.1.1/CHANGELOG.md +12 -0
- adag-0.1.1/LICENSE +21 -0
- adag-0.1.1/PKG-INFO +131 -0
- adag-0.1.1/README.md +99 -0
- adag-0.1.1/pyproject.toml +155 -0
- adag-0.1.1/src/adag/__init__.py +109 -0
- adag-0.1.1/src/adag/__main__.py +6 -0
- adag-0.1.1/src/adag/base.py +126 -0
- adag-0.1.1/src/adag/batch.py +393 -0
- adag-0.1.1/src/adag/cache_store.py +359 -0
- adag-0.1.1/src/adag/cli.py +391 -0
- adag-0.1.1/src/adag/config.py +67 -0
- adag-0.1.1/src/adag/deferred.py +357 -0
- adag-0.1.1/src/adag/dir_source.py +75 -0
- adag-0.1.1/src/adag/dispatch.py +267 -0
- adag-0.1.1/src/adag/fan_out.py +250 -0
- adag-0.1.1/src/adag/hashing.py +127 -0
- adag-0.1.1/src/adag/io.py +171 -0
- adag-0.1.1/src/adag/item_cache.py +272 -0
- adag-0.1.1/src/adag/observers.py +140 -0
- adag-0.1.1/src/adag/output.py +30 -0
- adag-0.1.1/src/adag/parameter.py +33 -0
- adag-0.1.1/src/adag/pipeline.py +700 -0
- adag-0.1.1/src/adag/py.typed +0 -0
- adag-0.1.1/src/adag/serializable.py +123 -0
- adag-0.1.1/src/adag/sinks.py +91 -0
- adag-0.1.1/src/adag/step.py +793 -0
- adag-0.1.1/src/adag/step_mode.py +16 -0
- adag-0.1.1/src/adag/visualization.py +1400 -0
- adag-0.1.1/tests/test_async.py +239 -0
- adag-0.1.1/tests/test_base.py +83 -0
- adag-0.1.1/tests/test_batch.py +281 -0
- adag-0.1.1/tests/test_cache_store.py +152 -0
- adag-0.1.1/tests/test_cli.py +117 -0
- adag-0.1.1/tests/test_config.py +28 -0
- adag-0.1.1/tests/test_content_hash_caching.py +144 -0
- adag-0.1.1/tests/test_dispatch.py +200 -0
- adag-0.1.1/tests/test_executor.py +97 -0
- adag-0.1.1/tests/test_fan_out_concurrent.py +118 -0
- adag-0.1.1/tests/test_file_source.py +146 -0
- adag-0.1.1/tests/test_hashing.py +107 -0
- adag-0.1.1/tests/test_inflight_dedup.py +100 -0
- adag-0.1.1/tests/test_io_utils.py +64 -0
- adag-0.1.1/tests/test_item_cache.py +227 -0
- adag-0.1.1/tests/test_observer.py +149 -0
- adag-0.1.1/tests/test_observer_propagation.py +92 -0
- adag-0.1.1/tests/test_parallel_context_propagation.py +45 -0
- adag-0.1.1/tests/test_parallel_execution.py +84 -0
- adag-0.1.1/tests/test_parameter.py +140 -0
- adag-0.1.1/tests/test_pipeline_resolve.py +117 -0
- adag-0.1.1/tests/test_pipeline_roundtrip.py +135 -0
- adag-0.1.1/tests/test_pipeline_validation.py +78 -0
- adag-0.1.1/tests/test_sinks.py +118 -0
- adag-0.1.1/tests/test_step.py +699 -0
- adag-0.1.1/tests/test_visualization.py +328 -0
adag-0.1.1/.gitignore
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.eggs/
|
|
6
|
+
|
|
7
|
+
# Build artifacts
|
|
8
|
+
/dist/
|
|
9
|
+
/build/
|
|
10
|
+
/site/
|
|
11
|
+
/public/
|
|
12
|
+
|
|
13
|
+
# uv / venv
|
|
14
|
+
.venv/
|
|
15
|
+
uv.lock.bak
|
|
16
|
+
|
|
17
|
+
# Tool caches
|
|
18
|
+
.pytest_cache/
|
|
19
|
+
.ruff_cache/
|
|
20
|
+
.pyright/
|
|
21
|
+
.mypy_cache/
|
|
22
|
+
.moon/cache/
|
|
23
|
+
|
|
24
|
+
# Editor
|
|
25
|
+
.vscode/
|
|
26
|
+
.idea/
|
|
27
|
+
*.swp
|
|
28
|
+
|
|
29
|
+
# Local session logs
|
|
30
|
+
logs/
|
|
31
|
+
|
|
32
|
+
# Personal, machine-specific agent overrides (AGENTS.md is tracked)
|
|
33
|
+
AGENTS.local.md
|
|
34
|
+
CLAUDE.local.md
|
|
35
|
+
|
|
36
|
+
# Claude Code: per-machine local settings + worktrees are local; .claude/settings.json IS tracked.
|
|
37
|
+
.claude/settings.local.json
|
|
38
|
+
.claude/worktrees/
|
|
39
|
+
|
|
40
|
+
# Local agent wiring: names cabildo-issues-mcp, which is not a dependency of this
|
|
41
|
+
# package. docs/issues/ IS tracked — the register is plain markdown anyone can read.
|
|
42
|
+
.mcp.json
|
adag-0.1.1/CHANGELOG.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Initial release.
|
adag-0.1.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jorge Cardona
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
adag-0.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: adag
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Another DAG: a cached, content-addressed DAG engine for Python data pipelines.
|
|
5
|
+
Project-URL: Homepage, https://gitlab.com/cardona-oss/adag
|
|
6
|
+
Project-URL: Repository, https://gitlab.com/cardona-oss/adag
|
|
7
|
+
Project-URL: Documentation, https://cardona-oss.gitlab.io/adag/
|
|
8
|
+
Project-URL: Changelog, https://gitlab.com/cardona-oss/adag/-/blob/main/CHANGELOG.md
|
|
9
|
+
Project-URL: Issues, https://gitlab.com/cardona-oss/adag/-/issues
|
|
10
|
+
Author-email: Jorge Cardona <jorgeecardona@gmail.com>
|
|
11
|
+
License-Expression: MIT
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
18
|
+
Classifier: Typing :: Typed
|
|
19
|
+
Requires-Python: >=3.13
|
|
20
|
+
Requires-Dist: pydantic>=2.12
|
|
21
|
+
Requires-Dist: structlog
|
|
22
|
+
Requires-Dist: typing-extensions>=4.4
|
|
23
|
+
Provides-Extra: docs
|
|
24
|
+
Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
|
|
25
|
+
Requires-Dist: mkdocs>=1.6; extra == 'docs'
|
|
26
|
+
Requires-Dist: mkdocstrings[python]>=0.27; extra == 'docs'
|
|
27
|
+
Provides-Extra: redis
|
|
28
|
+
Requires-Dist: redis>=5.0; extra == 'redis'
|
|
29
|
+
Provides-Extra: sql
|
|
30
|
+
Requires-Dist: sqlalchemy>=2.0; extra == 'sql'
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# adag
|
|
34
|
+
|
|
35
|
+
[](https://gitlab.com/cardona-oss/adag/-/pipelines)
|
|
36
|
+
[](https://pypi.org/project/adag/)
|
|
37
|
+
[](https://pypi.org/project/adag/)
|
|
38
|
+
[](https://gitlab.com/cardona-oss/adag/-/blob/main/LICENSE)
|
|
39
|
+
|
|
40
|
+
Another DAG: a cached, content-addressed DAG engine for Python data pipelines.
|
|
41
|
+
|
|
42
|
+
## Install
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pip install adag
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Requires Python 3.13+.
|
|
49
|
+
|
|
50
|
+
## Quickstart
|
|
51
|
+
|
|
52
|
+
Each step is a frozen dataclass. Call a step to wire it into the graph, then run the
|
|
53
|
+
graph with a `Pipeline`.
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
from dataclasses import dataclass
|
|
57
|
+
from pathlib import Path
|
|
58
|
+
|
|
59
|
+
from pydantic import BaseModel
|
|
60
|
+
|
|
61
|
+
from adag import InputParam, Pipeline, Step
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class Number(BaseModel):
|
|
65
|
+
value: int
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@dataclass(frozen=True)
|
|
69
|
+
class Square(Step[Number]):
|
|
70
|
+
def run(self, x: int) -> Number:
|
|
71
|
+
print("computing square")
|
|
72
|
+
return Number(value=x * x)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@dataclass(frozen=True)
|
|
76
|
+
class AddOne(Step[Number]):
|
|
77
|
+
def run(self, n: Number) -> Number:
|
|
78
|
+
return Number(value=n.value + 1)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
x = InputParam[int]("x")
|
|
82
|
+
total = AddOne()(n=Square()(x=x))
|
|
83
|
+
pipeline = Pipeline(total, cache_dir=Path("cache"))
|
|
84
|
+
|
|
85
|
+
print(pipeline.execute(x=4)) # computes both steps
|
|
86
|
+
print(pipeline.execute(x=4)) # served from the cache
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The second run reads both results from `cache/` and does not call `Square.run` again:
|
|
90
|
+
|
|
91
|
+
```text
|
|
92
|
+
computing square
|
|
93
|
+
value=17
|
|
94
|
+
value=17
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
(output trimmed: structlog lines removed)
|
|
98
|
+
|
|
99
|
+
## Why adag
|
|
100
|
+
|
|
101
|
+
A step's cache key is a hash of its fields, its inputs, and its source code. Change any of
|
|
102
|
+
them and that step and everything downstream recompute; nothing else does. Use it for data
|
|
103
|
+
and ML pipelines where steps are slow and you rerun the graph often.
|
|
104
|
+
|
|
105
|
+
## Key features
|
|
106
|
+
|
|
107
|
+
- Content-addressed cache: a code edit invalidates only the steps it touches.
|
|
108
|
+
- Sync and async steps (`Step`, `AsyncStep`), with a parallel executor.
|
|
109
|
+
- `FanOutStep` runs a sub-graph per key; `BatchStep` caches each item on its own.
|
|
110
|
+
- Cache backends: filesystem, memory, SQLite, SQLAlchemy (`adag[sql]`), Redis (`adag[redis]`).
|
|
111
|
+
- Save a pipeline to disk and load it back with identical hashes.
|
|
112
|
+
- SVG rendering of the graph, and an `adag run pipeline.py` CLI.
|
|
113
|
+
- Run fan-out branches in subprocesses or AWS Batch jobs, joined through the cache.
|
|
114
|
+
|
|
115
|
+
## Links
|
|
116
|
+
|
|
117
|
+
- **Documentation:** https://cardona-oss.gitlab.io/adag/
|
|
118
|
+
- **Source:** https://gitlab.com/cardona-oss/adag
|
|
119
|
+
- **Issues:** https://gitlab.com/cardona-oss/adag/-/issues
|
|
120
|
+
- **PyPI:** https://pypi.org/project/adag/
|
|
121
|
+
- **Changelog:** [CHANGELOG.md](https://gitlab.com/cardona-oss/adag/-/blob/main/CHANGELOG.md)
|
|
122
|
+
|
|
123
|
+
## Contributing
|
|
124
|
+
|
|
125
|
+
Developer setup, the dev loop, and the release process live in
|
|
126
|
+
[CONTRIBUTING.md](https://gitlab.com/cardona-oss/adag/-/blob/main/CONTRIBUTING.md). In short: `uv sync && make install-hooks`, then
|
|
127
|
+
`make check` runs the same gate CI runs.
|
|
128
|
+
|
|
129
|
+
## License
|
|
130
|
+
|
|
131
|
+
MIT © 2026 Jorge Cardona (see [LICENSE](https://gitlab.com/cardona-oss/adag/-/blob/main/LICENSE)).
|
adag-0.1.1/README.md
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# adag
|
|
2
|
+
|
|
3
|
+
[](https://gitlab.com/cardona-oss/adag/-/pipelines)
|
|
4
|
+
[](https://pypi.org/project/adag/)
|
|
5
|
+
[](https://pypi.org/project/adag/)
|
|
6
|
+
[](https://gitlab.com/cardona-oss/adag/-/blob/main/LICENSE)
|
|
7
|
+
|
|
8
|
+
Another DAG: a cached, content-addressed DAG engine for Python data pipelines.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pip install adag
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Requires Python 3.13+.
|
|
17
|
+
|
|
18
|
+
## Quickstart
|
|
19
|
+
|
|
20
|
+
Each step is a frozen dataclass. Call a step to wire it into the graph, then run the
|
|
21
|
+
graph with a `Pipeline`.
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
from dataclasses import dataclass
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
|
|
27
|
+
from pydantic import BaseModel
|
|
28
|
+
|
|
29
|
+
from adag import InputParam, Pipeline, Step
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class Number(BaseModel):
|
|
33
|
+
value: int
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass(frozen=True)
|
|
37
|
+
class Square(Step[Number]):
|
|
38
|
+
def run(self, x: int) -> Number:
|
|
39
|
+
print("computing square")
|
|
40
|
+
return Number(value=x * x)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass(frozen=True)
|
|
44
|
+
class AddOne(Step[Number]):
|
|
45
|
+
def run(self, n: Number) -> Number:
|
|
46
|
+
return Number(value=n.value + 1)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
x = InputParam[int]("x")
|
|
50
|
+
total = AddOne()(n=Square()(x=x))
|
|
51
|
+
pipeline = Pipeline(total, cache_dir=Path("cache"))
|
|
52
|
+
|
|
53
|
+
print(pipeline.execute(x=4)) # computes both steps
|
|
54
|
+
print(pipeline.execute(x=4)) # served from the cache
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The second run reads both results from `cache/` and does not call `Square.run` again:
|
|
58
|
+
|
|
59
|
+
```text
|
|
60
|
+
computing square
|
|
61
|
+
value=17
|
|
62
|
+
value=17
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
(output trimmed: structlog lines removed)
|
|
66
|
+
|
|
67
|
+
## Why adag
|
|
68
|
+
|
|
69
|
+
A step's cache key is a hash of its fields, its inputs, and its source code. Change any of
|
|
70
|
+
them and that step and everything downstream recompute; nothing else does. Use it for data
|
|
71
|
+
and ML pipelines where steps are slow and you rerun the graph often.
|
|
72
|
+
|
|
73
|
+
## Key features
|
|
74
|
+
|
|
75
|
+
- Content-addressed cache: a code edit invalidates only the steps it touches.
|
|
76
|
+
- Sync and async steps (`Step`, `AsyncStep`), with a parallel executor.
|
|
77
|
+
- `FanOutStep` runs a sub-graph per key; `BatchStep` caches each item on its own.
|
|
78
|
+
- Cache backends: filesystem, memory, SQLite, SQLAlchemy (`adag[sql]`), Redis (`adag[redis]`).
|
|
79
|
+
- Save a pipeline to disk and load it back with identical hashes.
|
|
80
|
+
- SVG rendering of the graph, and an `adag run pipeline.py` CLI.
|
|
81
|
+
- Run fan-out branches in subprocesses or AWS Batch jobs, joined through the cache.
|
|
82
|
+
|
|
83
|
+
## Links
|
|
84
|
+
|
|
85
|
+
- **Documentation:** https://cardona-oss.gitlab.io/adag/
|
|
86
|
+
- **Source:** https://gitlab.com/cardona-oss/adag
|
|
87
|
+
- **Issues:** https://gitlab.com/cardona-oss/adag/-/issues
|
|
88
|
+
- **PyPI:** https://pypi.org/project/adag/
|
|
89
|
+
- **Changelog:** [CHANGELOG.md](https://gitlab.com/cardona-oss/adag/-/blob/main/CHANGELOG.md)
|
|
90
|
+
|
|
91
|
+
## Contributing
|
|
92
|
+
|
|
93
|
+
Developer setup, the dev loop, and the release process live in
|
|
94
|
+
[CONTRIBUTING.md](https://gitlab.com/cardona-oss/adag/-/blob/main/CONTRIBUTING.md). In short: `uv sync && make install-hooks`, then
|
|
95
|
+
`make check` runs the same gate CI runs.
|
|
96
|
+
|
|
97
|
+
## License
|
|
98
|
+
|
|
99
|
+
MIT © 2026 Jorge Cardona (see [LICENSE](https://gitlab.com/cardona-oss/adag/-/blob/main/LICENSE)).
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "adag"
|
|
3
|
+
dynamic = ["version"] # the single source of truth is __version__ in src/adag/__init__.py (see [tool.hatch.version])
|
|
4
|
+
description = "Another DAG: a cached, content-addressed DAG engine for Python data pipelines."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.13"
|
|
7
|
+
license = "MIT"
|
|
8
|
+
license-files = ["LICENSE"]
|
|
9
|
+
authors = [{ name = "Jorge Cardona", email = "jorgeecardona@gmail.com" }]
|
|
10
|
+
keywords = []
|
|
11
|
+
classifiers = [
|
|
12
|
+
"Development Status :: 4 - Beta",
|
|
13
|
+
"Intended Audience :: Developers",
|
|
14
|
+
"Programming Language :: Python :: 3",
|
|
15
|
+
"Programming Language :: Python :: 3.13",
|
|
16
|
+
"Topic :: Software Development :: Libraries",
|
|
17
|
+
"Typing :: Typed",
|
|
18
|
+
]
|
|
19
|
+
dependencies = [
|
|
20
|
+
"pydantic>=2.12",
|
|
21
|
+
"structlog",
|
|
22
|
+
"typing-extensions>=4.4",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[project.scripts]
|
|
26
|
+
adag = "adag.cli:main"
|
|
27
|
+
|
|
28
|
+
[project.optional-dependencies]
|
|
29
|
+
sql = ["sqlalchemy>=2.0"] # SqlCacheStore
|
|
30
|
+
redis = ["redis>=5.0"] # RedisCacheStore
|
|
31
|
+
# docs are an *extra* (not a uv dependency-group) so Read the Docs can install them via pip
|
|
32
|
+
docs = [
|
|
33
|
+
"mkdocs>=1.6",
|
|
34
|
+
"mkdocs-material>=9.5",
|
|
35
|
+
"mkdocstrings[python]>=0.27",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[project.urls]
|
|
39
|
+
Homepage = "https://gitlab.com/cardona-oss/adag"
|
|
40
|
+
Repository = "https://gitlab.com/cardona-oss/adag"
|
|
41
|
+
Documentation = "https://cardona-oss.gitlab.io/adag/"
|
|
42
|
+
Changelog = "https://gitlab.com/cardona-oss/adag/-/blob/main/CHANGELOG.md"
|
|
43
|
+
Issues = "https://gitlab.com/cardona-oss/adag/-/issues"
|
|
44
|
+
|
|
45
|
+
[build-system]
|
|
46
|
+
requires = ["hatchling"]
|
|
47
|
+
build-backend = "hatchling.build"
|
|
48
|
+
|
|
49
|
+
[tool.hatch.version]
|
|
50
|
+
# The version lives as __version__ in the package itself; hatchling reads it (by regex, no import) so
|
|
51
|
+
# pyproject and the runtime never disagree. scripts/bump-version rewrites that literal.
|
|
52
|
+
path = "src/adag/__init__.py"
|
|
53
|
+
|
|
54
|
+
[tool.hatch.build.targets.wheel]
|
|
55
|
+
packages = ["src/adag"]
|
|
56
|
+
|
|
57
|
+
[tool.hatch.build.targets.sdist]
|
|
58
|
+
include = ["src", "tests", "README.md", "CHANGELOG.md", "LICENSE"]
|
|
59
|
+
|
|
60
|
+
[tool.uv]
|
|
61
|
+
# The version is dynamic (hatchling reads __version__ from the file below), so uv must watch it too:
|
|
62
|
+
# declaring cache-keys REPLACES uv's defaults, so pyproject.toml is listed explicitly alongside it.
|
|
63
|
+
# Without __init__.py here, bumping the version would not trigger an editable reinstall.
|
|
64
|
+
cache-keys = [{ file = "pyproject.toml" }, { file = "src/adag/__init__.py" }]
|
|
65
|
+
|
|
66
|
+
[dependency-groups]
|
|
67
|
+
dev = ["ruff>=0.6", "pyright[nodejs]>=1.1.390", "pytest>=8", "sqlalchemy>=2.0"]
|
|
68
|
+
|
|
69
|
+
[tool.pytest.ini_options]
|
|
70
|
+
testpaths = ["tests"]
|
|
71
|
+
addopts = "-q"
|
|
72
|
+
|
|
73
|
+
[tool.ruff]
|
|
74
|
+
line-length = 100
|
|
75
|
+
target-version = "py313"
|
|
76
|
+
src = ["src", "tests"]
|
|
77
|
+
|
|
78
|
+
[tool.ruff.lint]
|
|
79
|
+
select = ["ALL"]
|
|
80
|
+
# The canonical base ignore list shared across the oss/ libraries. E501 (line length) is handled by
|
|
81
|
+
# the formatter; COM812/ISC001 conflict with the formatter; D1/D401 relax docstring presence/mood;
|
|
82
|
+
# TRY003/EM101/EM102 allow inline exception messages; CPY001 drops the copyright-header demand;
|
|
83
|
+
# PLC0415 allows non-top-level imports; TC001/TC002/TC003 don't force type-only import blocks.
|
|
84
|
+
ignore = [
|
|
85
|
+
"E501",
|
|
86
|
+
"COM812",
|
|
87
|
+
"ISC001",
|
|
88
|
+
"D1",
|
|
89
|
+
"D401",
|
|
90
|
+
"TRY003",
|
|
91
|
+
"EM101",
|
|
92
|
+
"EM102",
|
|
93
|
+
"CPY001",
|
|
94
|
+
"PLC0415",
|
|
95
|
+
"TC001",
|
|
96
|
+
"TC002",
|
|
97
|
+
"TC003",
|
|
98
|
+
# adag-specific: Step.run() takes **kwargs resolved from the graph, so Any is the honest type.
|
|
99
|
+
"ANN401",
|
|
100
|
+
# Multi-line docstring summaries are used throughout the imported engine.
|
|
101
|
+
"D205",
|
|
102
|
+
# Modules inside the package read each other's private helpers on purpose.
|
|
103
|
+
"SLF001",
|
|
104
|
+
# Step and Pipeline constructors take many keyword options by design.
|
|
105
|
+
"PLR0913",
|
|
106
|
+
"PLR0917",
|
|
107
|
+
"PLR0911",
|
|
108
|
+
"PLR0912",
|
|
109
|
+
"PLR0915",
|
|
110
|
+
"C901",
|
|
111
|
+
# Generic[...] classes still pair with typing_extensions.TypeVar(default=...).
|
|
112
|
+
"UP046",
|
|
113
|
+
"UP040",
|
|
114
|
+
# The engine catches any step failure to record it and re-raise or report it.
|
|
115
|
+
"BLE001",
|
|
116
|
+
"TRY300",
|
|
117
|
+
# Hook and protocol overrides keep the base signature even when an argument is unused.
|
|
118
|
+
"ARG002",
|
|
119
|
+
"ARG004",
|
|
120
|
+
# StepTimeout is public API; renaming it would break callers.
|
|
121
|
+
"N818",
|
|
122
|
+
# Cache paths are built as str and handed to os functions; converting is churn, not a fix.
|
|
123
|
+
"PTH",
|
|
124
|
+
]
|
|
125
|
+
|
|
126
|
+
[tool.ruff.lint.pydocstyle]
|
|
127
|
+
convention = "pep257"
|
|
128
|
+
|
|
129
|
+
[tool.ruff.lint.per-file-ignores]
|
|
130
|
+
# Tests may assert (S101), use magic values (PLR2004), take unused fixtures (ARG), pass Any to typed
|
|
131
|
+
# calls (ANN401), and group assertions pytest would otherwise split (PT018).
|
|
132
|
+
"tests/**/*.py" = ["S101", "PLR2004", "ARG", "ANN", "PT018", "PT011", "D", "T201", "S108", "RUF043", "RUF059", "RUF009", "N818", "BLE001", "S603", "FBT001"]
|
|
133
|
+
# The CLI and the run-tree observer print to stdout on purpose.
|
|
134
|
+
"src/adag/cli.py" = ["T201"]
|
|
135
|
+
"src/adag/observers.py" = ["T201"]
|
|
136
|
+
# SVG layout uses literal pixel and ratio constants; asserts guard layout invariants.
|
|
137
|
+
"src/adag/visualization.py" = ["PLR2004", "S101", "FBT001"]
|
|
138
|
+
# The worker command is built from our own module name, never from user input.
|
|
139
|
+
"src/adag/dispatch.py" = ["S603"]
|
|
140
|
+
|
|
141
|
+
[tool.ruff.lint.isort]
|
|
142
|
+
known-first-party = ["adag"]
|
|
143
|
+
|
|
144
|
+
[tool.pyright]
|
|
145
|
+
include = ["src", "tests"]
|
|
146
|
+
extraPaths = ["src"]
|
|
147
|
+
pythonVersion = "3.13"
|
|
148
|
+
typeCheckingMode = "strict"
|
|
149
|
+
venvPath = "."
|
|
150
|
+
venv = ".venv"
|
|
151
|
+
# Modules inside the package share private helpers and ResolveContext internals on purpose
|
|
152
|
+
# (same reason as ruff SLF001).
|
|
153
|
+
reportPrivateUsage = false
|
|
154
|
+
# Step subclasses narrow run(self, **kwargs: Any) into a typed signature on purpose.
|
|
155
|
+
reportIncompatibleMethodOverride = false
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""adag — content-addressable DAG engine for data pipelines.
|
|
2
|
+
|
|
3
|
+
Core engine:
|
|
4
|
+
Deferred, InputParam, ResolveContext — value abstraction
|
|
5
|
+
PipelineObserver, NullObserver — instrumentation hook on ResolveContext
|
|
6
|
+
LoggingObserver — default observer; logs each step's lifecycle
|
|
7
|
+
RunTreeObserver — renders a run as an indented timing tree
|
|
8
|
+
Executor, LocalExecutor — pluggable step execution on ResolveContext
|
|
9
|
+
AsyncExecutor, LocalAsyncExecutor — the awaitable counterparts (the async resolve path)
|
|
10
|
+
Step — single step base class
|
|
11
|
+
AsyncStep — Step whose body awaits I/O; resolve via Pipeline.aexecute()
|
|
12
|
+
CachedStep — Step with content-addressable caching
|
|
13
|
+
SerializableStep — Step with save/load support
|
|
14
|
+
StepResult — deferred result of a Step
|
|
15
|
+
FileSource, LinesSource, JSONLSource — file leaf Deferreds (text / lines / parsed)
|
|
16
|
+
Config, ItemCache — input/cache types
|
|
17
|
+
Pipeline — DAG executor
|
|
18
|
+
BatchItem, BatchStep — per-item batch processing with ItemCache
|
|
19
|
+
|
|
20
|
+
Backward compatibility:
|
|
21
|
+
Parameter — alias for InputParam
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
__version__ = "0.1.1"
|
|
25
|
+
|
|
26
|
+
from adag.base import CachedStep
|
|
27
|
+
from adag.batch import BatchItem, BatchStep
|
|
28
|
+
from adag.config import Config
|
|
29
|
+
from adag.deferred import (
|
|
30
|
+
AsyncExecutor,
|
|
31
|
+
BranchDispatcher,
|
|
32
|
+
BranchRef,
|
|
33
|
+
Deferred,
|
|
34
|
+
Executor,
|
|
35
|
+
InlineDispatcher,
|
|
36
|
+
InputParam,
|
|
37
|
+
LocalAsyncExecutor,
|
|
38
|
+
LocalExecutor,
|
|
39
|
+
NullObserver,
|
|
40
|
+
PipelineObserver,
|
|
41
|
+
Progress,
|
|
42
|
+
ResolveContext,
|
|
43
|
+
current_observer,
|
|
44
|
+
observing,
|
|
45
|
+
)
|
|
46
|
+
from adag.dir_source import DirSource
|
|
47
|
+
from adag.dispatch import BatchDispatcher, BranchExecutionError, BranchPolicy, SubprocessDispatcher
|
|
48
|
+
from adag.fan_out import FanOutBranchError, FanOutStep
|
|
49
|
+
from adag.io import FileSource, JSONLSource, LinesSource
|
|
50
|
+
from adag.item_cache import ItemCache
|
|
51
|
+
from adag.observers import LoggingObserver, RunTreeObserver
|
|
52
|
+
from adag.parameter import Parameter
|
|
53
|
+
from adag.pipeline import CyclicPipelineError, Pipeline, UnboundInputError
|
|
54
|
+
from adag.serializable import SerializableStep
|
|
55
|
+
from adag.sinks import WriteJSONL
|
|
56
|
+
from adag.step import AsyncStep, Step, StepResult, StepTimeout
|
|
57
|
+
from adag.step_mode import StepMode
|
|
58
|
+
from adag.visualization import SVG, SvgOptions, dag, render_pipeline_svg, walk
|
|
59
|
+
|
|
60
|
+
__all__ = [
|
|
61
|
+
"SVG",
|
|
62
|
+
"AsyncExecutor",
|
|
63
|
+
"AsyncStep",
|
|
64
|
+
"BatchDispatcher",
|
|
65
|
+
"BatchItem",
|
|
66
|
+
"BatchStep",
|
|
67
|
+
"BranchDispatcher",
|
|
68
|
+
"BranchExecutionError",
|
|
69
|
+
"BranchPolicy",
|
|
70
|
+
"BranchRef",
|
|
71
|
+
"CachedStep",
|
|
72
|
+
"Config",
|
|
73
|
+
"CyclicPipelineError",
|
|
74
|
+
"Deferred",
|
|
75
|
+
"DirSource",
|
|
76
|
+
"Executor",
|
|
77
|
+
"FanOutBranchError",
|
|
78
|
+
"FanOutStep",
|
|
79
|
+
"FileSource",
|
|
80
|
+
"InlineDispatcher",
|
|
81
|
+
"InputParam",
|
|
82
|
+
"ItemCache",
|
|
83
|
+
"JSONLSource",
|
|
84
|
+
"LinesSource",
|
|
85
|
+
"LocalAsyncExecutor",
|
|
86
|
+
"LocalExecutor",
|
|
87
|
+
"LoggingObserver",
|
|
88
|
+
"NullObserver",
|
|
89
|
+
"Parameter",
|
|
90
|
+
"Pipeline",
|
|
91
|
+
"PipelineObserver",
|
|
92
|
+
"Progress",
|
|
93
|
+
"ResolveContext",
|
|
94
|
+
"RunTreeObserver",
|
|
95
|
+
"SerializableStep",
|
|
96
|
+
"Step",
|
|
97
|
+
"StepMode",
|
|
98
|
+
"StepResult",
|
|
99
|
+
"StepTimeout",
|
|
100
|
+
"SubprocessDispatcher",
|
|
101
|
+
"SvgOptions",
|
|
102
|
+
"UnboundInputError",
|
|
103
|
+
"WriteJSONL",
|
|
104
|
+
"current_observer",
|
|
105
|
+
"dag",
|
|
106
|
+
"observing",
|
|
107
|
+
"render_pipeline_svg",
|
|
108
|
+
"walk",
|
|
109
|
+
]
|