gemstone-engine 1.2.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.
@@ -0,0 +1,12 @@
1
+ Creative Commons Attribution–NoDerivatives 4.0 International (CC BY-ND 4.0)
2
+ Copyright © 2025 Your Name
3
+
4
+ This work is licensed under the Creative Commons Attribution–NoDerivatives 4.0 International License.
5
+ You may share — copy and redistribute the material in any medium or format — under the following terms:
6
+
7
+ Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made.
8
+
9
+ No Derivatives — If you remix, transform, or build upon the material, you may not distribute the modified material.
10
+
11
+ To view a copy of this license, visit:
12
+ https://creativecommons.org/licenses/by-nd/4.0/
@@ -0,0 +1,109 @@
1
+ Metadata-Version: 2.4
2
+ Name: gemstone_engine
3
+ Version: 1.2.0
4
+ Summary: GEMSTONe: Generic Engine for Modular Software Task Orchestration and Negotiation
5
+ Author-email: Milo Fryling <milo.fryling@duke.edu>
6
+ License: CC BY-ND 4.0
7
+ Classifier: Programming Language :: Python :: 3
8
+ Requires-Python: >=3.8
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Dynamic: license-file
12
+
13
+ # GEMSTONe: Generic Engine for Modular Software Task Orchestration and Negotiation
14
+
15
+ ## Overview
16
+
17
+ GEMSTONe is an engine that runs customizable modular workflows. We use the term "module" in the usual sense — a standalone process that takes inputs and produces outputs.
18
+ When modules are linked together in a workflow, GEMSTONe refers to each one as a "stage"; otherwise terms "module" and "stage" can be used interchangeably.
19
+ Each stage is a standalone processing step: it reads input files, does its job, and writes outputs. A workflow links these stages together in sequence. GEMSTONe takes care of running them in order, passing data along the way.
20
+
21
+ Your role is to make each stage GEMSTONe-ready so the engine can:
22
+
23
+ - Start it with the right parameters
24
+ - Handle all file paths and connections between stages automatically
25
+
26
+ ## Install
27
+
28
+ ```bash
29
+ pip install gemstone-engine
30
+ ```
31
+
32
+ ## Design Goals
33
+
34
+ - **No proprietary language.** Simple and obvious human-readable syntax for all configuration files (`specs.txt`, `iofiles.txt`, `stageflows.txt`, etc.)
35
+
36
+ - **Boilerplate code eliminated.** Parameter validation, path management, file compatibility, and chaining are handled by the engine.
37
+
38
+ - **Zero coupling.** Module code and engine code are 100% independent — changes to one never require changes to the other.
39
+
40
+ ## Quick Start
41
+
42
+ To plug a stage into GEMSTONe, just include these three files:
43
+
44
+ - `specs.txt`: lists user input parameters and any defaults or constraints
45
+ - `iofiles.txt`: defines the stage’s inputs and outputs in table format
46
+ - `run.py`: contains `def run(params, paths):`, which launches your code
47
+ - Your module’s own implementation lives in a `runtime/` subdirectory inside the stage folder.
48
+
49
+ To connect stages into a workflow, add:
50
+
51
+ - `stageflows.txt`: shows how outputs from one stage become inputs to the next
52
+ - `standards.txt`: lists the types of files your workflow uses
53
+
54
+ That’s it. With these files in place, GEMSTONe handles path management and input validation automatically — no need to write custom code for filenames, directories, or parameter parsing.
55
+
56
+ ## Example Usage
57
+
58
+ **Launching a workflow**
59
+
60
+ A GEMSTONe workflow is started from the command line through the standard entry point:
61
+
62
+ ```python
63
+ from gemstone.cli import main
64
+
65
+ if __name__ == "__main__":
66
+ main()
67
+ ```
68
+
69
+ This accepts:
70
+
71
+ ```
72
+ <param file>
73
+ [<jobid override>]
74
+ [--testmode]
75
+ ```
76
+
77
+ **Inside a stage (`run.py`)**
78
+
79
+ Each stage defines:
80
+
81
+ ```python
82
+ def run(params: Params, paths: Paths):
83
+ param_file = paths.get_parameter_file_path()
84
+ write_param_file(param_file, params)
85
+ args = make_args(params, param_file, paths)
86
+
87
+ subprocess.run(
88
+ args,
89
+ cwd=paths.stage,
90
+ capture_output=True,
91
+ text=True,
92
+ check=True
93
+ )
94
+ ```
95
+
96
+ The engine supplies both `params` and `paths`; your code focuses only on launching the underlying executable.
97
+
98
+ ## Project Status
99
+
100
+ GEMSTONe is under active development and already in use for production research pipelines. Interfaces shown here are stable, but minor refinements are expected as the documentation and tooling expand.
101
+
102
+ ## Documentation
103
+
104
+ Full integration documentation will be released with the public source repository.
105
+ Until then, concise usage notes and guidance are available—please reach out through the project’s PyPI page or institutional contact channels if you need assistance integrating a module or workflow.
106
+
107
+ ## License
108
+
109
+ Creative Commons BY‑ND 4.0
@@ -0,0 +1,101 @@
1
+ ## Name
2
+ DukeSim 3 (provisional name)
3
+
4
+ ## Purpose (objectives & intended audience/users)
5
+ This provides a generic, extensible framework for running very loosely coupled software components as a pipeline.
6
+ It provides an engine for modular orchestration, and an app library containing templates and a sample application
7
+
8
+ ## OS Platform, Language, dependencies
9
+ The software runs on Linux and is written in Python.
10
+
11
+ ## How to build, install and/or publish
12
+ Use directly from a clone of the repo. There is no build or publishing procedure.
13
+ To reset or switch the active pipeline, rerun `use_app_stages.py` with a new app name.
14
+
15
+ ## User Documentation
16
+
17
+ **For full instructions and detailed information, see:**
18
+
19
+ ### `src/USERGUIDE.md`
20
+
21
+ ---
22
+
23
+ ### Quick Overview (For Reference Only):
24
+
25
+ #### Summary:
26
+ - `src/app_library`: Holds the catalog of pipelines.
27
+ - `src/stages`: Holds the working copy of the active app.
28
+ - `src/params`: Holds parameter files.
29
+ - `stages.py` and `dirs.py` (at app level): Define a pipeline.
30
+ - `specs.txt` and `run.py` (in each component): Define the parameters and launch interface.
31
+ - `src/engine`: *Must not* be modified by the user.
32
+
33
+ #### Common Commands:
34
+ - **Select an app to activate:**
35
+ ```bash
36
+ python3 src/use_app_stages.py <app_name> # App name is the directory in app_library
37
+ ```
38
+ - **Run the numerous unit tests in `src/tests`:**
39
+ ```bash
40
+ src/do_tests.sh
41
+ ```
42
+ - **Run a pipeline from the command line:**
43
+ ```bash
44
+ python3 run.py params/<param_file_name>
45
+ # Optional: override auto-generated Job ID
46
+ python3 run.py params/<param_file_name> <jobid_override>
47
+ ```
48
+
49
+ - **Activate app and run pipeline in one step (convenience wrapper):**
50
+ ```bash
51
+ ./run_app.sh <app_name> <param_file> [jobid_override] [--testmode]
52
+ ```
53
+ ## App directory structure
54
+ App Repos Will:
55
+
56
+ - Be Git repos named like G-Recon, G-VIT, etc.
57
+ - Be self-contained, not installed via pip, but invoked from repo root.
58
+ - Use the name stages/ for the directory containing stage code.
59
+ - Place stageflows.txt inside the stages/ directory.
60
+ - Place params_example.txt and run_app.py at the repo root.
61
+ - Place job outputs in output/ (created by the engine), located at the repo root.
62
+
63
+ ## Packaging the engine and building apps
64
+ App Dev with GEMSTONe Clone on Same Server
65
+ - git clone git@gitlab.com:gemstone-world/GEMSTONe.git
66
+ - cd GEMSTONe
67
+ - pip install -e .
68
+
69
+ - cd ~/repos
70
+ - git clone git@gitlab.com:gemstone-world/G-Recon.git
71
+ - cd G-Recon
72
+ - python run_app.py params_example.txt
73
+
74
+ To update engine:
75
+ cd ~/repos/GEMSTONe
76
+ git pull
77
+
78
+ App Dev in the Field (pip install only)
79
+ - pip install gemstone_engine
80
+
81
+ - cd ~/repos
82
+ - git clone git@gitlab.com:gemstone-world/G-Recon.git
83
+ - cd G-Recon
84
+ - python run_app.py params_example.txt
85
+
86
+ To update engine:
87
+ pip install --upgrade gemstone_engine
88
+
89
+ Engine Publishing (per release)
90
+ - cd GEMSTONe
91
+ - git pull
92
+ - (bump version in pyproject.toml)
93
+ - python -m build
94
+ - twine upload dist/*
95
+
96
+ Then users can do:
97
+ pip install gemstone_engine
98
+ pip install --upgrade gemstone_engine
99
+
100
+ ## License details
101
+ No licensing has been considered yet.
@@ -0,0 +1,97 @@
1
+ # GEMSTONe: Generic Engine for Modular Software Task Orchestration and Negotiation
2
+
3
+ ## Overview
4
+
5
+ GEMSTONe is an engine that runs customizable modular workflows. We use the term "module" in the usual sense — a standalone process that takes inputs and produces outputs.
6
+ When modules are linked together in a workflow, GEMSTONe refers to each one as a "stage"; otherwise terms "module" and "stage" can be used interchangeably.
7
+ Each stage is a standalone processing step: it reads input files, does its job, and writes outputs. A workflow links these stages together in sequence. GEMSTONe takes care of running them in order, passing data along the way.
8
+
9
+ Your role is to make each stage GEMSTONe-ready so the engine can:
10
+
11
+ - Start it with the right parameters
12
+ - Handle all file paths and connections between stages automatically
13
+
14
+ ## Install
15
+
16
+ ```bash
17
+ pip install gemstone-engine
18
+ ```
19
+
20
+ ## Design Goals
21
+
22
+ - **No proprietary language.** Simple and obvious human-readable syntax for all configuration files (`specs.txt`, `iofiles.txt`, `stageflows.txt`, etc.)
23
+
24
+ - **Boilerplate code eliminated.** Parameter validation, path management, file compatibility, and chaining are handled by the engine.
25
+
26
+ - **Zero coupling.** Module code and engine code are 100% independent — changes to one never require changes to the other.
27
+
28
+ ## Quick Start
29
+
30
+ To plug a stage into GEMSTONe, just include these three files:
31
+
32
+ - `specs.txt`: lists user input parameters and any defaults or constraints
33
+ - `iofiles.txt`: defines the stage’s inputs and outputs in table format
34
+ - `run.py`: contains `def run(params, paths):`, which launches your code
35
+ - Your module’s own implementation lives in a `runtime/` subdirectory inside the stage folder.
36
+
37
+ To connect stages into a workflow, add:
38
+
39
+ - `stageflows.txt`: shows how outputs from one stage become inputs to the next
40
+ - `standards.txt`: lists the types of files your workflow uses
41
+
42
+ That’s it. With these files in place, GEMSTONe handles path management and input validation automatically — no need to write custom code for filenames, directories, or parameter parsing.
43
+
44
+ ## Example Usage
45
+
46
+ **Launching a workflow**
47
+
48
+ A GEMSTONe workflow is started from the command line through the standard entry point:
49
+
50
+ ```python
51
+ from gemstone.cli import main
52
+
53
+ if __name__ == "__main__":
54
+ main()
55
+ ```
56
+
57
+ This accepts:
58
+
59
+ ```
60
+ <param file>
61
+ [<jobid override>]
62
+ [--testmode]
63
+ ```
64
+
65
+ **Inside a stage (`run.py`)**
66
+
67
+ Each stage defines:
68
+
69
+ ```python
70
+ def run(params: Params, paths: Paths):
71
+ param_file = paths.get_parameter_file_path()
72
+ write_param_file(param_file, params)
73
+ args = make_args(params, param_file, paths)
74
+
75
+ subprocess.run(
76
+ args,
77
+ cwd=paths.stage,
78
+ capture_output=True,
79
+ text=True,
80
+ check=True
81
+ )
82
+ ```
83
+
84
+ The engine supplies both `params` and `paths`; your code focuses only on launching the underlying executable.
85
+
86
+ ## Project Status
87
+
88
+ GEMSTONe is under active development and already in use for production research pipelines. Interfaces shown here are stable, but minor refinements are expected as the documentation and tooling expand.
89
+
90
+ ## Documentation
91
+
92
+ Full integration documentation will be released with the public source repository.
93
+ Until then, concise usage notes and guidance are available—please reach out through the project’s PyPI page or institutional contact channels if you need assistance integrating a module or workflow.
94
+
95
+ ## License
96
+
97
+ Creative Commons BY‑ND 4.0
@@ -0,0 +1,24 @@
1
+ [build-system]
2
+ requires = ["setuptools", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "gemstone_engine" # distribution name (pip install gemstone_engine)
7
+ version = "1.2.0" # bump on each release
8
+ description = "GEMSTONe: Generic Engine for Modular Software Task Orchestration and Negotiation"
9
+ authors = [
10
+ { name="Milo Fryling", email="milo.fryling@duke.edu" }
11
+ ]
12
+ readme = "README_PyPI.md"
13
+ license = { text = "CC BY-ND 4.0" }
14
+ requires-python = ">=3.8"
15
+ classifiers = [ "Programming Language :: Python :: 3" ]
16
+
17
+ # what gets installed as the importable package
18
+ [tool.setuptools.packages.find]
19
+ include = ["gemstone"]
20
+ where = ["src"]
21
+
22
+
23
+ [project.scripts]
24
+ gemstone = "gemstone.cli:main"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,15 @@
1
+ # setup.py
2
+ from setuptools import setup, find_packages
3
+
4
+ setup(
5
+ name="gemstone_engine",
6
+ version="1.0.0",
7
+ packages=find_packages(where="src"),
8
+ package_dir={"": "src"},
9
+ install_requires=[],
10
+ entry_points={
11
+ "console_scripts": [
12
+ "gemstone = gemstone.cli:main",
13
+ ],
14
+ },
15
+ )
File without changes
@@ -0,0 +1,149 @@
1
+ import argparse
2
+ import os
3
+ import sys
4
+ import logging
5
+ import subprocess
6
+ from datetime import datetime
7
+ from importlib.metadata import version as pkg_version, PackageNotFoundError
8
+ from gemstone.pipeliner import run_pipeline
9
+ import gemstone.dir as Dir
10
+
11
+ class ExitCode:
12
+ """Standardized exit codes for pipeline errors."""
13
+ MISSING_FILE = 99
14
+ CONFIG_ERROR = 2
15
+ PERMISSION_ERROR = 3
16
+ KEY_ERROR = 4
17
+ RUNTIME_ERROR = 1
18
+ UNKNOWN_ERROR = 100
19
+
20
+ def get_version() -> str:
21
+ """Returns GEMSTONe version from package metadata (installed) or git (dev)."""
22
+ try:
23
+ return pkg_version("gemstone_engine") # Use actual package name
24
+ except PackageNotFoundError:
25
+ try:
26
+ return subprocess.check_output(
27
+ ["git", "describe", "--tags", "--dirty", "--always"],
28
+ stderr=subprocess.DEVNULL,
29
+ ).decode().strip()
30
+ except Exception:
31
+ return "unknown"
32
+
33
+ def display_version(terse:bool = False):
34
+ """Displays the pipeline version (from git describe)."""
35
+ version = get_version()
36
+
37
+ if terse:
38
+ print(f" GEMSTONe {version}")
39
+ else:
40
+ print('===========================')
41
+ print(f" GEMSTONe {version}")
42
+ print('===========================')
43
+
44
+ def show_error(msg, exit_code):
45
+ """Prints and logs an error message, then exits."""
46
+ print(msg)
47
+ logging.error(msg)
48
+ sys.exit(exit_code)
49
+
50
+ def main():
51
+ """Entry point to start the pipeline."""
52
+ parser = argparse.ArgumentParser(description="Run a GEMSTONe pipeline.")
53
+ parser.add_argument(
54
+ "params",
55
+ type=str,
56
+ nargs="?", # Make this argument optional
57
+ help="Path to the param file."
58
+ )
59
+ parser.add_argument(
60
+ "jobid_override",
61
+ type=str,
62
+ nargs="?", # Optional positional
63
+ help="Optional: override the auto-generated job ID, e.g. to restart a job."
64
+ )
65
+ parser.add_argument(
66
+ "--test_mode",
67
+ action="store_true",
68
+ help="Optional: run in test mode (create outputs without executing real code)."
69
+ )
70
+ parser.add_argument(
71
+ "--version",
72
+ action="store_true",
73
+ help="Display GEMSTONe version and exit.")
74
+
75
+ args = parser.parse_args()
76
+ if args.version:
77
+ display_version(True)
78
+ return
79
+
80
+ display_version(False)
81
+ setup_logging()
82
+
83
+ if args.test_mode:
84
+ logging.warning("TEST MODE ENABLED — no real execution will occur.")
85
+
86
+ try:
87
+ Dir.set_root(os.getcwd())
88
+ run_pipeline(args.params, args.jobid_override, test_mode=args.test_mode)
89
+
90
+ except FileNotFoundError as e:
91
+ show_error(f"Error: {e}", ExitCode.MISSING_FILE)
92
+
93
+ except ValueError as e:
94
+ show_error(f"Configuration error: {e}", ExitCode.CONFIG_ERROR)
95
+
96
+ except PermissionError as e:
97
+ show_error(f"Permission error: {e}", ExitCode.PERMISSION_ERROR)
98
+
99
+ except RuntimeError as e:
100
+ show_error(f"Runtime error: {e}", ExitCode.RUNTIME_ERROR)
101
+
102
+ except KeyError as e:
103
+ show_error(f"Param key error: {e}", ExitCode.KEY_ERROR)
104
+
105
+ except Exception as e:
106
+ logging.exception("Unexpected error:")
107
+ show_error("An unexpected error occurred. Check the log for details.", ExitCode.UNKNOWN_ERROR)
108
+
109
+ class DotMillisecondsFormatter(logging.Formatter):
110
+ def formatTime(self, record, datefmt=None):
111
+ t = datetime.fromtimestamp(record.created)
112
+ return t.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3] # Keep milliseconds only
113
+
114
+ def setup_logging():
115
+ """Set up logging with consistent format and millisecond timestamps."""
116
+ log_file = "GEMSTONe.log"
117
+
118
+ root_logger = logging.getLogger()
119
+ root_logger.setLevel(logging.INFO)
120
+
121
+ # Remove any existing handlers
122
+ for handler in root_logger.handlers[:]:
123
+ root_logger.removeHandler(handler)
124
+
125
+ formatter = DotMillisecondsFormatter(
126
+ "%(asctime)s - %(levelname)s - %(message)s"
127
+ )
128
+
129
+ file_handler = logging.FileHandler(log_file)
130
+ file_handler.setLevel(logging.INFO)
131
+ file_handler.setFormatter(formatter)
132
+
133
+ console_handler = logging.StreamHandler(sys.stdout)
134
+ console_handler.setLevel(logging.INFO)
135
+ console_handler.setFormatter(formatter)
136
+
137
+ root_logger.addHandler(file_handler)
138
+ root_logger.addHandler(console_handler)
139
+
140
+ # Ensure submodules propagate to root
141
+ logging.getLogger('subprocess').propagate = True
142
+ logging.getLogger('gemstone').propagate = True
143
+ logging.getLogger('stages').propagate = True
144
+
145
+ logging.info(f"Logging to file: {log_file}")
146
+
147
+
148
+ if __name__ == "__main__":
149
+ main()
@@ -0,0 +1,22 @@
1
+ # dir.py - absolute path directory layout constants in deployed environment
2
+ import os
3
+
4
+ root = "."
5
+ stages = "."
6
+ output = "."
7
+
8
+ def set_root(root_path):
9
+ global root, stages, output
10
+ root = os.path.abspath(root_path)
11
+ stages = os.path.join(root, "stages")
12
+ output = os.path.join(root, "output")
13
+
14
+ def validate(subdirs=None):
15
+ if root in (".", None):
16
+ raise RuntimeError("Dir.set_root() must be called before validate()")
17
+
18
+ check = subdirs or ["stages", "output"]
19
+ for d in check:
20
+ full = os.path.join(root, d)
21
+ if not os.path.isdir(full):
22
+ raise RuntimeError(f"Missing required directory: {full}")