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.
Files changed (25) hide show
  1. scientific_workflow-0.4.4/LICENSE +21 -0
  2. scientific_workflow-0.4.4/PKG-INFO +227 -0
  3. scientific_workflow-0.4.4/README.md +205 -0
  4. scientific_workflow-0.4.4/pyproject.toml +36 -0
  5. scientific_workflow-0.4.4/setup.cfg +4 -0
  6. scientific_workflow-0.4.4/src/scientific_workflow/__init__.py +39 -0
  7. scientific_workflow-0.4.4/src/scientific_workflow/_control.py +59 -0
  8. scientific_workflow-0.4.4/src/scientific_workflow/api.md +221 -0
  9. scientific_workflow-0.4.4/src/scientific_workflow/dependencies.py +198 -0
  10. scientific_workflow-0.4.4/src/scientific_workflow/errors.py +29 -0
  11. scientific_workflow-0.4.4/src/scientific_workflow/npy.py +1449 -0
  12. scientific_workflow-0.4.4/src/scientific_workflow/project.py +56 -0
  13. scientific_workflow-0.4.4/src/scientific_workflow/py.typed +1 -0
  14. scientific_workflow-0.4.4/src/scientific_workflow/reader.py +580 -0
  15. scientific_workflow-0.4.4/src/scientific_workflow/reporting.py +61 -0
  16. scientific_workflow-0.4.4/src/scientific_workflow/state.py +55 -0
  17. scientific_workflow-0.4.4/src/scientific_workflow.egg-info/PKG-INFO +227 -0
  18. scientific_workflow-0.4.4/src/scientific_workflow.egg-info/SOURCES.txt +23 -0
  19. scientific_workflow-0.4.4/src/scientific_workflow.egg-info/dependency_links.txt +1 -0
  20. scientific_workflow-0.4.4/src/scientific_workflow.egg-info/entry_points.txt +2 -0
  21. scientific_workflow-0.4.4/src/scientific_workflow.egg-info/requires.txt +4 -0
  22. scientific_workflow-0.4.4/src/scientific_workflow.egg-info/top_level.txt +1 -0
  23. scientific_workflow-0.4.4/tests/test_npy.py +486 -0
  24. scientific_workflow-0.4.4/tests/test_reader.py +265 -0
  25. 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,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -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