hpc-runner 0.2.2__tar.gz → 0.3.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.
Files changed (93) hide show
  1. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/PKG-INFO +1 -1
  2. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/_version.py +2 -2
  3. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/schedulers/local/scheduler.py +70 -41
  4. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/schedulers/local/templates/job.sh.j2 +22 -4
  5. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/tests/test_schedulers/test_local.py +2 -1
  6. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/.github/workflows/ci.yml +0 -0
  7. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/.github/workflows/docs.yml +0 -0
  8. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/.github/workflows/publish.yml +0 -0
  9. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/.gitignore +0 -0
  10. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/CLAUDE.md +0 -0
  11. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/README.md +0 -0
  12. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/defaults/config.toml +0 -0
  13. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/docs/HPC_MONITOR_TUI_PLAN.md +0 -0
  14. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/docs/Makefile +0 -0
  15. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/docs/TEXTUAL_STYLING_COOKBOOK.md +0 -0
  16. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/docs/source/_static/.gitkeep +0 -0
  17. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/docs/source/_templates/.gitkeep +0 -0
  18. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/docs/source/cli.rst +0 -0
  19. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/docs/source/conf.py +0 -0
  20. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/docs/source/configuration.rst +0 -0
  21. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/docs/source/getting_started.rst +0 -0
  22. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/docs/source/index.rst +0 -0
  23. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/docs/source/programmatic_api.rst +0 -0
  24. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/docs/source/sge.rst +0 -0
  25. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/pyproject.toml +0 -0
  26. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/sourceme +0 -0
  27. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/__init__.py +0 -0
  28. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/cli/__init__.py +0 -0
  29. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/cli/cancel.py +0 -0
  30. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/cli/config.py +0 -0
  31. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/cli/main.py +0 -0
  32. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/cli/monitor.py +0 -0
  33. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/cli/run.py +0 -0
  34. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/cli/status.py +0 -0
  35. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/cli/submit.py +0 -0
  36. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/core/__init__.py +0 -0
  37. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/core/config.py +0 -0
  38. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/core/descriptors.py +0 -0
  39. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/core/exceptions.py +0 -0
  40. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/core/job.py +0 -0
  41. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/core/job_array.py +0 -0
  42. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/core/job_info.py +0 -0
  43. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/core/resources.py +0 -0
  44. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/core/result.py +0 -0
  45. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/core/types.py +0 -0
  46. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/py.typed +0 -0
  47. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/schedulers/__init__.py +0 -0
  48. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/schedulers/base.py +0 -0
  49. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/schedulers/detection.py +0 -0
  50. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/schedulers/local/__init__.py +0 -0
  51. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/schedulers/sge/__init__.py +0 -0
  52. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/schedulers/sge/args.py +0 -0
  53. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/schedulers/sge/parser.py +0 -0
  54. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/schedulers/sge/scheduler.py +0 -0
  55. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/schedulers/sge/templates/batch.sh.j2 +0 -0
  56. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/schedulers/sge/templates/interactive.sh.j2 +0 -0
  57. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/templates/__init__.py +0 -0
  58. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/templates/engine.py +0 -0
  59. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/tui/__init__.py +0 -0
  60. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/tui/app.py +0 -0
  61. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/tui/components/__init__.py +0 -0
  62. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/tui/components/detail_panel.py +0 -0
  63. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/tui/components/filter_bar.py +0 -0
  64. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/tui/components/filter_popup.py +0 -0
  65. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/tui/components/job_table.py +0 -0
  66. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/tui/providers/__init__.py +0 -0
  67. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/tui/providers/jobs.py +0 -0
  68. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/tui/screens/__init__.py +0 -0
  69. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/tui/screens/confirm.py +0 -0
  70. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/tui/screens/job_details.py +0 -0
  71. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/tui/screens/log_viewer.py +0 -0
  72. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/tui/snapshot.py +0 -0
  73. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/tui/styles/monitor.tcss +0 -0
  74. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/workflow/__init__.py +0 -0
  75. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/workflow/dependency.py +0 -0
  76. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/src/hpc_runner/workflow/pipeline.py +0 -0
  77. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/tests/__init__.py +0 -0
  78. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/tests/conftest.py +0 -0
  79. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/tests/test_cli/__init__.py +0 -0
  80. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/tests/test_cli/test_run.py +0 -0
  81. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/tests/test_core/__init__.py +0 -0
  82. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/tests/test_core/test_config.py +0 -0
  83. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/tests/test_core/test_job.py +0 -0
  84. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/tests/test_core/test_resources.py +0 -0
  85. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/tests/test_schedulers/__init__.py +0 -0
  86. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/tests/test_schedulers/test_detection.py +0 -0
  87. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/tests/test_schedulers/test_sge.py +0 -0
  88. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/tests/test_tui/__init__.py +0 -0
  89. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/tests/test_tui/test_app_snapshot.py +0 -0
  90. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/tests/test_tui/test_detail_panel.py +0 -0
  91. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/tests/test_tui/test_job_table.py +0 -0
  92. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/tests/test_workflow/__init__.py +0 -0
  93. {hpc_runner-0.2.2 → hpc_runner-0.3.0}/tests/test_workflow/test_pipeline.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hpc-runner
