Skip to content

Commit 700504c

Browse files
ryuwdclaude
andcommitted
refactor(cli): restructure to resource-based hierarchy
Move CLI commands to `dirac job submit {cwl,cmd,jdl}` hierarchy: - dirac cwl submit → dirac job submit cwl - dirac submit → dirac job submit cmd - dirac jobs submit (JDL) → dirac job submit jdl Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent f9ab30b commit 700504c

12 files changed

Lines changed: 154 additions & 147 deletions

File tree

diracx-cli/pyproject.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ dirac-cwl-run = "diracx.cli.executor.__main__:cli"
4242
job = "diracx.cli.job:app"
4343
jobs = "diracx.cli.jobs:app"
4444
config = "diracx.cli.config:app"
45-
cwl = "diracx.cli.cwl:app"
4645

4746
[project.entry-points."diracx.cli.hidden"]
4847
internal = "diracx.cli.internal:app"

diracx-cli/src/diracx/cli/__init__.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,9 @@
33
from diracx.core.extensions import DiracEntryPoint, select_from_extension
44

55
from .auth import app
6-
from .submit import register_submit
76

87
__all__ = ("app",)
98

10-
# Register top-level submit command
11-
register_submit(app)
12-
139
# Load all the sub commands
1410
cli_names = set(
1511
[

diracx-cli/src/diracx/cli/cwl/__init__.py

Lines changed: 0 additions & 16 deletions
This file was deleted.

diracx-cli/src/diracx/cli/job/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
__all__ = ("app",)
44

55
from ..utils import AsyncTyper
6+
from .submit import app as submit_app
67

78
app = AsyncTyper(help="Job operations.")
9+
app.add_typer(submit_app, name="submit")
810

911
# Import submodules to register commands
1012
from . import search as _search # noqa: F401, E402
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from __future__ import annotations
2+
3+
__all__ = ("app",)
4+
5+
from ...utils import AsyncTyper
6+
7+
app = AsyncTyper(help="Submit jobs to the grid.")
8+
9+
# Import submodules to register commands
10+
from . import cmd as _cmd # noqa: F401, E402
11+
from . import cwl as _cwl # noqa: F401, E402
12+
from . import jdl as _jdl # noqa: F401, E402
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
from __future__ import annotations
2+
3+
__all__: list[str] = []
4+
5+
import tempfile
6+
from pathlib import Path
7+
from typing import Annotated
8+
9+
import typer
10+
import yaml
11+
12+
from ..._submission.pipeline import submit_cwl
13+
from ..._submission.simple import detect_sandbox_files, generate_cwl
14+
from . import app
15+
16+
17+
@app.async_command(
18+
help="""Submit a simple command to the grid.
19+
20+
Runs COMMAND on a worker node. Local files referenced in the command
21+
are automatically detected and shipped as input sandboxes.
22+
23+
Use --sandbox for additional files not mentioned in the command.
24+
25+
Examples:
26+
dirac job submit cmd "python my_script.py"
27+
dirac job submit cmd "python my_script.py" --sandbox config.json
28+
""",
29+
)
30+
async def cmd(
31+
command: Annotated[str, typer.Argument(help="Shell command to run on the grid")],
32+
sandbox: Annotated[
33+
list[Path],
34+
typer.Option("--sandbox", help="Additional local files to ship"),
35+
] = [],
36+
yes: Annotated[
37+
bool, typer.Option("-y", "--yes", help="Skip confirmation prompt")
38+
] = False,
39+
):
40+
"""Submit a simple command to the grid."""
41+
# Auto-detect files from command
42+
auto_files = detect_sandbox_files(command)
43+
all_sandbox = list(set(auto_files + sandbox))
44+
45+
# Generate CWL
46+
cwl = generate_cwl(command=command, sandbox_files=all_sandbox)
47+
48+
# Write CWL to temp file (pipeline expects a Path)
49+
with tempfile.NamedTemporaryFile(mode="w", suffix=".cwl", delete=False) as f:
50+
yaml.dump(cwl, f)
51+
cwl_path = Path(f.name)
52+
53+
try:
54+
# Build sandbox inputs if files exist
55+
input_files: list[Path] = []
56+
if all_sandbox:
57+
sandbox_input = {
58+
"sandbox_files": [
59+
{"class": "File", "path": str(p)} for p in all_sandbox
60+
]
61+
}
62+
with tempfile.NamedTemporaryFile(
63+
mode="w", suffix=".yaml", delete=False
64+
) as inp_f:
65+
yaml.dump(sandbox_input, inp_f)
66+
input_files = [Path(inp_f.name)]
67+
68+
results = await submit_cwl(
69+
workflow=cwl_path,
70+
input_files=input_files,
71+
cli_args=[],
72+
range_spec=None,
73+
yes=yes,
74+
)
75+
job_ids = [str(r.job_id) for r in results]
76+
print(f"Submitted {len(results)} job(s): {', '.join(job_ids)}")
77+
finally:
78+
cwl_path.unlink(missing_ok=True)

diracx-cli/src/diracx/cli/cwl/submit.py renamed to diracx-cli/src/diracx/cli/job/submit/cwl.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
import typer
99

10-
from .._submission.pipeline import submit_cwl
10+
from ..._submission.pipeline import submit_cwl
1111
from . import app
1212

1313

@@ -21,14 +21,14 @@
2121
Workflow inputs can also be passed as CLI arguments after a -- separator.
2222
These are parsed against the workflow's declared input parameters:
2323
24-
dirac cwl submit workflow.cwl -- --message "hello" --count 42
24+
dirac job submit cwl workflow.cwl -- --message "hello" --count 42
2525
2626
Combine file inputs with CLI overrides:
2727
28-
dirac cwl submit workflow.cwl base.yaml -- --message "override"
28+
dirac job submit cwl workflow.cwl base.yaml -- --message "override"
2929
""",
3030
)
31-
async def submit(
31+
async def cwl(
3232
ctx: typer.Context,
3333
workflow: Annotated[Path, typer.Argument(help="CWL workflow file (.cwl)")],
3434
range: Annotated[
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
from __future__ import annotations
2+
3+
__all__: list[str] = []
4+
5+
from typer import FileText
6+
7+
from diracx.client.aio import AsyncDiracClient
8+
9+
from . import app
10+
11+
12+
@app.async_command(
13+
help="""Submit jobs in JDL format.
14+
15+
JDL is one or more JDL file paths.
16+
17+
Examples:
18+
dirac job submit jdl job.jdl
19+
dirac job submit jdl job1.jdl job2.jdl
20+
""",
21+
)
22+
async def jdl(jdl: list[FileText]):
23+
"""Submit jobs in JDL format."""
24+
async with AsyncDiracClient() as api:
25+
jobs = await api.jobs.submit_jdl_jobs([x.read() for x in jdl])
26+
print(
27+
f"Inserted {len(jobs)} jobs with ids: {','.join(map(str, (job.job_id for job in jobs)))}"
28+
)

diracx-cli/src/diracx/cli/jobs.py

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
from rich.console import Console
1212
from rich.table import Table
13-
from typer import FileText, Option
13+
from typer import Option
1414

1515
from diracx.client.aio import AsyncDiracClient
1616
from diracx.core.models.search import (
@@ -151,12 +151,3 @@ def display_rich(data, content_range: ContentRange) -> None:
151151
for job in data:
152152
table.add_row(*map(str, job.values()))
153153
console.print(table)
154-
155-
156-
@app.async_command()
157-
async def submit(jdl: list[FileText]):
158-
async with AsyncDiracClient() as api:
159-
jobs = await api.jobs.submit_jdl_jobs([x.read() for x in jdl])
160-
print(
161-
f"Inserted {len(jobs)} jobs with ids: {','.join(map(str, (job.job_id for job in jobs)))}"
162-
)

diracx-cli/src/diracx/cli/submit.py

Lines changed: 0 additions & 82 deletions
This file was deleted.

0 commit comments

Comments
 (0)