quapp-hpc 0.0.1.dev5__tar.gz → 0.0.1.dev7__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 (30) hide show
  1. {quapp_hpc-0.0.1.dev5/quapp_hpc.egg-info → quapp_hpc-0.0.1.dev7}/PKG-INFO +1 -1
  2. {quapp_hpc-0.0.1.dev5 → quapp_hpc-0.0.1.dev7}/pyproject.toml +33 -33
  3. {quapp_hpc-0.0.1.dev5 → quapp_hpc-0.0.1.dev7/quapp_hpc.egg-info}/PKG-INFO +1 -1
  4. {quapp_hpc-0.0.1.dev5 → quapp_hpc-0.0.1.dev7}/quapp_hpc.egg-info/SOURCES.txt +3 -1
  5. quapp_hpc-0.0.1.dev7/quapp_hpc.egg-info/top_level.txt +2 -0
  6. quapp_hpc-0.0.1.dev7/quapp_slurm_hpc/__init__.py +0 -0
  7. quapp_hpc-0.0.1.dev7/quapp_slurm_hpc/runner.py +142 -0
  8. quapp_hpc-0.0.1.dev5/quapp_hpc.egg-info/top_level.txt +0 -1
  9. {quapp_hpc-0.0.1.dev5 → quapp_hpc-0.0.1.dev7}/LICENSE +0 -0
  10. {quapp_hpc-0.0.1.dev5 → quapp_hpc-0.0.1.dev7}/README.md +0 -0
  11. {quapp_hpc-0.0.1.dev5 → quapp_hpc-0.0.1.dev7}/quapp_hpc/__init__.py +0 -0
  12. {quapp_hpc-0.0.1.dev5 → quapp_hpc-0.0.1.dev7}/quapp_hpc/component/__init__.py +0 -0
  13. {quapp_hpc-0.0.1.dev5 → quapp_hpc-0.0.1.dev7}/quapp_hpc/component/backend/__init__.py +0 -0
  14. {quapp_hpc-0.0.1.dev5 → quapp_hpc-0.0.1.dev7}/quapp_hpc/component/backend/hpc_invocation.py +0 -0
  15. {quapp_hpc-0.0.1.dev5 → quapp_hpc-0.0.1.dev7}/quapp_hpc/component/backend/slurm_job_fetching.py +0 -0
  16. {quapp_hpc-0.0.1.dev5 → quapp_hpc-0.0.1.dev7}/quapp_hpc/factory/__init__.py +0 -0
  17. {quapp_hpc-0.0.1.dev5 → quapp_hpc-0.0.1.dev7}/quapp_hpc/factory/hpc_device_factory.py +0 -0
  18. {quapp_hpc-0.0.1.dev5 → quapp_hpc-0.0.1.dev7}/quapp_hpc/factory/hpc_handler_factory.py +0 -0
  19. {quapp_hpc-0.0.1.dev5 → quapp_hpc-0.0.1.dev7}/quapp_hpc/factory/hpc_provider_factory.py +0 -0
  20. {quapp_hpc-0.0.1.dev5 → quapp_hpc-0.0.1.dev7}/quapp_hpc/handler/__init__.py +0 -0
  21. {quapp_hpc-0.0.1.dev5 → quapp_hpc-0.0.1.dev7}/quapp_hpc/handler/invocation_handler.py +0 -0
  22. {quapp_hpc-0.0.1.dev5 → quapp_hpc-0.0.1.dev7}/quapp_hpc/handler/job_fetching_handler.py +0 -0
  23. {quapp_hpc-0.0.1.dev5 → quapp_hpc-0.0.1.dev7}/quapp_hpc/model/__init__.py +0 -0
  24. {quapp_hpc-0.0.1.dev5 → quapp_hpc-0.0.1.dev7}/quapp_hpc/model/device/__init__.py +0 -0
  25. {quapp_hpc-0.0.1.dev5 → quapp_hpc-0.0.1.dev7}/quapp_hpc/model/device/slurm_device.py +0 -0
  26. {quapp_hpc-0.0.1.dev5 → quapp_hpc-0.0.1.dev7}/quapp_hpc/model/provider/__init__.py +0 -0
  27. {quapp_hpc-0.0.1.dev5 → quapp_hpc-0.0.1.dev7}/quapp_hpc/model/provider/slurm_provider.py +0 -0
  28. {quapp_hpc-0.0.1.dev5 → quapp_hpc-0.0.1.dev7}/quapp_hpc.egg-info/dependency_links.txt +0 -0
  29. {quapp_hpc-0.0.1.dev5 → quapp_hpc-0.0.1.dev7}/quapp_hpc.egg-info/requires.txt +0 -0
  30. {quapp_hpc-0.0.1.dev5 → quapp_hpc-0.0.1.dev7}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quapp-hpc
3
- Version: 0.0.1.dev5
3
+ Version: 0.0.1.dev7
4
4
  Summary: Quapp HPC library — Slurm integration for Quapp Platform
5
5
  Author-email: "CITYNOW Co. Ltd." <corp@citynow.vn>
