Skip to content

Commit 371dfae

Browse files
committed
feat: add additional transformers
Signed-off-by: vsoch <[email protected]>
1 parent 0d7fccf commit 371dfae

File tree

17 files changed

+1314
-128
lines changed

17 files changed

+1314
-128
lines changed

fractale/cli/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,10 @@ def get_parser():
8585
default="kubernetes",
8686
)
8787
transform.add_argument(
88-
"-f", "--from", dest="from_transformer", help="transform from this jobspec", default="flux"
88+
"-f",
89+
"--from",
90+
dest="from_transformer",
91+
help="transform from this jobspec",
8992
)
9093
transform.add_argument(
9194
"--pretty",

fractale/cli/transform.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from rich import print
88
from rich.pretty import pprint
99

10-
from fractale.transformer import get_transformer
10+
from fractale.transformer import detect_transformer, get_transformer
1111

1212

1313
def main(args, extra, **kwargs):
@@ -18,6 +18,10 @@ def main(args, extra, **kwargs):
1818
if not os.path.exists(args.jobspec):
1919
sys.exit(f"JobSpec {args.jobspec} does not exist.")
2020

21+
# If no from transformer defined, try to detect
22+
if args.from_transformer is None:
23+
args.from_transformer = detect_transformer(args.jobspec)
24+
2125
# No selector or solver, just manual transform
2226
from_transformer = get_transformer(args.from_transformer)
2327
to_transformer = get_transformer(args.to_transformer)

fractale/transformer/__init__.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,47 @@
1+
import fractale.utils as utils
2+
3+
from .cobalt import Transformer as CobaltTransformer
14
from .flux import Transformer as FluxTransformer
25
from .kubernetes import Transformer as KubernetesTransformer
6+
from .lsf import Transformer as LSFTransformer
7+
from .oar import Transformer as OARTransformer
8+
from .pbs import Transformer as PBSTransformer
39
from .slurm import Transformer as SlurmTransformer
410

511
plugins = {
612
"kubernetes": KubernetesTransformer,
713
"flux": FluxTransformer,
814
"slurm": SlurmTransformer,
15+
"pbs": PBSTransformer,
16+
"lsf": LSFTransformer,
17+
"oar": OARTransformer,
18+
"cobalt": CobaltTransformer,
919
}
1020

1121

1222
def get_transformer(name, selector="random", solver=None):
1323
if name not in plugins:
1424
raise ValueError(f"{name} is not a valid transformer.")
1525
return plugins[name](selector, solver)
26+
27+
28+
def detect_transformer(jobspec):
29+
"""
30+
Quick and dirty detection.
31+
"""
32+
content = utils.read_file(jobspec)
33+
if "#FLUX" in content and "FLUX_CAPACITOR" not in content:
34+
return "flux"
35+
if "#SBATCH " in content:
36+
return "slurm"
37+
if "kind:" in content and "Job" in content:
38+
return "kubernetes"
39+
if "#PBS " in content:
40+
return "pbs"
41+
if "#BSUB" in content:
42+
return "lsf"
43+
if "#OAR" in content:
44+
return "oar"
45+
if "#COBALT" in content:
46+
return "cobalt"
47+
raise ValueError("Unkown transformer.")

fractale/transformer/base.py

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class TransformerBase:
1212
This can be very manual, or use an LLM.
1313
"""
1414

15-
def __init__(self, selector, solver):
15+
def __init__(self, selector="random", solver=None):
1616
"""
1717
Create a new transformer backend, accepting any options type.
1818
@@ -21,12 +21,18 @@ def __init__(self, selector, solver):
2121
self.selector = get_selector(selector)
2222
self.solver = solver
2323

24-
def parse(self, *args, **kwargs):
24+
def _parse(self, *args, **kwargs):
2525
"""
2626
Parse converts the native jobspec to the standard JobSpec
2727
"""
2828
raise NotImplementedError
2929

30+
def parse(self, filename):
31+
return self._parse(filename)
32+
33+
def unhandled(self, filename):
34+
return self._parse(filename, return_unhandled=True)
35+
3036
def convert(self, *args, **kwargs):
3137
"""
3238
Convert a normalized jobspec to the format here.
@@ -42,3 +48,46 @@ def render(self, matches, jobspec):
4248
"""
4349
js = utils.load_jobspec(jobspec)
4450
return self.run(matches, js)
51+
52+
53+
class Script:
54+
"""
55+
A helper class to build a batch script line by line.
56+
"""
57+
58+
def __init__(self, directive=""):
59+
self.script_lines = ["#!/bin/bash"]
60+
self.directive = directive
61+
62+
def newline(self):
63+
self.script_lines.append("")
64+
65+
def add_line(self, line: str):
66+
"""
67+
Add a custom line to the script.
68+
"""
69+
self.script_lines.append(line)
70+
71+
def add(self, name: str, value=None):
72+
"""
73+
Add a Flux directive, e.g., #FLUX: --job-name=my-job or #FLUX: -N 4.
74+
Handles both short and long options.
75+
"""
76+
if value is None:
77+
return
78+
79+
# Determine if it's a short (-n) or long (--tasks) option
80+
prefix = "-" if len(name) == 1 else "--"
81+
self.script_lines.append(f"{self.directive}: {prefix}{name}={value}")
82+
83+
def add_flag(self, name: str):
84+
"""
85+
Add a boolean flag (e.g., --exclusive).
86+
"""
87+
self.script_lines.append(f"{self.directive}: --{name}")
88+
89+
def render(self) -> str:
90+
"""
91+
Return the complete script as a single string.
92+
"""
93+
return "\n".join(self.script_lines)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from .transform import CobaltTransformer as Transformer
2+
3+
assert Transformer

0 commit comments

Comments
 (0)