3
- Version: 0.2.2
3
+ Version: 0.3.0
4
4
  Summary: Unified HPC job submission across multiple schedulers
5
5
  Project-URL: Homepage, https://github.com/sjalloq/hpc-runner
6
6
  Project-URL: Repository, https://github.com/sjalloq/hpc-runner
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '0.2.2'
32
- __version_tuple__ = version_tuple = (0, 2, 2)
31
+ __version__ = version = '0.3.0'
32
+ __version_tuple__ = version_tuple = (0, 3, 0)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -9,6 +9,7 @@ from datetime import datetime
9
9
  from pathlib import Path
10
10
  from typing import TYPE_CHECKING
11
11
 
12
+ from hpc_runner.core.config import get_config
12
13
  from hpc_runner.core.exceptions import AccountingNotAvailable, JobNotFoundError
13
14
  from hpc_runner.core.job_info import JobInfo
14
15
  from hpc_runner.core.result import ArrayJobResult, JobResult, JobStatus
@@ -26,6 +27,14 @@ class LocalScheduler(BaseScheduler):
26
27
  name = "local"
27
28
 
28
29
  _job_counter: int = 0
30
+
31
+ def __init__(self) -> None:
32
+ """Initialize local scheduler with config-driven settings."""
33
+ config = get_config()
34
+ local_config = config.get_scheduler_config("local")
35
+
36
+ self.module_init_script = local_config.get("module_init_script", "")
37
+
29
38
  _processes: dict[str, subprocess.Popen] = {} # type: ignore[type-arg]
30
39
  _exit_codes: dict[str, int] = {}
31
40
  _output_paths: dict[str, dict[str, Path]] = {}
@@ -37,8 +46,9 @@ class LocalScheduler(BaseScheduler):
37
46
  LocalScheduler._job_counter += 1
38
47
  job_id = f"local_{LocalScheduler._job_counter}_{datetime.now().strftime('%Y%m%d%H%M%S')}"
39
48
 
40
- # Set up environment with modules (modules not actually loaded locally)
49
+ # Set up environment
41
50
  env = os.environ.copy() if job.inherit_env else {}
51
+ env.update(job.env_vars)
42
52
 
43
53
  # Generate and write script
44
54
  script = self.generate_script(job)
@@ -48,69 +58,87 @@ class LocalScheduler(BaseScheduler):
48
58
 
49
59
  workdir = Path(job.workdir) if job.workdir else Path.cwd()
50
60
 
51
- # Determine output paths
52
- stdout_file = job.stdout or f"{job.name}.{job_id}.out"
53
- stdout_path = workdir / stdout_file
54
- if job.merge_output:
55
- stderr_path = stdout_path # Merge stderr into stdout
56
- else:
57
- stderr_file = job.stderr or f"{job.name}.{job_id}.err"
58
- stderr_path = workdir / stderr_file
61
+ # Check if output should pass through to console (no redirection)
62
+ passthrough = job.stdout is None and job.stderr is None
63
+
64
+ if not passthrough:
65
+ # Determine output paths
66
+ stdout_file = job.stdout or f"{job.name}.{job_id}.out"
67
+ stdout_path = workdir / stdout_file
68
+ if job.merge_output:
69
+ stderr_path = stdout_path # Merge stderr into stdout
70
+ else:
71
+ stderr_file = job.stderr or f"{job.name}.{job_id}.err"
72
+ stderr_path = workdir / stderr_file
59
73
 
60
- # Store output paths
61
- LocalScheduler._output_paths[job_id] = {
62
- "stdout": stdout_path,
63
- "stderr": stderr_path,
64
- }
74
+ # Store output paths
75
+ LocalScheduler._output_paths[job_id] = {
76
+ "stdout": stdout_path,
77
+ "stderr": stderr_path,
78
+ }
65
79
 
66
80
  if interactive:
67
81
  # Blocking execution
68
- with open(stdout_path, "w") as stdout_f:
69
- if job.merge_output:
70
- result = subprocess.run(
71
- [str(script_path)],
72
- cwd=workdir,
73
- env=env,
74
- stdout=stdout_f,
75
- stderr=subprocess.STDOUT,
76
- )
77
- else:
78
- with open(stderr_path, "w") as stderr_f:
82
+ if passthrough:
83
+ result = subprocess.run(
84
+ [str(script_path)],
85
+ cwd=workdir,
86
+ env=env,
87
+ )
88
+ else:
89
+ with open(stdout_path, "w") as stdout_f:
90
+ if job.merge_output:
79
91
  result = subprocess.run(
80
92
  [str(script_path)],
81
93
  cwd=workdir,
82
94
  env=env,
83
95
  stdout=stdout_f,
84
- stderr=stderr_f,
96
+ stderr=subprocess.STDOUT,
85
97
  )
