ms-flow 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.
- ms_flow-0.1.0/.github/workflows/docs.yml +58 -0
- ms_flow-0.1.0/.github/workflows/publish.yml +31 -0
- ms_flow-0.1.0/.gitignore +37 -0
- ms_flow-0.1.0/LICENSE +21 -0
- ms_flow-0.1.0/PKG-INFO +77 -0
- ms_flow-0.1.0/README.md +45 -0
- ms_flow-0.1.0/docs/api_guide.md +191 -0
- ms_flow-0.1.0/docs/api_reference.md +46 -0
- ms_flow-0.1.0/docs/examples.md +191 -0
- ms_flow-0.1.0/docs/index.md +83 -0
- ms_flow-0.1.0/pyproject.toml +77 -0
- ms_flow-0.1.0/setup.cfg +4 -0
- ms_flow-0.1.0/src/ms_flow/__init__.py +26 -0
- ms_flow-0.1.0/src/ms_flow/_api_impl/__init__.py +9 -0
- ms_flow-0.1.0/src/ms_flow/_api_impl/advanced.py +64 -0
- ms_flow-0.1.0/src/ms_flow/_api_impl/job_api.py +579 -0
- ms_flow-0.1.0/src/ms_flow/_api_impl/project_runtime.py +886 -0
- ms_flow-0.1.0/src/ms_flow/advanced.py +5 -0
- ms_flow-0.1.0/src/ms_flow/api.py +79 -0
- ms_flow-0.1.0/src/ms_flow/artifacts.py +329 -0
- ms_flow-0.1.0/src/ms_flow/core/__init__.py +0 -0
- ms_flow-0.1.0/src/ms_flow/core/app_settings.py +133 -0
- ms_flow-0.1.0/src/ms_flow/core/apps.py +173 -0
- ms_flow-0.1.0/src/ms_flow/core/callable_refs.py +66 -0
- ms_flow-0.1.0/src/ms_flow/core/configuration.py +365 -0
- ms_flow-0.1.0/src/ms_flow/core/data/__init__.py +53 -0
- ms_flow-0.1.0/src/ms_flow/core/data/backends.py +708 -0
- ms_flow-0.1.0/src/ms_flow/core/data/bridge.py +212 -0
- ms_flow-0.1.0/src/ms_flow/core/data/contracts.py +534 -0
- ms_flow-0.1.0/src/ms_flow/core/data/planner.py +75 -0
- ms_flow-0.1.0/src/ms_flow/core/data/project_output.py +27 -0
- ms_flow-0.1.0/src/ms_flow/core/data/runtime.py +72 -0
- ms_flow-0.1.0/src/ms_flow/core/data/shard.py +556 -0
- ms_flow-0.1.0/src/ms_flow/core/database/__init__.py +33 -0
- ms_flow-0.1.0/src/ms_flow/core/database/base.py +229 -0
- ms_flow-0.1.0/src/ms_flow/core/database/executor.py +218 -0
- ms_flow-0.1.0/src/ms_flow/core/database/executor_models.py +112 -0
- ms_flow-0.1.0/src/ms_flow/core/database/master.py +168 -0
- ms_flow-0.1.0/src/ms_flow/core/database/master_models.py +40 -0
- ms_flow-0.1.0/src/ms_flow/core/database/project.py +678 -0
- ms_flow-0.1.0/src/ms_flow/core/database/project_models.py +59 -0
- ms_flow-0.1.0/src/ms_flow/core/database/project_records.py +100 -0
- ms_flow-0.1.0/src/ms_flow/core/database/project_store_ops.py +958 -0
- ms_flow-0.1.0/src/ms_flow/core/database/sqlite_utils.py +208 -0
- ms_flow-0.1.0/src/ms_flow/core/events.py +5 -0
- ms_flow-0.1.0/src/ms_flow/core/executor/__init__.py +62 -0
- ms_flow-0.1.0/src/ms_flow/core/executor/backend_selector.py +254 -0
- ms_flow-0.1.0/src/ms_flow/core/executor/command_inbox.py +40 -0
- ms_flow-0.1.0/src/ms_flow/core/executor/dispatch_model.py +71 -0
- ms_flow-0.1.0/src/ms_flow/core/executor/dispatch_service.py +397 -0
- ms_flow-0.1.0/src/ms_flow/core/executor/executor_registry.py +236 -0
- ms_flow-0.1.0/src/ms_flow/core/executor/hpc_adapter.py +436 -0
- ms_flow-0.1.0/src/ms_flow/core/executor/hpc_runtime.py +124 -0
- ms_flow-0.1.0/src/ms_flow/core/executor/job_monitoring.py +666 -0
- ms_flow-0.1.0/src/ms_flow/core/executor/job_snapshot.py +124 -0
- ms_flow-0.1.0/src/ms_flow/core/executor/lifecycle_controller.py +369 -0
- ms_flow-0.1.0/src/ms_flow/core/executor/local_adapters.py +497 -0
- ms_flow-0.1.0/src/ms_flow/core/executor/local_compute_scheduler.py +116 -0
- ms_flow-0.1.0/src/ms_flow/core/executor/local_scheduler.py +169 -0
- ms_flow-0.1.0/src/ms_flow/core/executor/manager/__init__.py +15 -0
- ms_flow-0.1.0/src/ms_flow/core/executor/manager/_manager.py +1904 -0
- ms_flow-0.1.0/src/ms_flow/core/executor/provisioning.py +443 -0
- ms_flow-0.1.0/src/ms_flow/core/executor/ray_adapter.py +452 -0
- ms_flow-0.1.0/src/ms_flow/core/executor/resource_manager.py +65 -0
- ms_flow-0.1.0/src/ms_flow/core/executor/result_handlers.py +696 -0
- ms_flow-0.1.0/src/ms_flow/core/executor/runner_refs.py +152 -0
- ms_flow-0.1.0/src/ms_flow/core/executor/runtime_state.py +209 -0
- ms_flow-0.1.0/src/ms_flow/core/executor/runtime_status_service.py +408 -0
- ms_flow-0.1.0/src/ms_flow/core/executor/services/__init__.py +0 -0
- ms_flow-0.1.0/src/ms_flow/core/executor/services/dispatch_pool.py +230 -0
- ms_flow-0.1.0/src/ms_flow/core/executor/services/event_recorder.py +155 -0
- ms_flow-0.1.0/src/ms_flow/core/executor/services/feeding.py +274 -0
- ms_flow-0.1.0/src/ms_flow/core/executor/services/job_commands.py +258 -0
- ms_flow-0.1.0/src/ms_flow/core/executor/services/job_status.py +481 -0
- ms_flow-0.1.0/src/ms_flow/core/executor/services/job_store.py +350 -0
- ms_flow-0.1.0/src/ms_flow/core/executor/services/payload_store.py +166 -0
- ms_flow-0.1.0/src/ms_flow/core/executor/services/persistence_coordinator.py +240 -0
- ms_flow-0.1.0/src/ms_flow/core/executor/services/sink_writer_pool.py +122 -0
- ms_flow-0.1.0/src/ms_flow/core/executor/staging_manager.py +131 -0
- ms_flow-0.1.0/src/ms_flow/core/executor/submission_service.py +604 -0
- ms_flow-0.1.0/src/ms_flow/core/executor/utils.py +40 -0
- ms_flow-0.1.0/src/ms_flow/core/project/__init__.py +21 -0
- ms_flow-0.1.0/src/ms_flow/core/project/catalog.py +343 -0
- ms_flow-0.1.0/src/ms_flow/core/project/context.py +45 -0
- ms_flow-0.1.0/src/ms_flow/core/project/manager.py +193 -0
- ms_flow-0.1.0/src/ms_flow/core/project/repository.py +264 -0
- ms_flow-0.1.0/src/ms_flow/core/project/resources.py +150 -0
- ms_flow-0.1.0/src/ms_flow/core/project/state.py +45 -0
- ms_flow-0.1.0/src/ms_flow/core/settings/__init__.py +0 -0
- ms_flow-0.1.0/src/ms_flow/core/settings/defaults.toml +42 -0
- ms_flow-0.1.0/src/ms_flow/core/settings/manager.py +397 -0
- ms_flow-0.1.0/src/ms_flow/core/settings/models.py +412 -0
- ms_flow-0.1.0/src/ms_flow/job_templates.py +171 -0
- ms_flow-0.1.0/src/ms_flow/logger.py +316 -0
- ms_flow-0.1.0/src/ms_flow/main.py +164 -0
- ms_flow-0.1.0/src/ms_flow/progress.py +70 -0
- ms_flow-0.1.0/src/ms_flow/query.py +255 -0
- ms_flow-0.1.0/src/ms_flow/runtime.py +208 -0
- ms_flow-0.1.0/src/ms_flow/selection.py +70 -0
- ms_flow-0.1.0/src/ms_flow/sinks.py +180 -0
- ms_flow-0.1.0/src/ms_flow/specs/__init__.py +42 -0
- ms_flow-0.1.0/src/ms_flow/specs/input.py +260 -0
- ms_flow-0.1.0/src/ms_flow/specs/output.py +73 -0
- ms_flow-0.1.0/src/ms_flow/specs/processor.py +30 -0
- ms_flow-0.1.0/src/ms_flow/specs/workflow.py +176 -0
- ms_flow-0.1.0/src/ms_flow/tasking.py +771 -0
- ms_flow-0.1.0/src/ms_flow.egg-info/PKG-INFO +77 -0
- ms_flow-0.1.0/src/ms_flow.egg-info/SOURCES.txt +170 -0
- ms_flow-0.1.0/src/ms_flow.egg-info/dependency_links.txt +1 -0
- ms_flow-0.1.0/src/ms_flow.egg-info/requires.txt +13 -0
- ms_flow-0.1.0/src/ms_flow.egg-info/scm_file_list.json +167 -0
- ms_flow-0.1.0/src/ms_flow.egg-info/scm_version.json +8 -0
- ms_flow-0.1.0/src/ms_flow.egg-info/top_level.txt +1 -0
- ms_flow-0.1.0/tests/adapter_contracts.py +142 -0
- ms_flow-0.1.0/tests/adapter_test_fakes.py +179 -0
- ms_flow-0.1.0/tests/adversarial_runtime_scenarios.py +390 -0
- ms_flow-0.1.0/tests/conftest.py +28 -0
- ms_flow-0.1.0/tests/test_app_registry.py +173 -0
- ms_flow-0.1.0/tests/test_artifact_registry.py +135 -0
- ms_flow-0.1.0/tests/test_backend_selector.py +101 -0
- ms_flow-0.1.0/tests/test_batched_overhead_benchmark.py +88 -0
- ms_flow-0.1.0/tests/test_benchmark.py +76 -0
- ms_flow-0.1.0/tests/test_data_api.py +109 -0
- ms_flow-0.1.0/tests/test_data_backend.py +444 -0
- ms_flow-0.1.0/tests/test_database_manager.py +92 -0
- ms_flow-0.1.0/tests/test_database_migrations.py +123 -0
- ms_flow-0.1.0/tests/test_db_pages.py +67 -0
- ms_flow-0.1.0/tests/test_deferred_chunk_resources.py +25 -0
- ms_flow-0.1.0/tests/test_dispatch_model.py +64 -0
- ms_flow-0.1.0/tests/test_executor_adapter_contracts.py +193 -0
- ms_flow-0.1.0/tests/test_executor_adapter_contracts_remote.py +291 -0
- ms_flow-0.1.0/tests/test_executor_manager.py +3623 -0
- ms_flow-0.1.0/tests/test_executor_retention.py +50 -0
- ms_flow-0.1.0/tests/test_file_sink_append.py +82 -0
- ms_flow-0.1.0/tests/test_gpu_token_semaphore.py +112 -0
- ms_flow-0.1.0/tests/test_graph_deferred_relation.py +125 -0
- ms_flow-0.1.0/tests/test_hpc_io_threads.py +71 -0
- ms_flow-0.1.0/tests/test_hpc_reattach.py +114 -0
- ms_flow-0.1.0/tests/test_job_templates.py +203 -0
- ms_flow-0.1.0/tests/test_jobspec_api.py +68 -0
- ms_flow-0.1.0/tests/test_local_compute_scheduler.py +69 -0
- ms_flow-0.1.0/tests/test_local_scheduler.py +162 -0
- ms_flow-0.1.0/tests/test_logging_manager.py +135 -0
- ms_flow-0.1.0/tests/test_molsuite_runtime.py +1137 -0
- ms_flow-0.1.0/tests/test_mrf070_runtime_overhead_benchmark.py +209 -0
- ms_flow-0.1.0/tests/test_mrf072_monitor_stability.py +240 -0
- ms_flow-0.1.0/tests/test_operational_store.py +43 -0
- ms_flow-0.1.0/tests/test_output_helpers.py +290 -0
- ms_flow-0.1.0/tests/test_output_sink_batching.py +291 -0
- ms_flow-0.1.0/tests/test_overhead_benchmark.py +97 -0
- ms_flow-0.1.0/tests/test_process_executor_benchmark.py +111 -0
- ms_flow-0.1.0/tests/test_progress.py +38 -0
- ms_flow-0.1.0/tests/test_project_catalog.py +146 -0
- ms_flow-0.1.0/tests/test_project_manager.py +82 -0
- ms_flow-0.1.0/tests/test_project_storage.py +22 -0
- ms_flow-0.1.0/tests/test_project_store.py +431 -0
- ms_flow-0.1.0/tests/test_provisioning.py +99 -0
- ms_flow-0.1.0/tests/test_public_api.py +148 -0
- ms_flow-0.1.0/tests/test_pydantic_configuration.py +79 -0
- ms_flow-0.1.0/tests/test_remote_e2e_optional.py +49 -0
- ms_flow-0.1.0/tests/test_resource_manager.py +32 -0
- ms_flow-0.1.0/tests/test_selection.py +30 -0
- ms_flow-0.1.0/tests/test_settings_manager.py +217 -0
- ms_flow-0.1.0/tests/test_shard_container.py +144 -0
- ms_flow-0.1.0/tests/test_simple_api.py +226 -0
- ms_flow-0.1.0/tests/test_sink_column_defaults.py +58 -0
- ms_flow-0.1.0/tests/test_sink_writer_pool.py +63 -0
- ms_flow-0.1.0/tests/test_sql_compiler.py +104 -0
- ms_flow-0.1.0/tests/test_staging_manager.py +78 -0
- ms_flow-0.1.0/tests/test_tasking_api.py +1129 -0
- ms_flow-0.1.0/tests/test_workflow_api.py +130 -0
- ms_flow-0.1.0/zensical.toml +79 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
name: Documentation
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches:
|
|
5
|
+
- main
|
|
6
|
+
tags:
|
|
7
|
+
- "v*"
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
concurrency:
|
|
11
|
+
group: documentation
|
|
12
|
+
cancel-in-progress: false
|
|
13
|
+
|
|
14
|
+
permissions:
|
|
15
|
+
contents: write
|
|
16
|
+
pages: write
|
|
17
|
+
id-token: write
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
deploy:
|
|
21
|
+
environment:
|
|
22
|
+
name: github-pages
|
|
23
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
24
|
+
runs-on: ubuntu-latest
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@v6
|
|
27
|
+
with:
|
|
28
|
+
fetch-depth: 0
|
|
29
|
+
- uses: actions/configure-pages@v6
|
|
30
|
+
- uses: actions/setup-python@v6
|
|
31
|
+
with:
|
|
32
|
+
python-version: 3.x
|
|
33
|
+
- run: pip install zensical "mkdocstrings[python]" "mike @ git+https://github.com/squidfunk/mike.git"
|
|
34
|
+
- name: Configure Git author
|
|
35
|
+
run: |
|
|
36
|
+
git config user.name "github-actions[bot]"
|
|
37
|
+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
38
|
+
- name: Deploy development documentation
|
|
39
|
+
if: github.ref == 'refs/heads/main'
|
|
40
|
+
run: |
|
|
41
|
+
mike deploy --push dev
|
|
42
|
+
if ! mike list latest >/dev/null 2>&1; then
|
|
43
|
+
mike set-default --push dev
|
|
44
|
+
fi
|
|
45
|
+
- name: Deploy release documentation
|
|
46
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
47
|
+
run: |
|
|
48
|
+
mike deploy --push --update-aliases --alias-type redirect "$GITHUB_REF_NAME" latest
|
|
49
|
+
mike set-default --push latest
|
|
50
|
+
- name: Export versioned site
|
|
51
|
+
run: |
|
|
52
|
+
mkdir -p site
|
|
53
|
+
git archive gh-pages | tar -x -C site
|
|
54
|
+
- uses: actions/upload-pages-artifact@v5
|
|
55
|
+
with:
|
|
56
|
+
path: site
|
|
57
|
+
- uses: actions/deploy-pages@v5
|
|
58
|
+
id: deployment
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Publish to PyPI on a GitHub release.
|
|
2
|
+
#
|
|
3
|
+
# No secrets: the job authenticates to PyPI over OIDC (Trusted Publishing).
|
|
4
|
+
# It has to be enabled on the PyPI side *before* the first upload -- for a
|
|
5
|
+
# project that does not exist yet, that is a "pending publisher" under
|
|
6
|
+
# Your projects > Publishing, and it must name this repo, `publish.yml`
|
|
7
|
+
# and the `pypi` environment below.
|
|
8
|
+
name: publish
|
|
9
|
+
|
|
10
|
+
on:
|
|
11
|
+
release:
|
|
12
|
+
types: [published]
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
pypi:
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
environment: pypi
|
|
18
|
+
permissions:
|
|
19
|
+
# A permissions block sets every scope it does not name to none, so
|
|
20
|
+
# checkout needs contents spelled out or it cannot read a private repo.
|
|
21
|
+
contents: read
|
|
22
|
+
id-token: write # mint the OIDC token PyPI checks; no secret involved
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@v7
|
|
25
|
+
with:
|
|
26
|
+
fetch-depth: 0 # setuptools-scm reads the version off the tag
|
|
27
|
+
- uses: actions/setup-python@v7
|
|
28
|
+
with:
|
|
29
|
+
python-version: "3.12"
|
|
30
|
+
- run: pipx run build
|
|
31
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
ms_flow-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
build/
|
|
6
|
+
dist/
|
|
7
|
+
.eggs/
|
|
8
|
+
|
|
9
|
+
# Test / coverage
|
|
10
|
+
.pytest_cache/
|
|
11
|
+
.coverage
|
|
12
|
+
htmlcov/
|
|
13
|
+
|
|
14
|
+
# Benchmark output (disposable, large)
|
|
15
|
+
.bench_runs/
|
|
16
|
+
|
|
17
|
+
# IDE / editor
|
|
18
|
+
.idea/
|
|
19
|
+
.vscode/
|
|
20
|
+
|
|
21
|
+
# Envs
|
|
22
|
+
.venv/
|
|
23
|
+
venv/
|
|
24
|
+
|
|
25
|
+
# Artefactos de corridas (ejemplos, proyectos de prueba)
|
|
26
|
+
demo_project/
|
|
27
|
+
chunk_payloads/
|
|
28
|
+
*.db
|
|
29
|
+
*.db-shm
|
|
30
|
+
*.db-wal
|
|
31
|
+
*.log
|
|
32
|
+
|
|
33
|
+
# Agentes / herramientas locales
|
|
34
|
+
.claude/
|
|
35
|
+
.aiassistant/
|
|
36
|
+
|
|
37
|
+
.idea/
|
ms_flow-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mario S. Valdés-Tresanco, Mario E. Valdés-Tresanco, Ernesto Moreno, Pedro A. Valiente
|
|
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.
|
ms_flow-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ms_flow
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: In-process, desktop-first, domain-agnostic job/task/executor orchestrator for massive data flows.
|
|
5
|
+
Author: Mario S. Valdés-Tresanco, Mario E. Valdés-Tresanco, Ernesto Moreno, Pedro A. Valiente
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/MolSuite/ms_flow
|
|
8
|
+
Project-URL: Documentation, https://github.com/MolSuite/ms_flow/tree/main/docs
|
|
9
|
+
Project-URL: Issues, https://github.com/MolSuite/ms_flow/issues
|
|
10
|
+
Keywords: orchestration,executor,jobs,pipeline,scientific-computing
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering
|
|
17
|
+
Requires-Python: >=3.12
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Requires-Dist: blinker>=1.6
|
|
21
|
+
Requires-Dist: loky>=3.0
|
|
22
|
+
Requires-Dist: pydantic>=2.0
|
|
23
|
+
Requires-Dist: sqlalchemy>=2.0
|
|
24
|
+
Requires-Dist: sqlmodel>=0.0.14
|
|
25
|
+
Requires-Dist: toml>=0.10
|
|
26
|
+
Requires-Dist: tqdm>=4.66
|
|
27
|
+
Provides-Extra: ray
|
|
28
|
+
Requires-Dist: ray>=2.6; extra == "ray"
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
|
|
33
|
+
# ms_flow
|
|
34
|
+
|
|
35
|
+
In-process, desktop-first, domain-agnostic orchestrator for massive data flows:
|
|
36
|
+
jobs, executors and persistence in the same process as your app — no broker, no
|
|
37
|
+
daemon, no server. Works from a Qt app, a notebook or a plain script.
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from ms_flow.api import MolSuite, table_sink
|
|
41
|
+
from workers import double_value # el handler vive en un modulo importable
|
|
42
|
+
|
|
43
|
+
ms = MolSuite(app_id="demo")
|
|
44
|
+
ms.create_or_open_project(name="demo", folder="./demo_project", activate=True)
|
|
45
|
+
|
|
46
|
+
job_id = ms.run(
|
|
47
|
+
name="double_values",
|
|
48
|
+
input=[{"value": v} for v in range(1000)],
|
|
49
|
+
process=double_value,
|
|
50
|
+
output=table_sink("results", columns=("value", "double_value")),
|
|
51
|
+
executor="thread",
|
|
52
|
+
)
|
|
53
|
+
print(ms.wait_for_job(job_id).status)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Install
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pip install ms_flow # core: control plane + SQLite + loky
|
|
60
|
+
pip install "ms_flow[ray]" # Ray executor
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Where to look
|
|
64
|
+
|
|
65
|
+
- [5-minute quickstart](docs/guides/quickstart_5min.md) — first working job.
|
|
66
|
+
- [Runnable examples](examples/README.md) — scripts y notebook.
|
|
67
|
+
- [Public API](docs/guides/public_api.md) — API.
|
|
68
|
+
- [Docs index](docs/README.md) — guides, contracts, operation
|
|
69
|
+
- [Benchmarks](benchmarks/README.md) — performance.
|
|
70
|
+
|
|
71
|
+
## Status
|
|
72
|
+
|
|
73
|
+
Beta. The public API (`ms_flow.api`) is the stable surface; `ms_flow.core.*` may change without notice.
|
|
74
|
+
|
|
75
|
+
## License
|
|
76
|
+
|
|
77
|
+
MIT — see [LICENSE](LICENSE).
|
ms_flow-0.1.0/README.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# ms_flow
|
|
2
|
+
|
|
3
|
+
In-process, desktop-first, domain-agnostic orchestrator for massive data flows:
|
|
4
|
+
jobs, executors and persistence in the same process as your app — no broker, no
|
|
5
|
+
daemon, no server. Works from a Qt app, a notebook or a plain script.
|
|
6
|
+
|
|
7
|
+
```python
|
|
8
|
+
from ms_flow.api import MolSuite, table_sink
|
|
9
|
+
from workers import double_value # el handler vive en un modulo importable
|
|
10
|
+
|
|
11
|
+
ms = MolSuite(app_id="demo")
|
|
12
|
+
ms.create_or_open_project(name="demo", folder="./demo_project", activate=True)
|
|
13
|
+
|
|
14
|
+
job_id = ms.run(
|
|
15
|
+
name="double_values",
|
|
16
|
+
input=[{"value": v} for v in range(1000)],
|
|
17
|
+
process=double_value,
|
|
18
|
+
output=table_sink("results", columns=("value", "double_value")),
|
|
19
|
+
executor="thread",
|
|
20
|
+
)
|
|
21
|
+
print(ms.wait_for_job(job_id).status)
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Install
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pip install ms_flow # core: control plane + SQLite + loky
|
|
28
|
+
pip install "ms_flow[ray]" # Ray executor
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Where to look
|
|
32
|
+
|
|
33
|
+
- [5-minute quickstart](docs/guides/quickstart_5min.md) — first working job.
|
|
34
|
+
- [Runnable examples](examples/README.md) — scripts y notebook.
|
|
35
|
+
- [Public API](docs/guides/public_api.md) — API.
|
|
36
|
+
- [Docs index](docs/README.md) — guides, contracts, operation
|
|
37
|
+
- [Benchmarks](benchmarks/README.md) — performance.
|
|
38
|
+
|
|
39
|
+
## Status
|
|
40
|
+
|
|
41
|
+
Beta. The public API (`ms_flow.api`) is the stable surface; `ms_flow.core.*` may change without notice.
|
|
42
|
+
|
|
43
|
+
## License
|
|
44
|
+
|
|
45
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# API guide
|
|
2
|
+
|
|
3
|
+
Use `MolSuite` as the public facade. A `MolSuite` instance owns the active
|
|
4
|
+
project and its executors, so close it with `shutdown()` when the work is done.
|
|
5
|
+
|
|
6
|
+
```python
|
|
7
|
+
from ms_flow.api import MolSuite
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## Projects
|
|
11
|
+
|
|
12
|
+
Projects are isolated per `app_id`. Opening a project activates it; jobs run
|
|
13
|
+
against the active project.
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
ms = MolSuite(app_id="demo")
|
|
17
|
+
try:
|
|
18
|
+
context = ms.create_or_open_project(name="demo", folder="./demo-project")
|
|
19
|
+
print(context.id)
|
|
20
|
+
|
|
21
|
+
ms.close_project() # the executors stay alive
|
|
22
|
+
ms.open_project(context.id)
|
|
23
|
+
finally:
|
|
24
|
+
ms.shutdown() # stops the whole runtime
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`close_project()` and `open_project()` fail explicitly if the active project
|
|
28
|
+
cannot drain its running jobs.
|
|
29
|
+
|
|
30
|
+
## Run a job
|
|
31
|
+
|
|
32
|
+
`run()` is the shortest path: `input → process → output`. It returns a job
|
|
33
|
+
identifier immediately; `wait_for_job()` blocks until the job ends and returns
|
|
34
|
+
a `JobSnapshot`.
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from workers import double_value
|
|
38
|
+
|
|
39
|
+
job_id = ms.run(
|
|
40
|
+
name="double-values",
|
|
41
|
+
input=[{"value": 1}, {"value": 2}, {"value": 3}],
|
|
42
|
+
process=double_value,
|
|
43
|
+
executor="thread",
|
|
44
|
+
store_results=True,
|
|
45
|
+
)
|
|
46
|
+
final = ms.wait_for_job(job_id)
|
|
47
|
+
print(final.status, final.chunks_done)
|
|
48
|
+
print(ms.get_job_outputs(job_id))
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
!!! note "Workers must be importable"
|
|
52
|
+
The `process` callable has to live in an importable module. Functions
|
|
53
|
+
defined in the script you launch (`__main__`), lambdas, nested functions,
|
|
54
|
+
and notebook cells are rejected, because process and remote executors must
|
|
55
|
+
re-import the worker by name.
|
|
56
|
+
|
|
57
|
+
Each worker receives one payload dictionary and returns a dictionary.
|
|
58
|
+
|
|
59
|
+
## Persist results with sinks
|
|
60
|
+
|
|
61
|
+
By default outputs are not kept. Pass a sink to write results incrementally,
|
|
62
|
+
and keep `store_results=False` so the operational database does not grow with
|
|
63
|
+
every result.
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from ms_flow.api import file_sink, table_sink
|
|
67
|
+
|
|
68
|
+
job_id = ms.run(
|
|
69
|
+
name="double-values",
|
|
70
|
+
input=[{"value": value} for value in range(1000)],
|
|
71
|
+
process=double_value,
|
|
72
|
+
output=table_sink("results", columns=("value", "double_value")),
|
|
73
|
+
flush_every=100,
|
|
74
|
+
executor="thread",
|
|
75
|
+
)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
| Sink | Destination |
|
|
79
|
+
| --- | --- |
|
|
80
|
+
| `table_sink(table, columns=...)` | A table in the project database; the table must exist |
|
|
81
|
+
| `file_sink(path, fmt="json")` | A file under the project folder |
|
|
82
|
+
| `graph_sink(nodes=..., relations=...)` | Several related tables written in one transaction |
|
|
83
|
+
|
|
84
|
+
## Read project data
|
|
85
|
+
|
|
86
|
+
`db_input_for()` describes a query instead of materialising rows. The chunk
|
|
87
|
+
carries the specification and the rows are read inside the worker.
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
from ms_flow.query import db_input_for
|
|
91
|
+
from workers import summarize_molecules
|
|
92
|
+
|
|
93
|
+
job_id = ms.run(
|
|
94
|
+
name="summarize-ready",
|
|
95
|
+
input=[
|
|
96
|
+
{
|
|
97
|
+
"molecules": db_input_for(
|
|
98
|
+
"molecules",
|
|
99
|
+
fields=("id", "name"),
|
|
100
|
+
filters={"status": "ready"},
|
|
101
|
+
order=("id",),
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
],
|
|
105
|
+
process=summarize_molecules,
|
|
106
|
+
executor="thread",
|
|
107
|
+
store_results=True,
|
|
108
|
+
)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Reusable definitions
|
|
112
|
+
|
|
113
|
+
Separate definition from execution with `workflow()`:
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
from ms_flow.api import workflow
|
|
117
|
+
|
|
118
|
+
spec = workflow(
|
|
119
|
+
name="double-values",
|
|
120
|
+
input=[{"value": 1}, {"value": 2}],
|
|
121
|
+
process=double_value,
|
|
122
|
+
executor="thread",
|
|
123
|
+
store_results=True,
|
|
124
|
+
)
|
|
125
|
+
job_id = ms.run(spec=spec)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
When a job is shared between scripts, a UI, and an application runtime, declare
|
|
129
|
+
it once with `streaming_job()` (one payload per chunk) or `batch_job()` (one
|
|
130
|
+
batch per chunk) and submit it with parameters:
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
from ms_flow.api import streaming_job
|
|
134
|
+
from workers import double_value, value_chunks
|
|
135
|
+
|
|
136
|
+
double_job = streaming_job(
|
|
137
|
+
name="double-values",
|
|
138
|
+
run_chunk=double_value,
|
|
139
|
+
chunker=value_chunks,
|
|
140
|
+
executor="thread",
|
|
141
|
+
store_results=True,
|
|
142
|
+
)
|
|
143
|
+
job_id = ms.submit_job(double_job, params={"count": 10})
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Follow progress
|
|
147
|
+
|
|
148
|
+
`watch_job()` renders a tqdm progress bar in a terminal or notebook and returns
|
|
149
|
+
the final snapshot. Pass `follow=False` for a non-blocking snapshot.
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
from ms_flow import watch_job
|
|
153
|
+
|
|
154
|
+
snapshot = watch_job(ms, job_id)
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Cancel a running job with `ms.cancel_job(job_id)`.
|
|
158
|
+
|
|
159
|
+
## Application runtimes
|
|
160
|
+
|
|
161
|
+
Subclass `BaseRuntime` to build a domain facade on top of `MolSuite`. The
|
|
162
|
+
runtime fixes the `app_id` once and exposes the engine as `self.molsuite`.
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
from ms_flow.runtime import BaseRuntime
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class DemoRuntime(BaseRuntime):
|
|
169
|
+
def __init__(self):
|
|
170
|
+
super().__init__("demoapp")
|
|
171
|
+
|
|
172
|
+
def double(self, values):
|
|
173
|
+
return self.run(
|
|
174
|
+
name="double-values",
|
|
175
|
+
input=[{"value": value} for value in values],
|
|
176
|
+
process=double_value,
|
|
177
|
+
executor="thread",
|
|
178
|
+
store_results=True,
|
|
179
|
+
)
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Choose the API level
|
|
183
|
+
|
|
184
|
+
| Need | Recommended API |
|
|
185
|
+
| --- | --- |
|
|
186
|
+
| Run `input → process → output` | `MolSuite.run()` |
|
|
187
|
+
| Reuse a common definition | `workflow()`, `streaming_job()`, or `batch_job()` |
|
|
188
|
+
| Describe project queries | `ms_flow.query` |
|
|
189
|
+
| Control tasks, staging, and finalize | `ms_flow.tasking` |
|
|
190
|
+
| Build a domain facade | `BaseRuntime` / `AppRuntime` |
|
|
191
|
+
| Extend the engine | `ms_flow.advanced` and, exceptionally, `ms_flow.core` |
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# API reference
|
|
2
|
+
|
|
3
|
+
The reference below is generated directly from the current Python sources.
|
|
4
|
+
Names beginning with an underscore are implementation details.
|
|
5
|
+
|
|
6
|
+
## MolSuite
|
|
7
|
+
|
|
8
|
+
::: ms_flow.main.MolSuite
|
|
9
|
+
options:
|
|
10
|
+
inherited_members: true
|
|
11
|
+
# The mixin methods have no docstrings yet; hidden by default.
|
|
12
|
+
show_if_no_docstring: true
|
|
13
|
+
|
|
14
|
+
## Application runtimes
|
|
15
|
+
|
|
16
|
+
::: ms_flow.runtime.BaseRuntime
|
|
17
|
+
|
|
18
|
+
::: ms_flow.runtime.AppRuntime
|
|
19
|
+
|
|
20
|
+
## Workflow specs
|
|
21
|
+
|
|
22
|
+
::: ms_flow.specs
|
|
23
|
+
|
|
24
|
+
## Job templates
|
|
25
|
+
|
|
26
|
+
::: ms_flow.job_templates
|
|
27
|
+
|
|
28
|
+
## Sinks
|
|
29
|
+
|
|
30
|
+
::: ms_flow.sinks
|
|
31
|
+
|
|
32
|
+
## Queries
|
|
33
|
+
|
|
34
|
+
::: ms_flow.query
|
|
35
|
+
|
|
36
|
+
## Selections
|
|
37
|
+
|
|
38
|
+
::: ms_flow.selection
|
|
39
|
+
|
|
40
|
+
## Progress
|
|
41
|
+
|
|
42
|
+
::: ms_flow.progress
|
|
43
|
+
|
|
44
|
+
## Tasking
|
|
45
|
+
|
|
46
|
+
::: ms_flow.tasking
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# Examples
|
|
2
|
+
|
|
3
|
+
Every example imports its worker functions from `workers.py`, because workers
|
|
4
|
+
must be importable. Save this file next to the scripts:
|
|
5
|
+
|
|
6
|
+
```python
|
|
7
|
+
# workers.py
|
|
8
|
+
def double_value(payload: dict) -> dict:
|
|
9
|
+
value = int(payload["value"])
|
|
10
|
+
return {"value": value, "double_value": value * 2}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def summarize_molecules(payload: dict) -> dict:
|
|
14
|
+
molecules = list(payload["molecules"])
|
|
15
|
+
return {
|
|
16
|
+
"molecule_count": len(molecules),
|
|
17
|
+
"molecule_names": [row["name"] for row in molecules],
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def value_chunks(params: dict):
|
|
22
|
+
for value in range(int(params["count"])):
|
|
23
|
+
yield {"value": value}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Each example creates its project in the current directory, so run them in a
|
|
27
|
+
scratch folder.
|
|
28
|
+
|
|
29
|
+
## Persist results to a table
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
import sqlite3
|
|
33
|
+
from pathlib import Path
|
|
34
|
+
|
|
35
|
+
from ms_flow.api import MolSuite, table_sink
|
|
36
|
+
from workers import double_value
|
|
37
|
+
|
|
38
|
+
ms = MolSuite(app_id="demo")
|
|
39
|
+
try:
|
|
40
|
+
ms.create_or_open_project(
|
|
41
|
+
name="table-demo",
|
|
42
|
+
folder=Path("./table-demo").resolve(),
|
|
43
|
+
activate=True,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
# A table sink writes to the project database: the table has to exist.
|
|
47
|
+
with sqlite3.connect(str(ms.project_db.db_path)) as conn:
|
|
48
|
+
conn.execute("DROP TABLE IF EXISTS results")
|
|
49
|
+
conn.execute("CREATE TABLE results (value INTEGER, double_value INTEGER)")
|
|
50
|
+
|
|
51
|
+
job_id = ms.run(
|
|
52
|
+
name="double-values",
|
|
53
|
+
input=[{"value": value} for value in (1, 2, 3, 4)],
|
|
54
|
+
process=double_value,
|
|
55
|
+
output=table_sink("results", columns=("value", "double_value")),
|
|
56
|
+
flush_every=2,
|
|
57
|
+
executor="thread",
|
|
58
|
+
)
|
|
59
|
+
final = ms.wait_for_job(job_id, poll_s=0.05)
|
|
60
|
+
print(final.status, final.chunks_done)
|
|
61
|
+
|
|
62
|
+
with sqlite3.connect(str(ms.project_db.db_path)) as conn:
|
|
63
|
+
rows = conn.execute("SELECT value, double_value FROM results ORDER BY value")
|
|
64
|
+
print(rows.fetchall())
|
|
65
|
+
finally:
|
|
66
|
+
ms.shutdown()
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Feed a job from project rows
|
|
70
|
+
|
|
71
|
+
The chunk carries the query, not the rows: they are read inside the worker.
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
import sqlite3
|
|
75
|
+
from pathlib import Path
|
|
76
|
+
|
|
77
|
+
from ms_flow.api import MolSuite
|
|
78
|
+
from ms_flow.query import db_input_for
|
|
79
|
+
from workers import summarize_molecules
|
|
80
|
+
|
|
81
|
+
ms = MolSuite(app_id="demo")
|
|
82
|
+
try:
|
|
83
|
+
ms.create_or_open_project(
|
|
84
|
+
name="query-demo",
|
|
85
|
+
folder=Path("./query-demo").resolve(),
|
|
86
|
+
activate=True,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
with sqlite3.connect(str(ms.project_db.db_path)) as conn:
|
|
90
|
+
conn.execute(
|
|
91
|
+
"CREATE TABLE IF NOT EXISTS molecules "
|
|
92
|
+
"(id INTEGER PRIMARY KEY, name TEXT, status TEXT)"
|
|
93
|
+
)
|
|
94
|
+
conn.execute("DELETE FROM molecules")
|
|
95
|
+
conn.executemany(
|
|
96
|
+
"INSERT INTO molecules (id, name, status) VALUES (?, ?, ?)",
|
|
97
|
+
[(1, "MolA", "ready"), (2, "MolB", "ready"), (3, "MolC", "archived")],
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
job_id = ms.run(
|
|
101
|
+
name="summarize-ready",
|
|
102
|
+
input=[
|
|
103
|
+
{
|
|
104
|
+
"molecules": db_input_for(
|
|
105
|
+
"molecules",
|
|
106
|
+
fields=("id", "name"),
|
|
107
|
+
filters={"status": "ready"},
|
|
108
|
+
order=("id",),
|
|
109
|
+
)
|
|
110
|
+
}
|
|
111
|
+
],
|
|
112
|
+
process=summarize_molecules,
|
|
113
|
+
executor="thread",
|
|
114
|
+
store_results=True,
|
|
115
|
+
)
|
|
116
|
+
final = ms.wait_for_job(job_id, poll_s=0.05)
|
|
117
|
+
print(final.status, ms.get_job_outputs(job_id))
|
|
118
|
+
finally:
|
|
119
|
+
ms.shutdown()
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Reusable streaming job
|
|
123
|
+
|
|
124
|
+
Declare the job once and submit it with different parameters.
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
from pathlib import Path
|
|
128
|
+
|
|
129
|
+
from ms_flow import watch_job
|
|
130
|
+
from ms_flow.api import MolSuite, streaming_job
|
|
131
|
+
from workers import double_value, value_chunks
|
|
132
|
+
|
|
133
|
+
double_job = streaming_job(
|
|
134
|
+
name="double-values",
|
|
135
|
+
run_chunk=double_value,
|
|
136
|
+
chunker=value_chunks,
|
|
137
|
+
executor="thread",
|
|
138
|
+
store_results=True,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
ms = MolSuite(app_id="demo")
|
|
142
|
+
try:
|
|
143
|
+
ms.create_or_open_project(
|
|
144
|
+
name="streaming-demo",
|
|
145
|
+
folder=Path("./streaming-demo").resolve(),
|
|
146
|
+
activate=True,
|
|
147
|
+
)
|
|
148
|
+
job_id = ms.submit_job(double_job, params={"count": 5})
|
|
149
|
+
final = watch_job(ms, job_id)
|
|
150
|
+
print(final.status, len(ms.get_job_outputs(job_id)))
|
|
151
|
+
finally:
|
|
152
|
+
ms.shutdown()
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Application runtime
|
|
156
|
+
|
|
157
|
+
A domain facade over `MolSuite` for an application.
|
|
158
|
+
|
|
159
|
+
```python
|
|
160
|
+
from pathlib import Path
|
|
161
|
+
|
|
162
|
+
from ms_flow.runtime import BaseRuntime
|
|
163
|
+
from workers import double_value
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
class DemoRuntime(BaseRuntime):
|
|
167
|
+
def __init__(self):
|
|
168
|
+
super().__init__("demoapp")
|
|
169
|
+
|
|
170
|
+
def double(self, values):
|
|
171
|
+
return self.run(
|
|
172
|
+
name="double-values",
|
|
173
|
+
input=[{"value": value} for value in values],
|
|
174
|
+
process=double_value,
|
|
175
|
+
executor="thread",
|
|
176
|
+
store_results=True,
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
runtime = DemoRuntime()
|
|
181
|
+
try:
|
|
182
|
+
runtime.create_or_open_project(
|
|
183
|
+
name="runtime-demo",
|
|
184
|
+
folder=Path("./runtime-demo").resolve(),
|
|
185
|
+
)
|
|
186
|
+
job_id = runtime.double([1, 2, 3])
|
|
187
|
+
final = runtime.molsuite.wait_for_job(job_id, poll_s=0.05)
|
|
188
|
+
print(final.status, runtime.molsuite.get_job_outputs(job_id))
|
|
189
|
+
finally:
|
|
190
|
+
runtime.shutdown()
|
|
191
|
+
```
|