6
6
  License: The MIT License (MIT)
@@ -1,33 +1,33 @@
1
- [build-system]
2
- requires = ["setuptools>=61.0.0", "wheel==0.45.1"]
3
- build-backend = "setuptools.build_meta"
4
-
5
- [project]
6
- name = "quapp-hpc"
7
- version = "0.0.1.dev5"
8
- description = "Quapp HPC library — Slurm integration for Quapp Platform"
9
- readme = "README.md"
10
- authors = [{ name = "CITYNOW Co. Ltd.", email = "corp@citynow.vn" }]
11
- license = { file = "LICENSE" }
12
- classifiers = [
13
- "License :: OSI Approved :: MIT License",
14
- "Programming Language :: Python",
15
- "Programming Language :: Python :: 3",
16
- ]
17
- keywords = ["quapp", "quapp-hpc", "slurm", "hpc"]
18
- dependencies = [
19
- "quapp-common==0.0.12.dev12",
20
- "requests>=2.31.0",
21
- "boto3>=1.28.0",
22
- ]
23
- requires-python = ">=3.10,<3.13"
24
-
25
- [project.optional-dependencies]
26
- dev = ["black", "bumpver", "isort", "pip-tools", "pytest"]
27
-
28
- [tool.setuptools.packages.find]
29
- include = ["quapp_hpc*"]
30
- exclude = ["*.md", "*.yml", "*.yaml", "*.toml", "tests*", ".gitignore"]
31
-
32
- [project.urls]
33
- Homepage = "https://quapp.cloud/"
1
+ [build-system]
2
+ requires = ["setuptools>=61.0.0", "wheel==0.45.1"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "quapp-hpc"
7
+ version = "0.0.1.dev7"
8
+ description = "Quapp HPC library — Slurm integration for Quapp Platform"
9
+ readme = "README.md"
10
+ authors = [{ name = "CITYNOW Co. Ltd.", email = "corp@citynow.vn" }]
11
+ license = { file = "LICENSE" }
12
+ classifiers = [
13
+ "License :: OSI Approved :: MIT License",
14
+ "Programming Language :: Python",
15
+ "Programming Language :: Python :: 3",
16
+ ]
17
+ keywords = ["quapp", "quapp-hpc", "slurm", "hpc"]
18
+ dependencies = [
19
+ "quapp-common==0.0.12.dev12",
20
+ "requests>=2.31.0",
21
+ "boto3>=1.28.0",
22
+ ]
23
+ requires-python = ">=3.10,<3.13"
24
+
25
+ [project.optional-dependencies]
26
+ dev = ["black", "bumpver", "isort", "pip-tools", "pytest"]
27
+
28
+ [tool.setuptools.packages.find]
29
+ include = ["quapp_hpc*", "quapp_slurm_hpc*"]
30
+ exclude = ["*.md", "*.yml", "*.yaml", "*.toml", "tests*", ".gitignore"]
31
+
32
+ [project.urls]
33
+ Homepage = "https://quapp.cloud/"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quapp-hpc
3
- Version: 0.0.1.dev5
3
+ Version: 0.0.1.dev7
4
4
  Summary: Quapp HPC library — Slurm integration for Quapp Platform
5
5
  Author-email: "CITYNOW Co. Ltd." <corp@citynow.vn>
6
6
  License: The MIT License (MIT)
@@ -22,4 +22,6 @@ quapp_hpc/model/__init__.py
22
22
  quapp_hpc/model/device/__init__.py
23
23
  quapp_hpc/model/device/slurm_device.py
24
24
  quapp_hpc/model/provider/__init__.py
25
- quapp_hpc/model/provider/slurm_provider.py
25
+ quapp_hpc/model/provider/slurm_provider.py
26
+ quapp_slurm_hpc/__init__.py
27
+ quapp_slurm_hpc/runner.py
@@ -0,0 +1,2 @@
1
+ quapp_hpc
2
+ quapp_slurm_hpc
File without changes
@@ -0,0 +1,142 @@
1
+ """CLI runner — invoked by the sbatch script inside the Apptainer container.
2
+
3
+ The Java SlurmPlatformManager generates a sbatch script that runs:
4
+
5
+ apptainer exec docker://<image> python -m quapp_slurm_hpc.runner \
6
+ --input-file /data/jobs/<jobId>/input.json \
7
+ --output-file /data/jobs/<jobId>/output.json \
8
+ --handler-dir /home/app/function
9
+
10
+ This module:
11
+ 1. Reads the invocation input from --input-file (JSON written by the sbatch script).
12
+ 2. Loads handler.py from --handler-dir.
13
+ 3. Calls handler.processing(invocation_input) → bash script string.
14
+ 4. Executes the bash script, captures stdout / stderr / exit_code.
15
+ 5. Calls handler.post_processing(raw_result) if it exists.
16
+ 6. Writes the final result dict to --output-file.
17
+ 7. Uploads output.json to S3 (key: {QUAPP_JOB_ID}/output.json) when SLURM_S3_BUCKET is set.
18
+ """
19
+ import argparse
20
+ import importlib.util
21
+ import json
22
+ import os
23
+ import subprocess
24
+ import sys
25
+ import tempfile
26
+ import traceback
27
+ from pathlib import Path
28
+ from types import ModuleType
29
+
30
+
31
+ def _load_handler(handler_dir: str) -> ModuleType:
32
+ handler_path = Path(handler_dir) / "handler.py"
33
+ if not handler_path.exists():
34
+ raise FileNotFoundError(
35
+ f"handler.py not found in {handler_dir!r}. "
36
+ "Ensure the function image was built with the handler in /home/app/function."
37
+ )
38
+ if str(handler_dir) not in sys.path:
39
+ sys.path.insert(0, str(handler_dir))
40
+ spec = importlib.util.spec_from_file_location("handler", handler_path)
41
+ module = importlib.util.module_from_spec(spec)
42
+ spec.loader.exec_module(module)
43
+ return module
44
+
45
+
46
+ def _run_script(script: str) -> dict:
47
+ """Write script to a temp file and execute it with bash, capturing output."""
48
+ with tempfile.NamedTemporaryFile(
49
+ mode="w", suffix=".sh", delete=False, encoding="utf-8"
50
+ ) as f:
51
+ f.write(script)
52
+ tmp_path = f.name
53
+ try:
54
+ result = subprocess.run(
55
+ ["bash", tmp_path],
56
+ capture_output=True,
57
+ text=True,
58
+ )
59
+ return {
60
+ "exit_code": result.returncode,
61
+ "stdout": result.stdout,
62
+ "stderr": result.stderr,
63
+ }
64
+ finally:
65
+ Path(tmp_path).unlink(missing_ok=True)
66
+
67
+
68
+ def main() -> None:
69
+ parser = argparse.ArgumentParser(
70
+ description="Quapp HPC runner — executes handler.py inside an Apptainer container"
71
+ )
72
+ parser.add_argument("--input-file", required=True, help="Path to input JSON file")
73
+ parser.add_argument("--output-file", required=True, help="Path to write output JSON")
74
+ parser.add_argument("--handler-dir", required=True, help="Directory containing handler.py")
75
+ args = parser.parse_args()
76
+
77
+ output: dict = {}
78
+ try:
79
+ with open(args.input_file, encoding="utf-8") as f:
80
+ invocation_input = json.load(f)
81
+
82
+ handler = _load_handler(args.handler_dir)
83
+
84
+ if not hasattr(handler, "processing"):
85
+ raise AttributeError(
86
+ "handler.py must define a processing(invocation_input: dict) -> str function"
87
+ )
88
+
89
+ script = handler.processing(invocation_input)
90
+ if not isinstance(script, str):
91
+ raise TypeError(
92
+ f"handler.processing() must return a str (bash script), got {type(script).__name__}"
93
+ )
94
+
95
+ raw_result = _run_script(script)
96
+
97
+ if hasattr(handler, "post_processing"):
98
+ output = handler.post_processing(raw_result)
99
+ else:
100
+ output = raw_result
101
+
102
+ except Exception:
103
+ output = {
104
+ "exit_code": 1,
105
+ "stdout": "",
106
+ "stderr": traceback.format_exc(),
107
+ }
108
+
109
+ with open(args.output_file, "w", encoding="utf-8") as f:
110
+ json.dump(output, f)
111
+
112
+ _upload_to_s3(args.output_file)
113
+
114
+
115
+ def _upload_to_s3(output_file: str) -> None:
116
+ """Upload output.json to S3 so the Java backend can read the result.
117
+
118
+ Requires SLURM_S3_BUCKET and QUAPP_JOB_ID env vars (injected by sbatch script).
119
+ Skips silently when the bucket is not configured.
120
+ """
121
+ s3_bucket = os.getenv("SLURM_S3_BUCKET", "")
122
+ job_uuid = os.getenv("QUAPP_JOB_ID", "")
123
+ if not s3_bucket or not job_uuid:
124
+ return
125
+
126
+ try:
127
+ import boto3
128
+ s3 = boto3.client(
129
+ "s3",
130
+ region_name=os.getenv("SLURM_S3_REGION", "ap-southeast-1"),
131
+ aws_access_key_id=os.getenv("SLURM_S3_ACCESS_KEY"),
132
+ aws_secret_access_key=os.getenv("SLURM_S3_SECRET_KEY"),
133
+ )
134
+ s3_key = f"{job_uuid}/output.json"
135
+ s3.upload_file(output_file, s3_bucket, s3_key)
136
+ print(f"[runner] Uploaded output to s3://{s3_bucket}/{s3_key}", flush=True)
137
+ except Exception as exc:
138
+ print(f"[runner] WARNING: S3 upload failed: {exc}", file=sys.stderr, flush=True)
139
+
140
+
141
+ if __name__ == "__main__":
142
+ main()
@@ -1 +0,0 @@
1
- quapp_hpc
File without changes
File without changes
File without changes