98
+ else:
99
+ with open(stderr_path, "w") as stderr_f:
100
+ result = subprocess.run(
101
+ [str(script_path)],
102
+ cwd=workdir,
103
+ env=env,
104
+ stdout=stdout_f,
105
+ stderr=stderr_f,
106
+ )
86
107
  LocalScheduler._exit_codes[job_id] = result.returncode
87
108
  script_path.unlink(missing_ok=True)
88
109
  else:
89
110
  # Background execution
90
- stdout_f = open(stdout_path, "w")
91
- if job.merge_output:
111
+ if passthrough:
92
112
  proc = subprocess.Popen(
93
113
  [str(script_path)],
94
114
  cwd=workdir,
95
115
  env=env,
96
- stdout=stdout_f,
97
- stderr=subprocess.STDOUT,
98
116
  )
99
117
  else:
100
- stderr_f = open(stderr_path, "w")
101
- proc = subprocess.Popen(
102
- [str(script_path)],
103
- cwd=workdir,
104
- env=env,
105
- stdout=stdout_f,
106
- stderr=stderr_f,
107
- )
118
+ stdout_f = open(stdout_path, "w")
119
+ if job.merge_output:
120
+ proc = subprocess.Popen(
121
+ [str(script_path)],
122
+ cwd=workdir,
123
+ env=env,
124
+ stdout=stdout_f,
125
+ stderr=subprocess.STDOUT,
126
+ )
127
+ else:
128
+ stderr_f = open(stderr_path, "w")
129
+ proc = subprocess.Popen(
130
+ [str(script_path)],
131
+ cwd=workdir,
132
+ env=env,
133
+ stdout=stdout_f,
134
+ stderr=stderr_f,
135
+ )
136
+ proc._stdout_file = stdout_f # type: ignore[attr-defined]
137
+ if not job.merge_output:
138
+ proc._stderr_file = stderr_f # type: ignore[attr-defined]
108
139
  LocalScheduler._processes[job_id] = proc
109
140
  # Store script path for cleanup
110
141
  proc._script_path = script_path # type: ignore[attr-defined]
111
- proc._stdout_file = stdout_f # type: ignore[attr-defined]
112
- if not job.merge_output:
113
- proc._stderr_file = stderr_f # type: ignore[attr-defined]
114
142
 
115
143
  return JobResult(job_id=job_id, scheduler=self, job=job)
116
144
 
@@ -137,6 +165,7 @@ class LocalScheduler(BaseScheduler):
137
165
  def _submit_array_task(self, job: "Job", job_id: str, index: int) -> None:
138
166
  """Submit a single array task."""
139
167
  env = os.environ.copy() if job.inherit_env else {}
168
+ env.update(job.env_vars)
140
169
  env["HPC_ARRAY_TASK_ID"] = str(index)
141
170
 
142
171
  script = self.generate_script(job)
@@ -5,20 +5,38 @@
5
5
  # Exit on error
6
6
  set -e
7
7
 
8
+ # Module system initialization
9
+ {% if scheduler.module_init_script %}
10
+ . {{ scheduler.module_init_script }}
11
+ {% else %}
12
+ if [ -f /etc/profile.d/modules.sh ]; then
13
+ . /etc/profile.d/modules.sh
14
+ elif [ -f /usr/share/Modules/init/bash ]; then
15
+ . /usr/share/Modules/init/bash
16
+ elif [ -f /etc/modules/init/bash ]; then
17
+ . /etc/modules/init/bash
18
+ fi
19
+ {% endif %}
20
+
8
21
  {% if job.modules_path %}
9
- # Additional module paths (simulated for local)
22
+ # Additional module paths
10
23
  {% for path in job.modules_path %}
11
- # module use {{ path }}
24
+ module use {{ path }}
12
25
  {% endfor %}
13
26
  {% endif %}
14
27
 
15
28
  {% if job.modules %}
16
- # Modules (simulated for local - not actually loaded)
29
+ # Load modules
17
30
  {% for mod in job.modules %}
18
- # module load {{ mod }}
31
+ module load {{ mod }}
19
32
  {% endfor %}
20
33
  {% endif %}
21
34
 
35
+ {% if job.venv %}
36
+ # Activate virtual environment
37
+ source {{ job.venv }}/bin/activate
38
+ {% endif %}
39
+
22
40
  {% if job.env_prepend %}
23
41
  # Prepend to environment variables
24
42
  {% for key, value in job.env_prepend.items() %}
@@ -98,7 +98,7 @@ class TestLocalScheduler:
98
98
  def test_output_path(self, temp_dir):
99
99
  """Test output file creation."""
100
100
  scheduler = LocalScheduler()
101
- job = Job(command="echo hello", workdir=temp_dir)
101
+ job = Job(command="echo hello", workdir=temp_dir, stdout="out.log")
102
102
 
103
103
  result = scheduler.submit(job, interactive=True)
104
104
 
@@ -113,6 +113,7 @@ class TestLocalScheduler:
113
113
  job = Job(
114
114
  command="echo stdout; echo stderr >&2",
115
115
  workdir=temp_dir,
116
+ stdout="merged.log",
116
117
  )
117
118
 
118
119
  result = scheduler.submit(job, interactive=True)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes