wt-compiler 0.1.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.
- wt_compiler-0.1.0/PKG-INFO +301 -0
- wt_compiler-0.1.0/README.md +277 -0
- wt_compiler-0.1.0/pyproject.toml +128 -0
- wt_compiler-0.1.0/setup.cfg +4 -0
- wt_compiler-0.1.0/src/wt_compiler/__init__.py +68 -0
- wt_compiler-0.1.0/src/wt_compiler/__main__.py +6 -0
- wt_compiler-0.1.0/src/wt_compiler/_models.py +41 -0
- wt_compiler-0.1.0/src/wt_compiler/_version.py +34 -0
- wt_compiler-0.1.0/src/wt_compiler/artifacts.py +513 -0
- wt_compiler-0.1.0/src/wt_compiler/cli.py +157 -0
- wt_compiler-0.1.0/src/wt_compiler/compiler.py +1078 -0
- wt_compiler-0.1.0/src/wt_compiler/discovery.py +391 -0
- wt_compiler-0.1.0/src/wt_compiler/exceptions.py +227 -0
- wt_compiler-0.1.0/src/wt_compiler/formatting.py +62 -0
- wt_compiler-0.1.0/src/wt_compiler/jsonschema.py +268 -0
- wt_compiler-0.1.0/src/wt_compiler/progress.py +281 -0
- wt_compiler-0.1.0/src/wt_compiler/py.typed +0 -0
- wt_compiler-0.1.0/src/wt_compiler/requirements.py +267 -0
- wt_compiler-0.1.0/src/wt_compiler/spec.py +1006 -0
- wt_compiler-0.1.0/src/wt_compiler/templates/Dockerfile.jinja2 +30 -0
- wt_compiler-0.1.0/src/wt_compiler/templates/README.jinja2 +12 -0
- wt_compiler-0.1.0/src/wt_compiler/templates/dockerignore.jinja2 +4 -0
- wt_compiler-0.1.0/src/wt_compiler/templates/pixi.jinja2 +0 -0
- wt_compiler-0.1.0/src/wt_compiler/templates/pkg/cli.jinja2 +240 -0
- wt_compiler-0.1.0/src/wt_compiler/templates/pkg/dags/_macros.jinja2 +112 -0
- wt_compiler-0.1.0/src/wt_compiler/templates/pkg/dags/init.jinja2 +9 -0
- wt_compiler-0.1.0/src/wt_compiler/templates/pkg/dags/run_async.jinja2 +88 -0
- wt_compiler-0.1.0/src/wt_compiler/templates/pkg/dags/run_sequential.jinja2 +47 -0
- wt_compiler-0.1.0/src/wt_compiler/templates/pkg/dispatch.jinja2 +35 -0
- wt_compiler-0.1.0/src/wt_compiler/templates/pkg/metadata.jinja2 +68 -0
- wt_compiler-0.1.0/src/wt_compiler/templates/pkg/response.jinja2 +11 -0
- wt_compiler-0.1.0/src/wt_compiler/templates/tests/conftest.jinja2 +520 -0
- wt_compiler-0.1.0/src/wt_compiler/templates/tests/test_metadata.jinja2 +113 -0
- wt_compiler-0.1.0/src/wt_compiler/templates/tests/test_results.jinja2 +142 -0
- wt_compiler-0.1.0/src/wt_compiler/util.py +56 -0
- wt_compiler-0.1.0/src/wt_compiler/wizard/README.md +93 -0
- wt_compiler-0.1.0/src/wt_compiler/wizard/__init__.py +27 -0
- wt_compiler-0.1.0/src/wt_compiler/wizard/abstract.py +387 -0
- wt_compiler-0.1.0/src/wt_compiler/wizard/default.py +221 -0
- wt_compiler-0.1.0/src/wt_compiler/wizard/templates/.gitattributes.jinja2 +1 -0
- wt_compiler-0.1.0/src/wt_compiler/wizard/templates/.gitignore.jinja2 +15 -0
- wt_compiler-0.1.0/src/wt_compiler/wizard/templates/LICENSE.jinja2 +1 -0
- wt_compiler-0.1.0/src/wt_compiler/wizard/templates/README.md.jinja2 +11 -0
- wt_compiler-0.1.0/src/wt_compiler/wizard/templates/licenses/Apache-2.0.txt +191 -0
- wt_compiler-0.1.0/src/wt_compiler/wizard/templates/licenses/BSD-3-Clause.txt +28 -0
- wt_compiler-0.1.0/src/wt_compiler/wizard/templates/licenses/MIT.txt +21 -0
- wt_compiler-0.1.0/src/wt_compiler/wizard/templates/spec.yaml.jinja2 +11 -0
- wt_compiler-0.1.0/src/wt_compiler/wizard/templates/test-cases.yaml.jinja2 +16 -0
- wt_compiler-0.1.0/src/wt_compiler.egg-info/PKG-INFO +301 -0
- wt_compiler-0.1.0/src/wt_compiler.egg-info/SOURCES.txt +69 -0
- wt_compiler-0.1.0/src/wt_compiler.egg-info/dependency_links.txt +1 -0
- wt_compiler-0.1.0/src/wt_compiler.egg-info/entry_points.txt +2 -0
- wt_compiler-0.1.0/src/wt_compiler.egg-info/requires.txt +16 -0
- wt_compiler-0.1.0/src/wt_compiler.egg-info/top_level.txt +1 -0
- wt_compiler-0.1.0/tests/__init__.py +1 -0
- wt_compiler-0.1.0/tests/conftest.py +22 -0
- wt_compiler-0.1.0/tests/fixtures/simple_spec.yaml +26 -0
- wt_compiler-0.1.0/tests/test_artifacts.py +204 -0
- wt_compiler-0.1.0/tests/test_cli.py +281 -0
- wt_compiler-0.1.0/tests/test_compiler.py +840 -0
- wt_compiler-0.1.0/tests/test_discovery_integration.py +823 -0
- wt_compiler-0.1.0/tests/test_exceptions.py +157 -0
- wt_compiler-0.1.0/tests/test_jsonschema.py +159 -0
- wt_compiler-0.1.0/tests/test_progress.py +259 -0
- wt_compiler-0.1.0/tests/test_requirements.py +59 -0
- wt_compiler-0.1.0/tests/test_spec.py +580 -0
- wt_compiler-0.1.0/tests/test_wizard_abstract.py +171 -0
- wt_compiler-0.1.0/tests/test_wizard_cli_compat.py +303 -0
- wt_compiler-0.1.0/tests/test_wizard_default.py +303 -0
- wt_compiler-0.1.0/tests/test_wizard_extensibility.py +479 -0
- wt_compiler-0.1.0/uv.lock +855 -0
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: wt-compiler
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Workflow compiler for generating DAG artifacts from workflow specifications
|
|
5
|
+
Author: Ecoscope Team
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.12
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: wt-contracts<1.0.0,>=0.1.0
|
|
10
|
+
Requires-Dist: pydantic<3.0.0,>=2.0.0
|
|
11
|
+
Requires-Dist: jinja2>=3.0.0
|
|
12
|
+
Requires-Dist: ruamel.yaml>=0.17.0
|
|
13
|
+
Requires-Dist: py-rattler<0.23.0,>=0.22.0
|
|
14
|
+
Requires-Dist: datamodel-code-generator>=0.25.0
|
|
15
|
+
Requires-Dist: pydot>=1.4.0
|
|
16
|
+
Requires-Dist: ruff>=0.1.0
|
|
17
|
+
Requires-Dist: tomli-w>=1.0.0
|
|
18
|
+
Provides-Extra: dev
|
|
19
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
20
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
21
|
+
Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
|
|
22
|
+
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
23
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
24
|
+
|
|
25
|
+
# wt-compiler
|
|
26
|
+
|
|
27
|
+
Workflow compiler for generating DAG artifacts from workflow specifications.
|
|
28
|
+
|
|
29
|
+
## Overview
|
|
30
|
+
|
|
31
|
+
`wt-compiler` is a key component of the wt (workflow toolkit) ecosystem. It compiles workflow specifications (YAML files) into complete, executable workflow packages including:
|
|
32
|
+
|
|
33
|
+
- DAG Python code (async, sequential, and Jupytext variants)
|
|
34
|
+
- Pydantic parameter models with JSON schemas
|
|
35
|
+
- CLI interfaces for workflow execution
|
|
36
|
+
- Pixi configuration for dependency management
|
|
37
|
+
- Dockerfiles for containerized deployment
|
|
38
|
+
- Test suites
|
|
39
|
+
|
|
40
|
+
## Key Innovation: Environment-Isolated Task Discovery
|
|
41
|
+
|
|
42
|
+
Unlike legacy systems that require importing task libraries directly, `wt-compiler` uses **subprocess-based task discovery**:
|
|
43
|
+
|
|
44
|
+
1. Creates ephemeral rattler/pixi environments with specified requirements
|
|
45
|
+
2. Calls `wt-registry` CLI in that environment
|
|
46
|
+
3. Parses JSON output (validated against `wt-contracts` schemas)
|
|
47
|
+
4. Compiles workflows without Python import dependencies on task libraries
|
|
48
|
+
|
|
49
|
+
This enables:
|
|
50
|
+
- ✅ Cross-environment compilation (Python 3.10 compiler can target Python 3.12 tasks)
|
|
51
|
+
- ✅ Isolation from task library dependency conflicts
|
|
52
|
+
- ✅ Type-safe contracts via `wt-contracts` package
|
|
53
|
+
- ✅ No circular dependencies between packages
|
|
54
|
+
|
|
55
|
+
## Installation
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# From source (development)
|
|
59
|
+
cd wt/wt-compiler
|
|
60
|
+
uv sync
|
|
61
|
+
|
|
62
|
+
# Once published to PyPI
|
|
63
|
+
uv add wt-compiler
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Usage
|
|
67
|
+
|
|
68
|
+
### Basic Compilation
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
from wt_compiler import compile_workflow, Spec
|
|
72
|
+
from rattler import MatchSpec
|
|
73
|
+
|
|
74
|
+
# Load a workflow specification
|
|
75
|
+
spec = Spec.parse_file("workflow/spec.yaml")
|
|
76
|
+
|
|
77
|
+
# Compile to artifacts
|
|
78
|
+
artifacts = compile_workflow(
|
|
79
|
+
spec=spec,
|
|
80
|
+
spec_relpath="workflow/spec.yaml"
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# Write artifacts to disk
|
|
84
|
+
artifacts.dump(clobber=True)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Task Discovery
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
from wt_compiler.discovery import discover_tasks_from_requirements
|
|
91
|
+
from rattler import MatchSpec
|
|
92
|
+
|
|
93
|
+
# Discover tasks from requirements
|
|
94
|
+
requirements = [
|
|
95
|
+
MatchSpec("my-task-library>=1.0.0"),
|
|
96
|
+
MatchSpec("another-library>=2.0.0"),
|
|
97
|
+
]
|
|
98
|
+
|
|
99
|
+
tasks = discover_tasks_from_requirements(requirements)
|
|
100
|
+
# Returns: dict[task_name, dict[module_path, KnownTask]]
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Workflow Specification Format
|
|
104
|
+
|
|
105
|
+
```yaml
|
|
106
|
+
id: my-workflow
|
|
107
|
+
requirements:
|
|
108
|
+
- name: my-task-library
|
|
109
|
+
version: ">=1.0.0"
|
|
110
|
+
channel: conda-forge
|
|
111
|
+
|
|
112
|
+
workflow:
|
|
113
|
+
- id: task1
|
|
114
|
+
task: extract_data
|
|
115
|
+
partial:
|
|
116
|
+
source: "s3://my-bucket/data.csv"
|
|
117
|
+
|
|
118
|
+
- id: task2
|
|
119
|
+
task: transform_data
|
|
120
|
+
partial:
|
|
121
|
+
input_data: "${{ workflow.task1.return }}"
|
|
122
|
+
map:
|
|
123
|
+
argnames: param
|
|
124
|
+
argvalues: "${{ workflow.task1.return }}"
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Architecture
|
|
128
|
+
|
|
129
|
+
### Package Structure
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
wt-compiler/
|
|
133
|
+
├── src/wt_compiler/
|
|
134
|
+
│ ├── __init__.py # Public exports
|
|
135
|
+
│ ├── spec.py # Spec and TaskInstance models
|
|
136
|
+
│ ├── compiler.py # DagCompiler class
|
|
137
|
+
│ ├── discovery.py # Task discovery via rattler + CLI
|
|
138
|
+
│ ├── artifacts.py # Artifact generation models
|
|
139
|
+
│ ├── jsonschema.py # JSON schema utilities
|
|
140
|
+
│ ├── requirements.py # Rattler channel/matchspec handling
|
|
141
|
+
│ ├── util.py # Import validation utilities
|
|
142
|
+
│ ├── formatting.py # Ruff formatting decorator
|
|
143
|
+
│ ├── _models.py # Pydantic base classes
|
|
144
|
+
│ └── templates/ # Jinja2 templates
|
|
145
|
+
│ ├── pkg/
|
|
146
|
+
│ │ ├── dags/
|
|
147
|
+
│ │ │ ├── run_async.jinja2
|
|
148
|
+
│ │ │ ├── run_sequential.jinja2
|
|
149
|
+
│ │ │ └── jupytext.jinja2
|
|
150
|
+
│ │ ├── cli.jinja2
|
|
151
|
+
│ │ ├── dispatch.jinja2
|
|
152
|
+
│ │ └── ...
|
|
153
|
+
│ ├── tests/
|
|
154
|
+
│ ├── Dockerfile.jinja2
|
|
155
|
+
│ └── pixi.jinja2
|
|
156
|
+
└── tests/
|
|
157
|
+
├── test_spec.py
|
|
158
|
+
├── test_compiler.py
|
|
159
|
+
├── test_discovery.py
|
|
160
|
+
└── ...
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Dependencies
|
|
164
|
+
|
|
165
|
+
- **wt-contracts** (>=0.1.0): Shared type contracts (RegistryOutput, TaskProtocol, etc.)
|
|
166
|
+
- **pydantic** (>=2.0.0): Data validation and modeling
|
|
167
|
+
- **jinja2**: Template rendering
|
|
168
|
+
- **ruamel.yaml**: YAML parsing
|
|
169
|
+
- **rattler** (>=0.8.0): Conda environment management
|
|
170
|
+
- **datamodel-code-generator**: Generate Pydantic models from JSON schemas
|
|
171
|
+
- **pydot**: DAG visualization
|
|
172
|
+
|
|
173
|
+
## Implementation Status
|
|
174
|
+
|
|
175
|
+
### ✅ Completed Components
|
|
176
|
+
|
|
177
|
+
1. **Package Structure** - Full directory layout with src/ structure
|
|
178
|
+
2. **pyproject.toml** - setuptools-scm configuration, dependencies, tool configs
|
|
179
|
+
3. **spec.py** - Complete Spec, TaskInstance, and related models (~700 lines)
|
|
180
|
+
4. **discovery.py** - Task discovery via rattler + wt-registry CLI
|
|
181
|
+
5. **artifacts.py** - All artifact models (Dags, PixiToml, WorkflowArtifacts, etc.)
|
|
182
|
+
6. **requirements.py** - Channel and MatchSpec handling
|
|
183
|
+
7. **jsonschema.py** - JSON schema utilities with RJSF support
|
|
184
|
+
8. **util.py** - Import reference validation
|
|
185
|
+
9. **formatting.py** - Ruff formatting decorator
|
|
186
|
+
10. **_models.py** - Pydantic base model classes
|
|
187
|
+
11. **templates/** - All Jinja2 templates copied from legacy codebase
|
|
188
|
+
12. **compiler.py** - Core DagCompiler class structure
|
|
189
|
+
|
|
190
|
+
### ⚠️ Needs Expansion
|
|
191
|
+
|
|
192
|
+
The following areas are implemented as simplified stubs and need full implementation:
|
|
193
|
+
|
|
194
|
+
#### compiler.py TODOs
|
|
195
|
+
|
|
196
|
+
1. **get_params_jsonschema()** - Currently returns empty schema
|
|
197
|
+
- Needs: Extract schemas from discovered task metadata
|
|
198
|
+
- Needs: Merge schemas for task groups
|
|
199
|
+
- Needs: Apply omit_args logic
|
|
200
|
+
- Needs: Generate proper UI schema
|
|
201
|
+
- Needs: Apply RJSF overrides
|
|
202
|
+
|
|
203
|
+
2. **generate_params_model()** - Stub implementation
|
|
204
|
+
- Needs: Use datamodel-code-generator to create Pydantic model from JSON schema
|
|
205
|
+
- Needs: Proper imports and type hints
|
|
206
|
+
|
|
207
|
+
3. **Graph visualization** - Not implemented
|
|
208
|
+
- Needs: Generate pydot graphs showing task dependencies
|
|
209
|
+
- Needs: Export to PNG
|
|
210
|
+
|
|
211
|
+
4. **README generation** - Not implemented
|
|
212
|
+
- Needs: Generate README.md with fingerprint information
|
|
213
|
+
- Needs: Include workflow diagram, parameter documentation
|
|
214
|
+
|
|
215
|
+
5. **Version management** - Basic implementation only
|
|
216
|
+
- Needs: Full VERSION.yaml bump logic
|
|
217
|
+
- Needs: Lockfile carryover for updates
|
|
218
|
+
|
|
219
|
+
6. **get_per_taskinstance_params_notebook()** - Empty stub
|
|
220
|
+
- Needs: Generate parameter notebooks for Jupytext DAG
|
|
221
|
+
|
|
222
|
+
#### discovery.py TODOs
|
|
223
|
+
|
|
224
|
+
1. **rattler-py native API** - Currently uses subprocess fallback
|
|
225
|
+
- Needs: Update when rattler-py solve/install API is stable
|
|
226
|
+
- Needs: Better error handling
|
|
227
|
+
|
|
228
|
+
2. **Schema validation** - Basic validation only
|
|
229
|
+
- Needs: Full wt-contracts schema validation
|
|
230
|
+
- Needs: Better error messages for malformed CLI output
|
|
231
|
+
|
|
232
|
+
#### Testing
|
|
233
|
+
|
|
234
|
+
- **Unit tests** - Not yet written
|
|
235
|
+
- Need tests for: spec parsing, validation, compilation
|
|
236
|
+
- Need tests for: task discovery with mock environments
|
|
237
|
+
- Need tests for: artifact generation
|
|
238
|
+
- Need tests for: template rendering
|
|
239
|
+
|
|
240
|
+
## Development
|
|
241
|
+
|
|
242
|
+
### Setup
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
cd wt/wt-compiler
|
|
246
|
+
uv sync
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### Run Tests
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
uv run pytest
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Type Checking
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
uv run mypy src/wt_compiler
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Linting
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
uv run ruff check src/wt_compiler
|
|
265
|
+
uv run ruff format src/wt_compiler
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## Relationship to Other Packages
|
|
269
|
+
|
|
270
|
+
- **wt-contracts**: Depends on (provides type contracts)
|
|
271
|
+
- **wt-registry**: Called via subprocess (no Python dependency)
|
|
272
|
+
- **wt-task**: No dependency (generates code that uses it)
|
|
273
|
+
- **wt-runner**: No dependency (runner may depend on compiler in future)
|
|
274
|
+
- **wt-invokers**: No dependency
|
|
275
|
+
|
|
276
|
+
## Migration from Legacy
|
|
277
|
+
|
|
278
|
+
This package replaces `ecoscope_workflows_core.compiler`. Key differences:
|
|
279
|
+
|
|
280
|
+
1. **No direct task imports** - Uses CLI-based discovery instead
|
|
281
|
+
2. **wt-contracts integration** - Type-safe schemas for all interfaces
|
|
282
|
+
3. **Modular dependencies** - Only depends on wt-contracts
|
|
283
|
+
4. **Simplified models** - Spec models are now in spec.py instead of compiler.py
|
|
284
|
+
|
|
285
|
+
## Future Work
|
|
286
|
+
|
|
287
|
+
1. Complete all TODO areas in compiler.py
|
|
288
|
+
2. Write comprehensive test suite
|
|
289
|
+
3. Add CLI tool for standalone compilation
|
|
290
|
+
4. Add workflow visualization tools
|
|
291
|
+
5. Add workflow validation tools
|
|
292
|
+
6. Performance optimization for large workflows
|
|
293
|
+
7. Better error messages and debugging tools
|
|
294
|
+
|
|
295
|
+
## Contributing
|
|
296
|
+
|
|
297
|
+
See main wt repository CONTRIBUTING.md for guidelines.
|
|
298
|
+
|
|
299
|
+
## License
|
|
300
|
+
|
|
301
|
+
MIT
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
# wt-compiler
|
|
2
|
+
|
|
3
|
+
Workflow compiler for generating DAG artifacts from workflow specifications.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
`wt-compiler` is a key component of the wt (workflow toolkit) ecosystem. It compiles workflow specifications (YAML files) into complete, executable workflow packages including:
|
|
8
|
+
|
|
9
|
+
- DAG Python code (async, sequential, and Jupytext variants)
|
|
10
|
+
- Pydantic parameter models with JSON schemas
|
|
11
|
+
- CLI interfaces for workflow execution
|
|
12
|
+
- Pixi configuration for dependency management
|
|
13
|
+
- Dockerfiles for containerized deployment
|
|
14
|
+
- Test suites
|
|
15
|
+
|
|
16
|
+
## Key Innovation: Environment-Isolated Task Discovery
|
|
17
|
+
|
|
18
|
+
Unlike legacy systems that require importing task libraries directly, `wt-compiler` uses **subprocess-based task discovery**:
|
|
19
|
+
|
|
20
|
+
1. Creates ephemeral rattler/pixi environments with specified requirements
|
|
21
|
+
2. Calls `wt-registry` CLI in that environment
|
|
22
|
+
3. Parses JSON output (validated against `wt-contracts` schemas)
|
|
23
|
+
4. Compiles workflows without Python import dependencies on task libraries
|
|
24
|
+
|
|
25
|
+
This enables:
|
|
26
|
+
- ✅ Cross-environment compilation (Python 3.10 compiler can target Python 3.12 tasks)
|
|
27
|
+
- ✅ Isolation from task library dependency conflicts
|
|
28
|
+
- ✅ Type-safe contracts via `wt-contracts` package
|
|
29
|
+
- ✅ No circular dependencies between packages
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
# From source (development)
|
|
35
|
+
cd wt/wt-compiler
|
|
36
|
+
uv sync
|
|
37
|
+
|
|
38
|
+
# Once published to PyPI
|
|
39
|
+
uv add wt-compiler
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Usage
|
|
43
|
+
|
|
44
|
+
### Basic Compilation
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
from wt_compiler import compile_workflow, Spec
|
|
48
|
+
from rattler import MatchSpec
|
|
49
|
+
|
|
50
|
+
# Load a workflow specification
|
|
51
|
+
spec = Spec.parse_file("workflow/spec.yaml")
|
|
52
|
+
|
|
53
|
+
# Compile to artifacts
|
|
54
|
+
artifacts = compile_workflow(
|
|
55
|
+
spec=spec,
|
|
56
|
+
spec_relpath="workflow/spec.yaml"
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
# Write artifacts to disk
|
|
60
|
+
artifacts.dump(clobber=True)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Task Discovery
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from wt_compiler.discovery import discover_tasks_from_requirements
|
|
67
|
+
from rattler import MatchSpec
|
|
68
|
+
|
|
69
|
+
# Discover tasks from requirements
|
|
70
|
+
requirements = [
|
|
71
|
+
MatchSpec("my-task-library>=1.0.0"),
|
|
72
|
+
MatchSpec("another-library>=2.0.0"),
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
tasks = discover_tasks_from_requirements(requirements)
|
|
76
|
+
# Returns: dict[task_name, dict[module_path, KnownTask]]
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Workflow Specification Format
|
|
80
|
+
|
|
81
|
+
```yaml
|
|
82
|
+
id: my-workflow
|
|
83
|
+
requirements:
|
|
84
|
+
- name: my-task-library
|
|
85
|
+
version: ">=1.0.0"
|
|
86
|
+
channel: conda-forge
|
|
87
|
+
|
|
88
|
+
workflow:
|
|
89
|
+
- id: task1
|
|
90
|
+
task: extract_data
|
|
91
|
+
partial:
|
|
92
|
+
source: "s3://my-bucket/data.csv"
|
|
93
|
+
|
|
94
|
+
- id: task2
|
|
95
|
+
task: transform_data
|
|
96
|
+
partial:
|
|
97
|
+
input_data: "${{ workflow.task1.return }}"
|
|
98
|
+
map:
|
|
99
|
+
argnames: param
|
|
100
|
+
argvalues: "${{ workflow.task1.return }}"
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Architecture
|
|
104
|
+
|
|
105
|
+
### Package Structure
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
wt-compiler/
|
|
109
|
+
├── src/wt_compiler/
|
|
110
|
+
│ ├── __init__.py # Public exports
|
|
111
|
+
│ ├── spec.py # Spec and TaskInstance models
|
|
112
|
+
│ ├── compiler.py # DagCompiler class
|
|
113
|
+
│ ├── discovery.py # Task discovery via rattler + CLI
|
|
114
|
+
│ ├── artifacts.py # Artifact generation models
|
|
115
|
+
│ ├── jsonschema.py # JSON schema utilities
|
|
116
|
+
│ ├── requirements.py # Rattler channel/matchspec handling
|
|
117
|
+
│ ├── util.py # Import validation utilities
|
|
118
|
+
│ ├── formatting.py # Ruff formatting decorator
|
|
119
|
+
│ ├── _models.py # Pydantic base classes
|
|
120
|
+
│ └── templates/ # Jinja2 templates
|
|
121
|
+
│ ├── pkg/
|
|
122
|
+
│ │ ├── dags/
|
|
123
|
+
│ │ │ ├── run_async.jinja2
|
|
124
|
+
│ │ │ ├── run_sequential.jinja2
|
|
125
|
+
│ │ │ └── jupytext.jinja2
|
|
126
|
+
│ │ ├── cli.jinja2
|
|
127
|
+
│ │ ├── dispatch.jinja2
|
|
128
|
+
│ │ └── ...
|
|
129
|
+
│ ├── tests/
|
|
130
|
+
│ ├── Dockerfile.jinja2
|
|
131
|
+
│ └── pixi.jinja2
|
|
132
|
+
└── tests/
|
|
133
|
+
├── test_spec.py
|
|
134
|
+
├── test_compiler.py
|
|
135
|
+
├── test_discovery.py
|
|
136
|
+
└── ...
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Dependencies
|
|
140
|
+
|
|
141
|
+
- **wt-contracts** (>=0.1.0): Shared type contracts (RegistryOutput, TaskProtocol, etc.)
|
|
142
|
+
- **pydantic** (>=2.0.0): Data validation and modeling
|
|
143
|
+
- **jinja2**: Template rendering
|
|
144
|
+
- **ruamel.yaml**: YAML parsing
|
|
145
|
+
- **rattler** (>=0.8.0): Conda environment management
|
|
146
|
+
- **datamodel-code-generator**: Generate Pydantic models from JSON schemas
|
|
147
|
+
- **pydot**: DAG visualization
|
|
148
|
+
|
|
149
|
+
## Implementation Status
|
|
150
|
+
|
|
151
|
+
### ✅ Completed Components
|
|
152
|
+
|
|
153
|
+
1. **Package Structure** - Full directory layout with src/ structure
|
|
154
|
+
2. **pyproject.toml** - setuptools-scm configuration, dependencies, tool configs
|
|
155
|
+
3. **spec.py** - Complete Spec, TaskInstance, and related models (~700 lines)
|
|
156
|
+
4. **discovery.py** - Task discovery via rattler + wt-registry CLI
|
|
157
|
+
5. **artifacts.py** - All artifact models (Dags, PixiToml, WorkflowArtifacts, etc.)
|
|
158
|
+
6. **requirements.py** - Channel and MatchSpec handling
|
|
159
|
+
7. **jsonschema.py** - JSON schema utilities with RJSF support
|
|
160
|
+
8. **util.py** - Import reference validation
|
|
161
|
+
9. **formatting.py** - Ruff formatting decorator
|
|
162
|
+
10. **_models.py** - Pydantic base model classes
|
|
163
|
+
11. **templates/** - All Jinja2 templates copied from legacy codebase
|
|
164
|
+
12. **compiler.py** - Core DagCompiler class structure
|
|
165
|
+
|
|
166
|
+
### ⚠️ Needs Expansion
|
|
167
|
+
|
|
168
|
+
The following areas are implemented as simplified stubs and need full implementation:
|
|
169
|
+
|
|
170
|
+
#### compiler.py TODOs
|
|
171
|
+
|
|
172
|
+
1. **get_params_jsonschema()** - Currently returns empty schema
|
|
173
|
+
- Needs: Extract schemas from discovered task metadata
|
|
174
|
+
- Needs: Merge schemas for task groups
|
|
175
|
+
- Needs: Apply omit_args logic
|
|
176
|
+
- Needs: Generate proper UI schema
|
|
177
|
+
- Needs: Apply RJSF overrides
|
|
178
|
+
|
|
179
|
+
2. **generate_params_model()** - Stub implementation
|
|
180
|
+
- Needs: Use datamodel-code-generator to create Pydantic model from JSON schema
|
|
181
|
+
- Needs: Proper imports and type hints
|
|
182
|
+
|
|
183
|
+
3. **Graph visualization** - Not implemented
|
|
184
|
+
- Needs: Generate pydot graphs showing task dependencies
|
|
185
|
+
- Needs: Export to PNG
|
|
186
|
+
|
|
187
|
+
4. **README generation** - Not implemented
|
|
188
|
+
- Needs: Generate README.md with fingerprint information
|
|
189
|
+
- Needs: Include workflow diagram, parameter documentation
|
|
190
|
+
|
|
191
|
+
5. **Version management** - Basic implementation only
|
|
192
|
+
- Needs: Full VERSION.yaml bump logic
|
|
193
|
+
- Needs: Lockfile carryover for updates
|
|
194
|
+
|
|
195
|
+
6. **get_per_taskinstance_params_notebook()** - Empty stub
|
|
196
|
+
- Needs: Generate parameter notebooks for Jupytext DAG
|
|
197
|
+
|
|
198
|
+
#### discovery.py TODOs
|
|
199
|
+
|
|
200
|
+
1. **rattler-py native API** - Currently uses subprocess fallback
|
|
201
|
+
- Needs: Update when rattler-py solve/install API is stable
|
|
202
|
+
- Needs: Better error handling
|
|
203
|
+
|
|
204
|
+
2. **Schema validation** - Basic validation only
|
|
205
|
+
- Needs: Full wt-contracts schema validation
|
|
206
|
+
- Needs: Better error messages for malformed CLI output
|
|
207
|
+
|
|
208
|
+
#### Testing
|
|
209
|
+
|
|
210
|
+
- **Unit tests** - Not yet written
|
|
211
|
+
- Need tests for: spec parsing, validation, compilation
|
|
212
|
+
- Need tests for: task discovery with mock environments
|
|
213
|
+
- Need tests for: artifact generation
|
|
214
|
+
- Need tests for: template rendering
|
|
215
|
+
|
|
216
|
+
## Development
|
|
217
|
+
|
|
218
|
+
### Setup
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
cd wt/wt-compiler
|
|
222
|
+
uv sync
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Run Tests
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
uv run pytest
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Type Checking
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
uv run mypy src/wt_compiler
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Linting
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
uv run ruff check src/wt_compiler
|
|
241
|
+
uv run ruff format src/wt_compiler
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Relationship to Other Packages
|
|
245
|
+
|
|
246
|
+
- **wt-contracts**: Depends on (provides type contracts)
|
|
247
|
+
- **wt-registry**: Called via subprocess (no Python dependency)
|
|
248
|
+
- **wt-task**: No dependency (generates code that uses it)
|
|
249
|
+
- **wt-runner**: No dependency (runner may depend on compiler in future)
|
|
250
|
+
- **wt-invokers**: No dependency
|
|
251
|
+
|
|
252
|
+
## Migration from Legacy
|
|
253
|
+
|
|
254
|
+
This package replaces `ecoscope_workflows_core.compiler`. Key differences:
|
|
255
|
+
|
|
256
|
+
1. **No direct task imports** - Uses CLI-based discovery instead
|
|
257
|
+
2. **wt-contracts integration** - Type-safe schemas for all interfaces
|
|
258
|
+
3. **Modular dependencies** - Only depends on wt-contracts
|
|
259
|
+
4. **Simplified models** - Spec models are now in spec.py instead of compiler.py
|
|
260
|
+
|
|
261
|
+
## Future Work
|
|
262
|
+
|
|
263
|
+
1. Complete all TODO areas in compiler.py
|
|
264
|
+
2. Write comprehensive test suite
|
|
265
|
+
3. Add CLI tool for standalone compilation
|
|
266
|
+
4. Add workflow visualization tools
|
|
267
|
+
5. Add workflow validation tools
|
|
268
|
+
6. Performance optimization for large workflows
|
|
269
|
+
7. Better error messages and debugging tools
|
|
270
|
+
|
|
271
|
+
## Contributing
|
|
272
|
+
|
|
273
|
+
See main wt repository CONTRIBUTING.md for guidelines.
|
|
274
|
+
|
|
275
|
+
## License
|
|
276
|
+
|
|
277
|
+
MIT
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=64", "setuptools-scm>=8"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "wt-compiler"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Workflow compiler for generating DAG artifacts from workflow specifications"
|
|
9
|
+
requires-python = ">=3.12"
|
|
10
|
+
readme = "README.md"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Ecoscope Team" }
|
|
14
|
+
]
|
|
15
|
+
dependencies = [
|
|
16
|
+
"wt-contracts>=0.1.0,<1.0.0",
|
|
17
|
+
"pydantic>=2.0.0,<3.0.0",
|
|
18
|
+
"jinja2>=3.0.0",
|
|
19
|
+
"ruamel.yaml>=0.17.0",
|
|
20
|
+
"py-rattler>=0.22.0,<0.23.0",
|
|
21
|
+
"datamodel-code-generator>=0.25.0",
|
|
22
|
+
"pydot>=1.4.0",
|
|
23
|
+
"ruff>=0.1.0",
|
|
24
|
+
"tomli-w>=1.0.0",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[project.scripts]
|
|
28
|
+
wt-compiler = "wt_compiler.cli:main"
|
|
29
|
+
|
|
30
|
+
[project.optional-dependencies]
|
|
31
|
+
dev = [
|
|
32
|
+
"pytest>=7.0.0",
|
|
33
|
+
"pytest-cov>=4.0.0",
|
|
34
|
+
"pytest-asyncio>=0.23.0",
|
|
35
|
+
"mypy>=1.0.0",
|
|
36
|
+
"ruff>=0.1.0",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
[dependency-groups]
|
|
40
|
+
dev = [
|
|
41
|
+
"pytest>=7.0.0",
|
|
42
|
+
"pytest-cov>=4.0.0",
|
|
43
|
+
"pytest-asyncio>=0.23.0",
|
|
44
|
+
"mypy>=1.0.0",
|
|
45
|
+
"ruff>=0.1.0",
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
[tool.setuptools.packages.find]
|
|
49
|
+
where = ["src"]
|
|
50
|
+
|
|
51
|
+
[tool.setuptools_scm]
|
|
52
|
+
# Version comes from git tags matching this pattern
|
|
53
|
+
tag_regex = "^wt-compiler/v(?P<version>[0-9.]+)$"
|
|
54
|
+
# Root is the git repository root (parent of wt/)
|
|
55
|
+
root = ".."
|
|
56
|
+
# Generate version file
|
|
57
|
+
version_file = "src/wt_compiler/_version.py"
|
|
58
|
+
git_describe_command = ["git", "describe", "--dirty", "--tags", "--long", "--match", "wt-compiler/v*"]
|
|
59
|
+
|
|
60
|
+
[tool.uv.sources]
|
|
61
|
+
# Development override - uses local editable wt-contracts
|
|
62
|
+
# Published packages will reference PyPI version normally
|
|
63
|
+
wt-contracts = { path = "../wt-contracts", editable = true }
|
|
64
|
+
|
|
65
|
+
[tool.pytest.ini_options]
|
|
66
|
+
testpaths = ["tests"]
|
|
67
|
+
pythonpath = ["tests"]
|
|
68
|
+
python_files = ["test_*.py"]
|
|
69
|
+
addopts = "--cov=wt_compiler --cov-report=term-missing"
|
|
70
|
+
asyncio_mode = "auto"
|
|
71
|
+
|
|
72
|
+
[tool.mypy]
|
|
73
|
+
strict = true
|
|
74
|
+
python_version = "3.12"
|
|
75
|
+
warn_return_any = true
|
|
76
|
+
warn_unused_configs = true
|
|
77
|
+
disallow_untyped_defs = true
|
|
78
|
+
|
|
79
|
+
[tool.ruff]
|
|
80
|
+
line-length = 100
|
|
81
|
+
target-version = "py312"
|
|
82
|
+
|
|
83
|
+
[tool.ruff.lint]
|
|
84
|
+
select = [
|
|
85
|
+
"E", # pycodestyle errors
|
|
86
|
+
"W", # pycodestyle warnings
|
|
87
|
+
"F", # pyflakes
|
|
88
|
+
"I", # isort
|
|
89
|
+
"B", # flake8-bugbear
|
|
90
|
+
"C4", # flake8-comprehensions
|
|
91
|
+
"UP", # pyupgrade
|
|
92
|
+
]
|
|
93
|
+
ignore = []
|
|
94
|
+
|
|
95
|
+
[tool.ruff.lint.per-file-ignores]
|
|
96
|
+
"__init__.py" = ["F401"] # Allow unused imports in __init__.py
|
|
97
|
+
|
|
98
|
+
# ============================================================================
|
|
99
|
+
# Pixi Build Configuration (conda package building)
|
|
100
|
+
# ============================================================================
|
|
101
|
+
|
|
102
|
+
[tool.pixi.package]
|
|
103
|
+
name = "wt-compiler"
|
|
104
|
+
version = "0.1.0"
|
|
105
|
+
|
|
106
|
+
[tool.pixi.package.build]
|
|
107
|
+
backend = { name = "pixi-build-python", version = "*", channels = ["conda-forge", "https://prefix.dev/pixi-build-backends"] }
|
|
108
|
+
|
|
109
|
+
[tool.pixi.package.build.config]
|
|
110
|
+
noarch = true
|
|
111
|
+
|
|
112
|
+
[tool.pixi.package.host-dependencies]
|
|
113
|
+
python = ">=3.12,<3.16"
|
|
114
|
+
setuptools = ">=64"
|
|
115
|
+
setuptools-scm = ">=8"
|
|
116
|
+
pip = "*"
|
|
117
|
+
|
|
118
|
+
[tool.pixi.package.run-dependencies]
|
|
119
|
+
python = ">=3.12,<3.16"
|
|
120
|
+
pydantic = ">=2.0.0,<3.0.0"
|
|
121
|
+
jinja2 = ">=3.0.0"
|
|
122
|
+
"ruamel.yaml" = ">=0.17.0"
|
|
123
|
+
py-rattler = ">=0.22.0,<0.23.0"
|
|
124
|
+
datamodel-code-generator = ">=0.25.0"
|
|
125
|
+
pydot = ">=1.4.0"
|
|
126
|
+
ruff = ">=0.1.0"
|
|
127
|
+
tomli-w = ">=1.0.0"
|
|
128
|
+
wt-contracts = ">=0.1.0,<1.0.0"
|