scientific-workflow 0.4.4__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.
- scientific_workflow-0.4.4/LICENSE +21 -0
- scientific_workflow-0.4.4/PKG-INFO +227 -0
- scientific_workflow-0.4.4/README.md +205 -0
- scientific_workflow-0.4.4/pyproject.toml +36 -0
- scientific_workflow-0.4.4/setup.cfg +4 -0
- scientific_workflow-0.4.4/src/scientific_workflow/__init__.py +39 -0
- scientific_workflow-0.4.4/src/scientific_workflow/_control.py +59 -0
- scientific_workflow-0.4.4/src/scientific_workflow/api.md +221 -0
- scientific_workflow-0.4.4/src/scientific_workflow/dependencies.py +198 -0
- scientific_workflow-0.4.4/src/scientific_workflow/errors.py +29 -0
- scientific_workflow-0.4.4/src/scientific_workflow/npy.py +1449 -0
- scientific_workflow-0.4.4/src/scientific_workflow/project.py +56 -0
- scientific_workflow-0.4.4/src/scientific_workflow/py.typed +1 -0
- scientific_workflow-0.4.4/src/scientific_workflow/reader.py +580 -0
- scientific_workflow-0.4.4/src/scientific_workflow/reporting.py +61 -0
- scientific_workflow-0.4.4/src/scientific_workflow/state.py +55 -0
- scientific_workflow-0.4.4/src/scientific_workflow.egg-info/PKG-INFO +227 -0
- scientific_workflow-0.4.4/src/scientific_workflow.egg-info/SOURCES.txt +23 -0
- scientific_workflow-0.4.4/src/scientific_workflow.egg-info/dependency_links.txt +1 -0
- scientific_workflow-0.4.4/src/scientific_workflow.egg-info/entry_points.txt +2 -0
- scientific_workflow-0.4.4/src/scientific_workflow.egg-info/requires.txt +4 -0
- scientific_workflow-0.4.4/src/scientific_workflow.egg-info/top_level.txt +1 -0
- scientific_workflow-0.4.4/tests/test_npy.py +486 -0
- scientific_workflow-0.4.4/tests/test_reader.py +265 -0
- scientific_workflow-0.4.4/tests/test_utilities.py +52 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Dingyi Sun
|
|
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.
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: scientific-workflow
|
|
3
|
+
Version: 0.4.4
|
|
4
|
+
Summary: Python utilities, verified readers, and optional NumPy conversion for Scientific Workflow
|
|
5
|
+
Author: Dingyi Sun
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/dingyisun0101/Scientific-Workflow
|
|
8
|
+
Project-URL: Repository, https://github.com/dingyisun0101/Scientific-Workflow
|
|
9
|
+
Keywords: science,workflow,recording,jsonl,reproducibility
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering
|
|
15
|
+
Requires-Python: >=3.14
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Provides-Extra: npy
|
|
19
|
+
Requires-Dist: numpy>=1.26; extra == "npy"
|
|
20
|
+
Requires-Dist: threadpoolctl>=3; extra == "npy"
|
|
21
|
+
Dynamic: license-file
|
|
22
|
+
|
|
23
|
+
# Scientific Workflow Python utilities
|
|
24
|
+
|
|
25
|
+
> **BREAKING IMPORT CHANGE — 0.4.4:** use `scientific_workflow`; the old
|
|
26
|
+
> `scientific_workflow_reader` namespace is not provided.
|
|
27
|
+
> **LINUX ONLY. Python 3.14+ REQUIRED.** Activate the environment containing
|
|
28
|
+
> `scientific-workflow[npy]` before every Workflow launch, in every new shell.
|
|
29
|
+
> **REQUIRED LAYOUT:** keep `<study>/wf_configs/study.json` and `parameters.json`.
|
|
30
|
+
> Standard accessors do not discover renamed or relocated files.
|
|
31
|
+
|
|
32
|
+
[Complete structured API reference](src/scientific_workflow/api.md).
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
`scientific-workflow` is the official Python reader for completed
|
|
36
|
+
Scientific Workflow recordings. It implements the same versioned metadata,
|
|
37
|
+
JSONL framing, lifecycle, schema, ordering, and mandatory chunk-integrity
|
|
38
|
+
contract as Workflow's Rust `StoredStateSeriesReader`.
|
|
39
|
+
|
|
40
|
+
The reader is intentionally eager. A stream is returned only after all selected
|
|
41
|
+
chunks pass byte-count, SHA-256, JSON record, field, descriptor, and iteration
|
|
42
|
+
validation. A failure never returns a partial scientific series.
|
|
43
|
+
|
|
44
|
+
## Installation
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
python3.14 -m venv .venv
|
|
48
|
+
source .venv/bin/activate
|
|
49
|
+
python -m pip install \
|
|
50
|
+
"scientific-workflow @ git+https://github.com/dingyisun0101/Scientific-Workflow.git@v0.13.8#subdirectory=python"
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Python 3.14 or newer is required. The core reader has no runtime dependencies.
|
|
54
|
+
Install the optional NumPy converter when a project uses Workflow's reserved
|
|
55
|
+
`$npy` phase or when converting a recording directly:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
python -m pip install \
|
|
59
|
+
"scientific-workflow[npy] @ git+https://github.com/dingyisun0101/Scientific-Workflow.git@v0.13.8#subdirectory=python"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
This guide documents release 0.4.4.
|
|
63
|
+
|
|
64
|
+
## Reading a recording
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from scientific_workflow import open_completed_recording
|
|
68
|
+
|
|
69
|
+
reader = open_completed_recording("results/study/recordings/task-000000")
|
|
70
|
+
print(reader.stream_names)
|
|
71
|
+
print(reader.user_metadata)
|
|
72
|
+
print(reader.terminal_metadata)
|
|
73
|
+
|
|
74
|
+
signal = reader.read_stream("signal")
|
|
75
|
+
for state in signal:
|
|
76
|
+
print(state.iteration, state.physical_time, state.values["abundance"])
|
|
77
|
+
|
|
78
|
+
latest = reader.read_latest("checkpoint")
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
JSON payloads reconstruct into ordinary Python scalars, lists, and dictionaries
|
|
82
|
+
by default. Applications may supply explicit per-field decoders without making
|
|
83
|
+
the storage package depend on NumPy:
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
import numpy as np
|
|
87
|
+
from scientific_workflow import open_completed_recording
|
|
88
|
+
|
|
89
|
+
reader = open_completed_recording(
|
|
90
|
+
"recording",
|
|
91
|
+
decoders={
|
|
92
|
+
"abundance": np.asarray,
|
|
93
|
+
"space": np.asarray,
|
|
94
|
+
"total": float,
|
|
95
|
+
},
|
|
96
|
+
)
|
|
97
|
+
checkpoint = reader.read_latest("checkpoint")
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
When a decoder mapping is supplied, every selected stream field must have a
|
|
101
|
+
decoder. Decoder failures are reported as `DecoderError` with the original
|
|
102
|
+
exception chained as their cause.
|
|
103
|
+
|
|
104
|
+
## Public API
|
|
105
|
+
|
|
106
|
+
- `open_completed_recording(path, decoders=None)`
|
|
107
|
+
- `RecordingReader`
|
|
108
|
+
- `stream_names`, `user_metadata`, `terminal_metadata`, and `timing`
|
|
109
|
+
- `stream_record_count(name)` and `stream_encoded_bytes(name)`
|
|
110
|
+
- `read_stream(name)`, `read_all_streams()`, and `read_latest(name)`
|
|
111
|
+
- `iter_verified_records(name)` for bounded-memory incremental consumers
|
|
112
|
+
- structurally read-only `StateField`, `StateRecord`, and `StateSeries`
|
|
113
|
+
- typed exceptions rooted at `RecordingError`
|
|
114
|
+
|
|
115
|
+
Release 0.4.4 supports
|
|
116
|
+
`scientific-workflow-jsonl` format versions 7 and 8, positional JSON payload encoding, JSON Lines
|
|
117
|
+
framing, and `sha256:` chunk checksums. Unknown versions and algorithms fail
|
|
118
|
+
closed.
|
|
119
|
+
|
|
120
|
+
The normative language-neutral contract is the repository's
|
|
121
|
+
[recording v7 protocol](https://github.com/dingyisun0101/Scientific-Workflow/blob/v0.13.8/protocol/recording-v7.md),
|
|
122
|
+
with a strict structural JSON Schema and a package
|
|
123
|
+
[compatibility matrix](https://github.com/dingyisun0101/Scientific-Workflow/blob/v0.13.8/protocol/compatibility.md). This package is the
|
|
124
|
+
v7/v8 reader listed there; it does not expose a supported writer.
|
|
125
|
+
|
|
126
|
+
The record containers cannot be reassigned and their value mappings are
|
|
127
|
+
read-only. Decoded payload objects retain the type and mutability chosen by
|
|
128
|
+
JSON decoding or by the caller's field decoder.
|
|
129
|
+
|
|
130
|
+
`read_stream()` is transactional: it returns a complete series or nothing.
|
|
131
|
+
`iter_verified_records()` verifies an entire bounded chunk before yielding its
|
|
132
|
+
first record, but a later chunk may fail after earlier records were consumed.
|
|
133
|
+
Incremental callers should therefore write only to private temporary storage
|
|
134
|
+
and publish it after iteration completes successfully.
|
|
135
|
+
|
|
136
|
+
## Integrity boundary
|
|
137
|
+
|
|
138
|
+
The reader validates:
|
|
139
|
+
|
|
140
|
+
- successful recording completion;
|
|
141
|
+
- strict metadata and record keys;
|
|
142
|
+
- safe relative stream and chunk paths;
|
|
143
|
+
- deterministic chunk names and contiguous ordinals;
|
|
144
|
+
- declared byte lengths and mandatory SHA-256 checksums;
|
|
145
|
+
- newline-terminated, nonempty JSONL records;
|
|
146
|
+
- exact stream field coverage;
|
|
147
|
+
- record counts and first/last iterations; and
|
|
148
|
+
- strictly increasing iterations across chunk boundaries.
|
|
149
|
+
|
|
150
|
+
Checksums detect storage corruption and accidental alteration. They do not
|
|
151
|
+
establish scientific correctness, authorship, or cryptographic authenticity.
|
|
152
|
+
|
|
153
|
+
The fixture under `tests/fixtures/complete` is also opened by Workflow's Rust
|
|
154
|
+
reader, making it the shared cross-language golden example. The normative
|
|
155
|
+
protocol and schema remain authoritative over any individual fixture.
|
|
156
|
+
|
|
157
|
+
Workflow's Rust integration suite additionally runs a bidirectional test. Its
|
|
158
|
+
internal automatic persistence path produces a multi-chunk recording for this
|
|
159
|
+
package to read; the test-only Python bridge re-encodes those records; and the
|
|
160
|
+
public Rust reader validates the Python result, including exact
|
|
161
|
+
sensitive-float bits and Unicode. The Rust write session and Python bridge are
|
|
162
|
+
test infrastructure, not supported writer APIs.
|
|
163
|
+
|
|
164
|
+
## NumPy conversion
|
|
165
|
+
|
|
166
|
+
The optional converter verifies one completed recording through the official
|
|
167
|
+
reader and converts every field into manifest-directed, C-contiguous `.npy`
|
|
168
|
+
data. Fixed-shape numeric fields map directly. Changing numeric shapes use
|
|
169
|
+
packed data, offsets, and shapes. Structured fields use canonical UTF-8 JSON
|
|
170
|
+
bytes plus offsets as a lossless fallback and expose every stable nested
|
|
171
|
+
numeric value as a fixed or ragged projection. Conversion never writes NumPy
|
|
172
|
+
object arrays and never enables pickle.
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
scientific-workflow-to-npy path/to/member-recording
|
|
176
|
+
scientific-workflow-to-npy path/to/member-recording --output path/to/processed
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
The equivalent module entry point is
|
|
180
|
+
`python -m scientific_workflow.npy`. With no `--output`, conversion uses
|
|
181
|
+
a sibling directory named `<recording>-npy`. It never writes inside or modifies
|
|
182
|
+
the raw recording. Conversion uses bounded-memory verified iteration, writes a
|
|
183
|
+
private temporary directory, atomically publishes the completed result, and
|
|
184
|
+
resumes an existing result only when its manifest and arrays still match.
|
|
185
|
+
From a source checkout, the source-tree launcher script is
|
|
186
|
+
`python/scripts/recording_to_npy.py`; it adds the adjacent package source and
|
|
187
|
+
accepts the same recording and `--output` arguments.
|
|
188
|
+
|
|
189
|
+
Python callers may use
|
|
190
|
+
`scientific_workflow.npy.convert_recording(recording, output=None)`.
|
|
191
|
+
The resulting member directory is valid only with its required
|
|
192
|
+
`manifest.json`. Open and verify it before accessing arrays:
|
|
193
|
+
|
|
194
|
+
```python
|
|
195
|
+
from scientific_workflow.npy import open_npy_conversion
|
|
196
|
+
|
|
197
|
+
converted = open_npy_conversion("path/to/processed/member-000000")
|
|
198
|
+
latest = converted.reconstruct("state", "label", 4)
|
|
199
|
+
positions = converted.projection("particles", "particles", "/attributes/r", 4)
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
`NpyConversion.array()` memory-maps a declared component,
|
|
203
|
+
`field()` returns its representation metadata, `reconstruct()` reads one whole
|
|
204
|
+
field record, and `projection()` reads one nested numeric path. Component
|
|
205
|
+
relationships and paths always come from the manifest, never directory scans
|
|
206
|
+
or file-name inference.
|
|
207
|
+
|
|
208
|
+
Workflow itself invokes the same module in batch mode for the reserved `$npy`
|
|
209
|
+
phase and publishes one member directory plus a
|
|
210
|
+
`scientific-workflow-npy-batch.v2` manifest at the standard
|
|
211
|
+
`<execution>/processed/replicate-NNNNNN` path. Use
|
|
212
|
+
`open_npy_batch()` to verify that manifest and every referenced member.
|
|
213
|
+
|
|
214
|
+
The normative converted-data contract is
|
|
215
|
+
[`protocol/npy-v2.md`](../protocol/npy-v2.md).
|
|
216
|
+
|
|
217
|
+
## Development
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
cd python
|
|
221
|
+
python -m pip install -e ".[npy]"
|
|
222
|
+
python -m unittest discover -s tests -v
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## License
|
|
226
|
+
|
|
227
|
+
Licensed under the MIT License.
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# Scientific Workflow Python utilities
|
|
2
|
+
|
|
3
|
+
> **BREAKING IMPORT CHANGE — 0.4.4:** use `scientific_workflow`; the old
|
|
4
|
+
> `scientific_workflow_reader` namespace is not provided.
|
|
5
|
+
> **LINUX ONLY. Python 3.14+ REQUIRED.** Activate the environment containing
|
|
6
|
+
> `scientific-workflow[npy]` before every Workflow launch, in every new shell.
|
|
7
|
+
> **REQUIRED LAYOUT:** keep `<study>/wf_configs/study.json` and `parameters.json`.
|
|
8
|
+
> Standard accessors do not discover renamed or relocated files.
|
|
9
|
+
|
|
10
|
+
[Complete structured API reference](src/scientific_workflow/api.md).
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
`scientific-workflow` is the official Python reader for completed
|
|
14
|
+
Scientific Workflow recordings. It implements the same versioned metadata,
|
|
15
|
+
JSONL framing, lifecycle, schema, ordering, and mandatory chunk-integrity
|
|
16
|
+
contract as Workflow's Rust `StoredStateSeriesReader`.
|
|
17
|
+
|
|
18
|
+
The reader is intentionally eager. A stream is returned only after all selected
|
|
19
|
+
chunks pass byte-count, SHA-256, JSON record, field, descriptor, and iteration
|
|
20
|
+
validation. A failure never returns a partial scientific series.
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
python3.14 -m venv .venv
|
|
26
|
+
source .venv/bin/activate
|
|
27
|
+
python -m pip install \
|
|
28
|
+
"scientific-workflow @ git+https://github.com/dingyisun0101/Scientific-Workflow.git@v0.13.8#subdirectory=python"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Python 3.14 or newer is required. The core reader has no runtime dependencies.
|
|
32
|
+
Install the optional NumPy converter when a project uses Workflow's reserved
|
|
33
|
+
`$npy` phase or when converting a recording directly:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
python -m pip install \
|
|
37
|
+
"scientific-workflow[npy] @ git+https://github.com/dingyisun0101/Scientific-Workflow.git@v0.13.8#subdirectory=python"
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
This guide documents release 0.4.4.
|
|
41
|
+
|
|
42
|
+
## Reading a recording
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
from scientific_workflow import open_completed_recording
|
|
46
|
+
|
|
47
|
+
reader = open_completed_recording("results/study/recordings/task-000000")
|
|
48
|
+
print(reader.stream_names)
|
|
49
|
+
print(reader.user_metadata)
|
|
50
|
+
print(reader.terminal_metadata)
|
|
51
|
+
|
|
52
|
+
signal = reader.read_stream("signal")
|
|
53
|
+
for state in signal:
|
|
54
|
+
print(state.iteration, state.physical_time, state.values["abundance"])
|
|
55
|
+
|
|
56
|
+
latest = reader.read_latest("checkpoint")
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
JSON payloads reconstruct into ordinary Python scalars, lists, and dictionaries
|
|
60
|
+
by default. Applications may supply explicit per-field decoders without making
|
|
61
|
+
the storage package depend on NumPy:
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
import numpy as np
|
|
65
|
+
from scientific_workflow import open_completed_recording
|
|
66
|
+
|
|
67
|
+
reader = open_completed_recording(
|
|
68
|
+
"recording",
|
|
69
|
+
decoders={
|
|
70
|
+
"abundance": np.asarray,
|
|
71
|
+
"space": np.asarray,
|
|
72
|
+
"total": float,
|
|
73
|
+
},
|
|
74
|
+
)
|
|
75
|
+
checkpoint = reader.read_latest("checkpoint")
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
When a decoder mapping is supplied, every selected stream field must have a
|
|
79
|
+
decoder. Decoder failures are reported as `DecoderError` with the original
|
|
80
|
+
exception chained as their cause.
|
|
81
|
+
|
|
82
|
+
## Public API
|
|
83
|
+
|
|
84
|
+
- `open_completed_recording(path, decoders=None)`
|
|
85
|
+
- `RecordingReader`
|
|
86
|
+
- `stream_names`, `user_metadata`, `terminal_metadata`, and `timing`
|
|
87
|
+
- `stream_record_count(name)` and `stream_encoded_bytes(name)`
|
|
88
|
+
- `read_stream(name)`, `read_all_streams()`, and `read_latest(name)`
|
|
89
|
+
- `iter_verified_records(name)` for bounded-memory incremental consumers
|
|
90
|
+
- structurally read-only `StateField`, `StateRecord`, and `StateSeries`
|
|
91
|
+
- typed exceptions rooted at `RecordingError`
|
|
92
|
+
|
|
93
|
+
Release 0.4.4 supports
|
|
94
|
+
`scientific-workflow-jsonl` format versions 7 and 8, positional JSON payload encoding, JSON Lines
|
|
95
|
+
framing, and `sha256:` chunk checksums. Unknown versions and algorithms fail
|
|
96
|
+
closed.
|
|
97
|
+
|
|
98
|
+
The normative language-neutral contract is the repository's
|
|
99
|
+
[recording v7 protocol](https://github.com/dingyisun0101/Scientific-Workflow/blob/v0.13.8/protocol/recording-v7.md),
|
|
100
|
+
with a strict structural JSON Schema and a package
|
|
101
|
+
[compatibility matrix](https://github.com/dingyisun0101/Scientific-Workflow/blob/v0.13.8/protocol/compatibility.md). This package is the
|
|
102
|
+
v7/v8 reader listed there; it does not expose a supported writer.
|
|
103
|
+
|
|
104
|
+
The record containers cannot be reassigned and their value mappings are
|
|
105
|
+
read-only. Decoded payload objects retain the type and mutability chosen by
|
|
106
|
+
JSON decoding or by the caller's field decoder.
|
|
107
|
+
|
|
108
|
+
`read_stream()` is transactional: it returns a complete series or nothing.
|
|
109
|
+
`iter_verified_records()` verifies an entire bounded chunk before yielding its
|
|
110
|
+
first record, but a later chunk may fail after earlier records were consumed.
|
|
111
|
+
Incremental callers should therefore write only to private temporary storage
|
|
112
|
+
and publish it after iteration completes successfully.
|
|
113
|
+
|
|
114
|
+
## Integrity boundary
|
|
115
|
+
|
|
116
|
+
The reader validates:
|
|
117
|
+
|
|
118
|
+
- successful recording completion;
|
|
119
|
+
- strict metadata and record keys;
|
|
120
|
+
- safe relative stream and chunk paths;
|
|
121
|
+
- deterministic chunk names and contiguous ordinals;
|
|
122
|
+
- declared byte lengths and mandatory SHA-256 checksums;
|
|
123
|
+
- newline-terminated, nonempty JSONL records;
|
|
124
|
+
- exact stream field coverage;
|
|
125
|
+
- record counts and first/last iterations; and
|
|
126
|
+
- strictly increasing iterations across chunk boundaries.
|
|
127
|
+
|
|
128
|
+
Checksums detect storage corruption and accidental alteration. They do not
|
|
129
|
+
establish scientific correctness, authorship, or cryptographic authenticity.
|
|
130
|
+
|
|
131
|
+
The fixture under `tests/fixtures/complete` is also opened by Workflow's Rust
|
|
132
|
+
reader, making it the shared cross-language golden example. The normative
|
|
133
|
+
protocol and schema remain authoritative over any individual fixture.
|
|
134
|
+
|
|
135
|
+
Workflow's Rust integration suite additionally runs a bidirectional test. Its
|
|
136
|
+
internal automatic persistence path produces a multi-chunk recording for this
|
|
137
|
+
package to read; the test-only Python bridge re-encodes those records; and the
|
|
138
|
+
public Rust reader validates the Python result, including exact
|
|
139
|
+
sensitive-float bits and Unicode. The Rust write session and Python bridge are
|
|
140
|
+
test infrastructure, not supported writer APIs.
|
|
141
|
+
|
|
142
|
+
## NumPy conversion
|
|
143
|
+
|
|
144
|
+
The optional converter verifies one completed recording through the official
|
|
145
|
+
reader and converts every field into manifest-directed, C-contiguous `.npy`
|
|
146
|
+
data. Fixed-shape numeric fields map directly. Changing numeric shapes use
|
|
147
|
+
packed data, offsets, and shapes. Structured fields use canonical UTF-8 JSON
|
|
148
|
+
bytes plus offsets as a lossless fallback and expose every stable nested
|
|
149
|
+
numeric value as a fixed or ragged projection. Conversion never writes NumPy
|
|
150
|
+
object arrays and never enables pickle.
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
scientific-workflow-to-npy path/to/member-recording
|
|
154
|
+
scientific-workflow-to-npy path/to/member-recording --output path/to/processed
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
The equivalent module entry point is
|
|
158
|
+
`python -m scientific_workflow.npy`. With no `--output`, conversion uses
|
|
159
|
+
a sibling directory named `<recording>-npy`. It never writes inside or modifies
|
|
160
|
+
the raw recording. Conversion uses bounded-memory verified iteration, writes a
|
|
161
|
+
private temporary directory, atomically publishes the completed result, and
|
|
162
|
+
resumes an existing result only when its manifest and arrays still match.
|
|
163
|
+
From a source checkout, the source-tree launcher script is
|
|
164
|
+
`python/scripts/recording_to_npy.py`; it adds the adjacent package source and
|
|
165
|
+
accepts the same recording and `--output` arguments.
|
|
166
|
+
|
|
167
|
+
Python callers may use
|
|
168
|
+
`scientific_workflow.npy.convert_recording(recording, output=None)`.
|
|
169
|
+
The resulting member directory is valid only with its required
|
|
170
|
+
`manifest.json`. Open and verify it before accessing arrays:
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
from scientific_workflow.npy import open_npy_conversion
|
|
174
|
+
|
|
175
|
+
converted = open_npy_conversion("path/to/processed/member-000000")
|
|
176
|
+
latest = converted.reconstruct("state", "label", 4)
|
|
177
|
+
positions = converted.projection("particles", "particles", "/attributes/r", 4)
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
`NpyConversion.array()` memory-maps a declared component,
|
|
181
|
+
`field()` returns its representation metadata, `reconstruct()` reads one whole
|
|
182
|
+
field record, and `projection()` reads one nested numeric path. Component
|
|
183
|
+
relationships and paths always come from the manifest, never directory scans
|
|
184
|
+
or file-name inference.
|
|
185
|
+
|
|
186
|
+
Workflow itself invokes the same module in batch mode for the reserved `$npy`
|
|
187
|
+
phase and publishes one member directory plus a
|
|
188
|
+
`scientific-workflow-npy-batch.v2` manifest at the standard
|
|
189
|
+
`<execution>/processed/replicate-NNNNNN` path. Use
|
|
190
|
+
`open_npy_batch()` to verify that manifest and every referenced member.
|
|
191
|
+
|
|
192
|
+
The normative converted-data contract is
|
|
193
|
+
[`protocol/npy-v2.md`](../protocol/npy-v2.md).
|
|
194
|
+
|
|
195
|
+
## Development
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
cd python
|
|
199
|
+
python -m pip install -e ".[npy]"
|
|
200
|
+
python -m unittest discover -s tests -v
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## License
|
|
204
|
+
|
|
205
|
+
Licensed under the MIT License.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "scientific-workflow"
|
|
7
|
+
version = "0.4.4"
|
|
8
|
+
description = "Python utilities, verified readers, and optional NumPy conversion for Scientific Workflow"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.14"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
authors = [{name = "Dingyi Sun"}]
|
|
13
|
+
keywords = ["science", "workflow", "recording", "jsonl", "reproducibility"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 3 - Alpha",
|
|
16
|
+
"Intended Audience :: Science/Research",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
19
|
+
"Topic :: Scientific/Engineering",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
[project.optional-dependencies]
|
|
23
|
+
npy = ["numpy>=1.26", "threadpoolctl>=3"]
|
|
24
|
+
|
|
25
|
+
[project.scripts]
|
|
26
|
+
scientific-workflow-to-npy = "scientific_workflow.npy:main"
|
|
27
|
+
|
|
28
|
+
[project.urls]
|
|
29
|
+
Homepage = "https://github.com/dingyisun0101/Scientific-Workflow"
|
|
30
|
+
Repository = "https://github.com/dingyisun0101/Scientific-Workflow"
|
|
31
|
+
|
|
32
|
+
[tool.setuptools.packages.find]
|
|
33
|
+
where = ["src"]
|
|
34
|
+
|
|
35
|
+
[tool.setuptools.package-data]
|
|
36
|
+
scientific_workflow = ["py.typed", "api.md"]
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""Official verified Python reader for Scientific Workflow recordings."""
|
|
2
|
+
|
|
3
|
+
from .errors import (
|
|
4
|
+
DecoderError,
|
|
5
|
+
IntegrityError,
|
|
6
|
+
MetadataError,
|
|
7
|
+
RecordError,
|
|
8
|
+
RecordingError,
|
|
9
|
+
RecordingNotCompleteError,
|
|
10
|
+
UnknownStreamError,
|
|
11
|
+
)
|
|
12
|
+
from .state import StateField, StateRecord, StateSeries
|
|
13
|
+
from .reader import (
|
|
14
|
+
FORMAT_NAME,
|
|
15
|
+
FORMAT_VERSION,
|
|
16
|
+
Decoder,
|
|
17
|
+
RecordingReader,
|
|
18
|
+
open_completed_recording,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"Decoder",
|
|
23
|
+
"DecoderError",
|
|
24
|
+
"FORMAT_NAME",
|
|
25
|
+
"FORMAT_VERSION",
|
|
26
|
+
"IntegrityError",
|
|
27
|
+
"MetadataError",
|
|
28
|
+
"RecordError",
|
|
29
|
+
"RecordingError",
|
|
30
|
+
"RecordingNotCompleteError",
|
|
31
|
+
"RecordingReader",
|
|
32
|
+
"StateField",
|
|
33
|
+
"StateRecord",
|
|
34
|
+
"StateSeries",
|
|
35
|
+
"UnknownStreamError",
|
|
36
|
+
"open_completed_recording",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
__version__ = "0.4.4"
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""Private cooperative control for the coordinated standard converter."""
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
import time
|
|
6
|
+
|
|
7
|
+
_TOKEN = "parent"
|
|
8
|
+
_LAST_CHECK = 0.0
|
|
9
|
+
_PAUSE_STARTED = None
|
|
10
|
+
_PAUSE_TOTAL = 0.0
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def state() -> tuple[Path | None, bool, bool]:
|
|
14
|
+
value = os.environ.get("WORKFLOW_CONTROL_PATH")
|
|
15
|
+
if not value:
|
|
16
|
+
return None, False, False
|
|
17
|
+
path = Path(value)
|
|
18
|
+
document = json.loads(path.read_text(encoding="utf-8"))
|
|
19
|
+
global _PAUSE_STARTED, _PAUSE_TOTAL
|
|
20
|
+
now = time.monotonic()
|
|
21
|
+
if document["paused"] and _PAUSE_STARTED is None:
|
|
22
|
+
_PAUSE_STARTED = now
|
|
23
|
+
elif not document["paused"] and _PAUSE_STARTED is not None:
|
|
24
|
+
_PAUSE_TOTAL += now - _PAUSE_STARTED
|
|
25
|
+
_PAUSE_STARTED = None
|
|
26
|
+
return path, document["paused"], document["cancelled"]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def acknowledgement(path: Path, token: str) -> Path:
|
|
30
|
+
return path.with_name(path.name + f".{token}.paused")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def checkpoint(*, force: bool = False) -> None:
|
|
34
|
+
global _LAST_CHECK
|
|
35
|
+
now = time.monotonic()
|
|
36
|
+
if not force and now - _LAST_CHECK < 0.02:
|
|
37
|
+
return
|
|
38
|
+
_LAST_CHECK = now
|
|
39
|
+
path, paused, cancelled = state()
|
|
40
|
+
if cancelled:
|
|
41
|
+
raise InterruptedError("Workflow conversion cancelled")
|
|
42
|
+
if not paused or path is None:
|
|
43
|
+
return
|
|
44
|
+
ack = acknowledgement(path, _TOKEN)
|
|
45
|
+
ack.touch()
|
|
46
|
+
try:
|
|
47
|
+
while paused:
|
|
48
|
+
time.sleep(0.01)
|
|
49
|
+
_, paused, cancelled = state()
|
|
50
|
+
if cancelled:
|
|
51
|
+
raise InterruptedError("Workflow conversion cancelled")
|
|
52
|
+
finally:
|
|
53
|
+
ack.unlink(missing_ok=True)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def active_time() -> float:
|
|
57
|
+
"""Converter diagnostic clock; Runtime remains authoritative for task budgets."""
|
|
58
|
+
state()
|
|
59
|
+
return (_PAUSE_STARTED if _PAUSE_STARTED is not None else time.monotonic()) - _PAUSE_TOTAL
|