Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion executorlib/__init__.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -55,7 +55,7 @@ def terminate_tasks_in_cache(
config_directory (str, optional): path to the config directory.
backend (str, optional): name of the backend used to spawn tasks ["slurm", "flux"].
"""
from executorlib.task_scheduler.file.queue_spawner import terminate_tasks_in_cache
from executorlib.task_scheduler.file.spawner_pysqa import terminate_tasks_in_cache

return terminate_tasks_in_cache(
cache_directory=cache_directory,
Expand Down
2 changes: 1 addition & 1 deletion executorlib/executor/flux.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -458,7 +458,7 @@ def create_flux_executor(
Returns:
InteractiveStepExecutor/ InteractiveExecutor
"""
from executorlib.task_scheduler.interactive.fluxspawner import (
from executorlib.task_scheduler.interactive.spawner_flux import (
FluxPythonSpawner,
validate_max_workers,
)
Expand Down
2 changes: 1 addition & 1 deletion executorlib/executor/single.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -314,7 +314,7 @@ def __init__(
{k: v for k, v in default_resource_dict.items() if k not in resource_dict}
)
if not plot_dependency_graph:
from executorlib.task_scheduler.file.subprocess_spawner import (
from executorlib.task_scheduler.file.spawner_subprocess import (
execute_in_subprocess,
)
from executorlib.task_scheduler.file.task_scheduler import (
Expand Down
2 changes: 1 addition & 1 deletion executorlib/executor/slurm.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,7 +13,7 @@
)
from executorlib.task_scheduler.interactive.dependency import DependencyTaskScheduler
from executorlib.task_scheduler.interactive.onetoone import OneProcessTaskScheduler
from executorlib.task_scheduler.interactive.slurmspawner import (
from executorlib.task_scheduler.interactive.spawner_slurm import (
SrunSpawner,
validate_max_workers,
)
Expand Down
50 changes: 50 additions & 0 deletions executorlib/standalone/command.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,6 +3,8 @@
import sys
from typing import Optional

SLURM_COMMAND = "srun"


def get_command_path(executable: str) -> str:
"""
Expand DownExpand Up@@ -112,3 +114,51 @@ def get_interactive_execute_command(
else:
command_lst += [get_command_path(executable="interactive_serial.py")]
return command_lst


def generate_slurm_command(
cores: int,
cwd: Optional[str],
threads_per_core: int = 1,
gpus_per_core: int = 0,
num_nodes: Optional[int] = None,
exclusive: bool = False,
openmpi_oversubscribe: bool = False,
slurm_cmd_args: Optional[list[str]] = None,
pmi_mode: Optional[str] = None,
) -> list[str]:
"""
Generate the command list for the SLURM interface.

Args:
cores (int): The number of cores.
cwd (str): The current working directory.
threads_per_core (int, optional): The number of threads per core. Defaults to 1.
gpus_per_core (int, optional): The number of GPUs per core. Defaults to 0.
num_nodes (int, optional): The number of compute nodes to use for executing the task. Defaults to None.
exclusive (bool): Whether to exclusively reserve the compute nodes, or allow sharing compute notes. Defaults to False.
openmpi_oversubscribe (bool, optional): Whether to oversubscribe the cores. Defaults to False.
slurm_cmd_args (list[str], optional): Additional command line arguments. Defaults to [].
pmi_mode (str): PMI interface to use (OpenMPI v5 requires pmix) default is None

Returns:
list[str]: The generated command list.
"""
command_prepend_lst = [SLURM_COMMAND, "-n", str(cores)]
if cwd is not None:
command_prepend_lst += ["-D", cwd]
if pmi_mode is not None:
command_prepend_lst += ["--mpi=" + pmi_mode]
if num_nodes is not None:
command_prepend_lst += ["-N", str(num_nodes)]
if threads_per_core > 1:
command_prepend_lst += ["--cpus-per-task=" + str(threads_per_core)]
if gpus_per_core > 0:
command_prepend_lst += ["--gpus-per-task=" + str(gpus_per_core)]
if exclusive:
command_prepend_lst += ["--exact"]
if openmpi_oversubscribe:
command_prepend_lst += ["--oversubscribe"]
if slurm_cmd_args is not None and len(slurm_cmd_args) > 0:
command_prepend_lst += slurm_cmd_args
return command_prepend_lst
51 changes: 0 additions & 51 deletions executorlib/standalone/slurm_command.py

This file was deleted.

2 changes: 1 addition & 1 deletion executorlib/task_scheduler/file/shared.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,7 +7,7 @@
from executorlib.standalone.command import get_cache_execute_command
from executorlib.standalone.hdf import get_cache_files, get_output
from executorlib.standalone.serialize import serialize_funct
from executorlib.task_scheduler.file.subprocess_spawner import terminate_subprocess
from executorlib.task_scheduler.file.spawner_subprocess import terminate_subprocess


class FutureItem:
Expand Down
4 changes: 2 additions & 2 deletions executorlib/task_scheduler/file/task_scheduler.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,14 +11,14 @@
)
from executorlib.task_scheduler.base import TaskSchedulerBase
from executorlib.task_scheduler.file.shared import execute_tasks_h5
from executorlib.task_scheduler.file.subprocess_spawner import (
from executorlib.task_scheduler.file.spawner_subprocess import (
execute_in_subprocess,
terminate_subprocess,
)

try:
from executorlib.standalone.scheduler import terminate_with_pysqa
from executorlib.task_scheduler.file.queue_spawner import execute_with_pysqa
from executorlib.task_scheduler.file.spawner_pysqa import execute_with_pysqa
except ImportError:
# If pysqa is not available fall back to executing tasks in a subprocess
execute_with_pysqa = execute_in_subprocess # type: ignore
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
import os
from typing import Optional

from executorlib.standalone.command import generate_slurm_command

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Exclusive flag likely mapped to the wrong srun option in generate_slurm_command.

Srun’s exclusive allocation uses --exclusive, not --exact. Since this module now depends on generate_slurm_command, verify and fix there to avoid incorrect scheduling semantics.

Proposed fix in executorlib/standalone/command.py (illustrative):

# inside generate_slurm_command(...)ifexclusive:
command_prepend_lst+= ["--exclusive"] # not "--exact"

If --exact was intentional, please document the rationale and rename the parameter to avoid confusion.


🏁 Script executed:

#!/bin/bash# Locate generate_slurm_command definition with context
rg -nP --type=py 'def generate_slurm_command' -C10 executorlib/standalone/command.py
# Check for "--exact" usage in this file
rg -n '"--exact"' -C3 executorlib/standalone/command.py
# Check for "exclusive" parameter or flag handling
rg -n 'exclusive' -C10 executorlib/standalone/command.py

Length of output: 6979


Replace --exact with --exclusive for exclusive allocations

  • In executorlib/standalone/command.py → get_cache_execute_command (around lines 58–60), change
    command_prepend += ["--exact"]
    to
    command_prepend += ["--exclusive"]
  • In executorlib/standalone/command.py → generate_slurm_command (around lines 158–159), change
    command_prepend_lst += ["--exact"]
    to
    command_prepend_lst += ["--exclusive"]
    If --exact was intentional, document why and rename the exclusive parameter to avoid confusion.

from executorlib.standalone.interactive.spawner import SubprocessSpawner
from executorlib.standalone.slurm_command import generate_slurm_command


def validate_max_workers(max_workers: int, cores: int, threads_per_core: int):
Expand Down
2 changes: 1 addition & 1 deletion tests/test_cache_fileexecutor_mpi.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,7 +5,7 @@

try:
from executorlib.task_scheduler.file.task_scheduler import FileTaskScheduler
from executorlib.task_scheduler.file.subprocess_spawner import execute_in_subprocess
from executorlib.task_scheduler.file.spawner_subprocess import execute_in_subprocess

skip_h5py_test = False
except ImportError:
Expand Down
2 changes: 1 addition & 1 deletion tests/test_cache_fileexecutor_serial.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,7 +7,7 @@
from time import sleep

try:
from executorlib.task_scheduler.file.subprocess_spawner import (
from executorlib.task_scheduler.file.spawner_subprocess import (
execute_in_subprocess,
terminate_subprocess,
)
Expand Down
2 changes: 1 addition & 1 deletion tests/test_fluxclusterexecutor.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,7 +12,7 @@
import flux.job
from executorlib import terminate_tasks_in_cache
from executorlib.standalone.hdf import dump
from executorlib.task_scheduler.file.queue_spawner import execute_with_pysqa
from executorlib.task_scheduler.file.spawner_pysqa import execute_with_pysqa
from executorlib.standalone.scheduler import terminate_with_pysqa

skip_flux_test = "FLUX_URI" not in os.environ
Expand Down
2 changes: 1 addition & 1 deletion tests/test_fluxjobexecutor.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,7 +8,7 @@

try:
import flux.job
from executorlib.task_scheduler.interactive.fluxspawner import FluxPythonSpawner
from executorlib.task_scheduler.interactive.spawner_flux import FluxPythonSpawner

skip_flux_test = "FLUX_URI" not in os.environ
pmi = os.environ.get("EXECUTORLIB_PMIX", None)
Expand Down
2 changes: 1 addition & 1 deletion tests/test_fluxjobexecutor_plot.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,7 +10,7 @@
try:
import pygraphviz
import flux.job
from executorlib.task_scheduler.interactive.fluxspawner import FluxPythonSpawner
from executorlib.task_scheduler.interactive.spawner_flux import FluxPythonSpawner

skip_graphviz_flux_test = "FLUX_URI" not in os.environ
except ImportError:
Expand Down
2 changes: 1 addition & 1 deletion tests/test_fluxpythonspawner.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,7 +12,7 @@

try:
import flux.job
from executorlib.task_scheduler.interactive.fluxspawner import FluxPythonSpawner
from executorlib.task_scheduler.interactive.spawner_flux import FluxPythonSpawner

skip_flux_test = "FLUX_URI" not in os.environ
pmi = os.environ.get("EXECUTORLIB_PMIX", None)
Expand Down
2 changes: 1 addition & 1 deletion tests/test_interactive_slurmspawner.py
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
import unittest
from executorlib.standalone.slurm_command import generate_slurm_command
from executorlib.standalone.command import generate_slurm_command

try:
from executorlib.standalone.scheduler import pysqa_execute_command
Expand Down
2 changes: 1 addition & 1 deletion tests/test_standalone_interactive_backend.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,7 @@

from executorlib.standalone.interactive.backend import parse_arguments
from executorlib.standalone.interactive.spawner import MpiExecSpawner
from executorlib.task_scheduler.interactive.slurmspawner import SrunSpawner
from executorlib.task_scheduler.interactive.spawner_slurm import SrunSpawner


class TestParser(unittest.TestCase):
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion executorlib/__init__.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -55,7 +55,7 @@ def terminate_tasks_in_cache(
config_directory (str, optional): path to the config directory.
backend (str, optional): name of the backend used to spawn tasks ["slurm", "flux"].
"""
from executorlib.task_scheduler.file.queue_spawner import terminate_tasks_in_cache
from executorlib.task_scheduler.file.spawner_pysqa import terminate_tasks_in_cache

return terminate_tasks_in_cache(
cache_directory=cache_directory,
Expand Down
2 changes: 1 addition & 1 deletion executorlib/executor/flux.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -458,7 +458,7 @@ def create_flux_executor(
Returns:
InteractiveStepExecutor/ InteractiveExecutor
"""
from executorlib.task_scheduler.interactive.fluxspawner import (
from executorlib.task_scheduler.interactive.spawner_flux import (
FluxPythonSpawner,
validate_max_workers,
)
Expand Down
2 changes: 1 addition & 1 deletion executorlib/executor/single.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -314,7 +314,7 @@ def __init__(
{k: v for k, v in default_resource_dict.items() if k not in resource_dict}
)
if not plot_dependency_graph:
from executorlib.task_scheduler.file.subprocess_spawner import (
from executorlib.task_scheduler.file.spawner_subprocess import (
execute_in_subprocess,
)
from executorlib.task_scheduler.file.task_scheduler import (
Expand Down
2 changes: 1 addition & 1 deletion executorlib/executor/slurm.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,7 +13,7 @@
)
from executorlib.task_scheduler.interactive.dependency import DependencyTaskScheduler
from executorlib.task_scheduler.interactive.onetoone import OneProcessTaskScheduler
from executorlib.task_scheduler.interactive.slurmspawner import (
from executorlib.task_scheduler.interactive.spawner_slurm import (
SrunSpawner,
validate_max_workers,
)
Expand Down
50 changes: 50 additions & 0 deletions executorlib/standalone/command.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,6 +3,8 @@
import sys
from typing import Optional

SLURM_COMMAND = "srun"


def get_command_path(executable: str) -> str:
"""
Expand DownExpand Up@@ -112,3 +114,51 @@ def get_interactive_execute_command(
else:
command_lst += [get_command_path(executable="interactive_serial.py")]
return command_lst


def generate_slurm_command(
cores: int,
cwd: Optional[str],
threads_per_core: int = 1,
gpus_per_core: int = 0,
num_nodes: Optional[int] = None,
exclusive: bool = False,
openmpi_oversubscribe: bool = False,
slurm_cmd_args: Optional[list[str]] = None,
pmi_mode: Optional[str] = None,
) -> list[str]:
"""
Generate the command list for the SLURM interface.

Args:
cores (int): The number of cores.
cwd (str): The current working directory.
threads_per_core (int, optional): The number of threads per core. Defaults to 1.
gpus_per_core (int, optional): The number of GPUs per core. Defaults to 0.
num_nodes (int, optional): The number of compute nodes to use for executing the task. Defaults to None.
exclusive (bool): Whether to exclusively reserve the compute nodes, or allow sharing compute notes. Defaults to False.
openmpi_oversubscribe (bool, optional): Whether to oversubscribe the cores. Defaults to False.
slurm_cmd_args (list[str], optional): Additional command line arguments. Defaults to [].
pmi_mode (str): PMI interface to use (OpenMPI v5 requires pmix) default is None

Returns:
list[str]: The generated command list.
"""
command_prepend_lst = [SLURM_COMMAND, "-n", str(cores)]
if cwd is not None:
command_prepend_lst += ["-D", cwd]
if pmi_mode is not None:
command_prepend_lst += ["--mpi=" + pmi_mode]
if num_nodes is not None:
command_prepend_lst += ["-N", str(num_nodes)]
if threads_per_core > 1:
command_prepend_lst += ["--cpus-per-task=" + str(threads_per_core)]
if gpus_per_core > 0:
command_prepend_lst += ["--gpus-per-task=" + str(gpus_per_core)]
if exclusive:
command_prepend_lst += ["--exact"]
if openmpi_oversubscribe:
command_prepend_lst += ["--oversubscribe"]
if slurm_cmd_args is not None and len(slurm_cmd_args) > 0:
command_prepend_lst += slurm_cmd_args
return command_prepend_lst
51 changes: 0 additions & 51 deletions executorlib/standalone/slurm_command.py

This file was deleted.

2 changes: 1 addition & 1 deletion executorlib/task_scheduler/file/shared.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,7 +7,7 @@
from executorlib.standalone.command import get_cache_execute_command
from executorlib.standalone.hdf import get_cache_files, get_output
from executorlib.standalone.serialize import serialize_funct
from executorlib.task_scheduler.file.subprocess_spawner import terminate_subprocess
from executorlib.task_scheduler.file.spawner_subprocess import terminate_subprocess


class FutureItem:
Expand Down
4 changes: 2 additions & 2 deletions executorlib/task_scheduler/file/task_scheduler.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,14 +11,14 @@
)
from executorlib.task_scheduler.base import TaskSchedulerBase
from executorlib.task_scheduler.file.shared import execute_tasks_h5
from executorlib.task_scheduler.file.subprocess_spawner import (
from executorlib.task_scheduler.file.spawner_subprocess import (
execute_in_subprocess,
terminate_subprocess,
)

try:
from executorlib.standalone.scheduler import terminate_with_pysqa
from executorlib.task_scheduler.file.queue_spawner import execute_with_pysqa
from executorlib.task_scheduler.file.spawner_pysqa import execute_with_pysqa
except ImportError:
# If pysqa is not available fall back to executing tasks in a subprocess
execute_with_pysqa = execute_in_subprocess # type: ignore
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
import os
from typing import Optional

from executorlib.standalone.command import generate_slurm_command

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Exclusive flag likely mapped to the wrong srun option in generate_slurm_command.

Srun’s exclusive allocation uses --exclusive, not --exact. Since this module now depends on generate_slurm_command, verify and fix there to avoid incorrect scheduling semantics.

Proposed fix in executorlib/standalone/command.py (illustrative):

# inside generate_slurm_command(...)ifexclusive:
command_prepend_lst+= ["--exclusive"] # not "--exact"

If --exact was intentional, please document the rationale and rename the parameter to avoid confusion.


🏁 Script executed:

#!/bin/bash# Locate generate_slurm_command definition with context
rg -nP --type=py 'def generate_slurm_command' -C10 executorlib/standalone/command.py
# Check for "--exact" usage in this file
rg -n '"--exact"' -C3 executorlib/standalone/command.py
# Check for "exclusive" parameter or flag handling
rg -n 'exclusive' -C10 executorlib/standalone/command.py

Length of output: 6979


Replace --exact with --exclusive for exclusive allocations

  • In executorlib/standalone/command.py → get_cache_execute_command (around lines 58–60), change
    command_prepend += ["--exact"]
    to
    command_prepend += ["--exclusive"]
  • In executorlib/standalone/command.py → generate_slurm_command (around lines 158–159), change
    command_prepend_lst += ["--exact"]
    to
    command_prepend_lst += ["--exclusive"]
    If --exact was intentional, document why and rename the exclusive parameter to avoid confusion.

from executorlib.standalone.interactive.spawner import SubprocessSpawner
from executorlib.standalone.slurm_command import generate_slurm_command


def validate_max_workers(max_workers: int, cores: int, threads_per_core: int):
Expand Down
2 changes: 1 addition & 1 deletion tests/test_cache_fileexecutor_mpi.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,7 +5,7 @@

try:
from executorlib.task_scheduler.file.task_scheduler import FileTaskScheduler
from executorlib.task_scheduler.file.subprocess_spawner import execute_in_subprocess
from executorlib.task_scheduler.file.spawner_subprocess import execute_in_subprocess

skip_h5py_test = False
except ImportError:
Expand Down
2 changes: 1 addition & 1 deletion tests/test_cache_fileexecutor_serial.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,7 +7,7 @@
from time import sleep

try:
from executorlib.task_scheduler.file.subprocess_spawner import (
from executorlib.task_scheduler.file.spawner_subprocess import (
execute_in_subprocess,
terminate_subprocess,
)
Expand Down
2 changes: 1 addition & 1 deletion tests/test_fluxclusterexecutor.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,7 +12,7 @@
import flux.job
from executorlib import terminate_tasks_in_cache
from executorlib.standalone.hdf import dump
from executorlib.task_scheduler.file.queue_spawner import execute_with_pysqa
from executorlib.task_scheduler.file.spawner_pysqa import execute_with_pysqa
from executorlib.standalone.scheduler import terminate_with_pysqa

skip_flux_test = "FLUX_URI" not in os.environ
Expand Down
2 changes: 1 addition & 1 deletion tests/test_fluxjobexecutor.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,7 +8,7 @@

try:
import flux.job
from executorlib.task_scheduler.interactive.fluxspawner import FluxPythonSpawner
from executorlib.task_scheduler.interactive.spawner_flux import FluxPythonSpawner

skip_flux_test = "FLUX_URI" not in os.environ
pmi = os.environ.get("EXECUTORLIB_PMIX", None)
Expand Down
2 changes: 1 addition & 1 deletion tests/test_fluxjobexecutor_plot.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,7 +10,7 @@
try:
import pygraphviz
import flux.job
from executorlib.task_scheduler.interactive.fluxspawner import FluxPythonSpawner
from executorlib.task_scheduler.interactive.spawner_flux import FluxPythonSpawner

skip_graphviz_flux_test = "FLUX_URI" not in os.environ
except ImportError:
Expand Down
2 changes: 1 addition & 1 deletion tests/test_fluxpythonspawner.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,7 +12,7 @@

try:
import flux.job
from executorlib.task_scheduler.interactive.fluxspawner import FluxPythonSpawner
from executorlib.task_scheduler.interactive.spawner_flux import FluxPythonSpawner

skip_flux_test = "FLUX_URI" not in os.environ
pmi = os.environ.get("EXECUTORLIB_PMIX", None)
Expand Down
2 changes: 1 addition & 1 deletion tests/test_interactive_slurmspawner.py
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
import unittest
from executorlib.standalone.slurm_command import generate_slurm_command
from executorlib.standalone.command import generate_slurm_command

try:
from executorlib.standalone.scheduler import pysqa_execute_command
Expand Down
2 changes: 1 addition & 1 deletion tests/test_standalone_interactive_backend.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,7 @@

from executorlib.standalone.interactive.backend import parse_arguments
from executorlib.standalone.interactive.spawner import MpiExecSpawner
from executorlib.task_scheduler.interactive.slurmspawner import SrunSpawner
from executorlib.task_scheduler.interactive.spawner_slurm import SrunSpawner


class TestParser(unittest.TestCase):
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion executorlib/__init__.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -55,7 +55,7 @@ def terminate_tasks_in_cache(
config_directory (str, optional): path to the config directory.
backend (str, optional): name of the backend used to spawn tasks ["slurm", "flux"].
"""
from executorlib.task_scheduler.file.queue_spawner import terminate_tasks_in_cache
from executorlib.task_scheduler.file.spawner_pysqa import terminate_tasks_in_cache

return terminate_tasks_in_cache(
cache_directory=cache_directory,
Expand Down
2 changes: 1 addition & 1 deletion executorlib/executor/flux.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -458,7 +458,7 @@ def create_flux_executor(
Returns:
InteractiveStepExecutor/ InteractiveExecutor
"""
from executorlib.task_scheduler.interactive.fluxspawner import (
from executorlib.task_scheduler.interactive.spawner_flux import (
FluxPythonSpawner,
validate_max_workers,
)
Expand Down
2 changes: 1 addition & 1 deletion executorlib/executor/single.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -314,7 +314,7 @@ def __init__(
{k: v for k, v in default_resource_dict.items() if k not in resource_dict}
)
if not plot_dependency_graph:
from executorlib.task_scheduler.file.subprocess_spawner import (
from executorlib.task_scheduler.file.spawner_subprocess import (
execute_in_subprocess,
)
from executorlib.task_scheduler.file.task_scheduler import (
Expand Down
2 changes: 1 addition & 1 deletion executorlib/executor/slurm.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,7 +13,7 @@
)
from executorlib.task_scheduler.interactive.dependency import DependencyTaskScheduler
from executorlib.task_scheduler.interactive.onetoone import OneProcessTaskScheduler
from executorlib.task_scheduler.interactive.slurmspawner import (
from executorlib.task_scheduler.interactive.spawner_slurm import (
SrunSpawner,
validate_max_workers,
)
Expand Down
50 changes: 50 additions & 0 deletions executorlib/standalone/command.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,6 +3,8 @@
import sys
from typing import Optional

SLURM_COMMAND = "srun"


def get_command_path(executable: str) -> str:
"""
Expand DownExpand Up@@ -112,3 +114,51 @@ def get_interactive_execute_command(
else:
command_lst += [get_command_path(executable="interactive_serial.py")]
return command_lst


def generate_slurm_command(
cores: int,
cwd: Optional[str],
threads_per_core: int = 1,
gpus_per_core: int = 0,
num_nodes: Optional[int] = None,
exclusive: bool = False,
openmpi_oversubscribe: bool = False,
slurm_cmd_args: Optional[list[str]] = None,
pmi_mode: Optional[str] = None,
) -> list[str]:
"""
Generate the command list for the SLURM interface.

Args:
cores (int): The number of cores.
cwd (str): The current working directory.
threads_per_core (int, optional): The number of threads per core. Defaults to 1.
gpus_per_core (int, optional): The number of GPUs per core. Defaults to 0.
num_nodes (int, optional): The number of compute nodes to use for executing the task. Defaults to None.
exclusive (bool): Whether to exclusively reserve the compute nodes, or allow sharing compute notes. Defaults to False.
openmpi_oversubscribe (bool, optional): Whether to oversubscribe the cores. Defaults to False.
slurm_cmd_args (list[str], optional): Additional command line arguments. Defaults to [].
pmi_mode (str): PMI interface to use (OpenMPI v5 requires pmix) default is None

Returns:
list[str]: The generated command list.
"""
command_prepend_lst = [SLURM_COMMAND, "-n", str(cores)]
if cwd is not None:
command_prepend_lst += ["-D", cwd]
if pmi_mode is not None:
command_prepend_lst += ["--mpi=" + pmi_mode]
if num_nodes is not None:
command_prepend_lst += ["-N", str(num_nodes)]
if threads_per_core > 1:
command_prepend_lst += ["--cpus-per-task=" + str(threads_per_core)]
if gpus_per_core > 0:
command_prepend_lst += ["--gpus-per-task=" + str(gpus_per_core)]
if exclusive:
command_prepend_lst += ["--exact"]
if openmpi_oversubscribe:
command_prepend_lst += ["--oversubscribe"]
if slurm_cmd_args is not None and len(slurm_cmd_args) > 0:
command_prepend_lst += slurm_cmd_args
return command_prepend_lst
51 changes: 0 additions & 51 deletions executorlib/standalone/slurm_command.py

This file was deleted.

2 changes: 1 addition & 1 deletion executorlib/task_scheduler/file/shared.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,7 +7,7 @@
from executorlib.standalone.command import get_cache_execute_command
from executorlib.standalone.hdf import get_cache_files, get_output
from executorlib.standalone.serialize import serialize_funct
from executorlib.task_scheduler.file.subprocess_spawner import terminate_subprocess
from executorlib.task_scheduler.file.spawner_subprocess import terminate_subprocess


class FutureItem:
Expand Down
4 changes: 2 additions & 2 deletions executorlib/task_scheduler/file/task_scheduler.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,14 +11,14 @@
)
from executorlib.task_scheduler.base import TaskSchedulerBase
from executorlib.task_scheduler.file.shared import execute_tasks_h5
from executorlib.task_scheduler.file.subprocess_spawner import (
from executorlib.task_scheduler.file.spawner_subprocess import (
execute_in_subprocess,
terminate_subprocess,
)

try:
from executorlib.standalone.scheduler import terminate_with_pysqa
from executorlib.task_scheduler.file.queue_spawner import execute_with_pysqa
from executorlib.task_scheduler.file.spawner_pysqa import execute_with_pysqa
except ImportError:
# If pysqa is not available fall back to executing tasks in a subprocess
execute_with_pysqa = execute_in_subprocess # type: ignore
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
import os
from typing import Optional

from executorlib.standalone.command import generate_slurm_command

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Exclusive flag likely mapped to the wrong srun option in generate_slurm_command.

Srun’s exclusive allocation uses --exclusive, not --exact. Since this module now depends on generate_slurm_command, verify and fix there to avoid incorrect scheduling semantics.

Proposed fix in executorlib/standalone/command.py (illustrative):

# inside generate_slurm_command(...)ifexclusive:
command_prepend_lst+= ["--exclusive"] # not "--exact"

If --exact was intentional, please document the rationale and rename the parameter to avoid confusion.


🏁 Script executed:

#!/bin/bash# Locate generate_slurm_command definition with context
rg -nP --type=py 'def generate_slurm_command' -C10 executorlib/standalone/command.py
# Check for "--exact" usage in this file
rg -n '"--exact"' -C3 executorlib/standalone/command.py
# Check for "exclusive" parameter or flag handling
rg -n 'exclusive' -C10 executorlib/standalone/command.py

Length of output: 6979


Replace --exact with --exclusive for exclusive allocations

  • In executorlib/standalone/command.py → get_cache_execute_command (around lines 58–60), change
    command_prepend += ["--exact"]
    to
    command_prepend += ["--exclusive"]
  • In executorlib/standalone/command.py → generate_slurm_command (around lines 158–159), change
    command_prepend_lst += ["--exact"]
    to
    command_prepend_lst += ["--exclusive"]
    If --exact was intentional, document why and rename the exclusive parameter to avoid confusion.

from executorlib.standalone.interactive.spawner import SubprocessSpawner
from executorlib.standalone.slurm_command import generate_slurm_command


def validate_max_workers(max_workers: int, cores: int, threads_per_core: int):
Expand Down
2 changes: 1 addition & 1 deletion tests/test_cache_fileexecutor_mpi.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,7 +5,7 @@

try:
from executorlib.task_scheduler.file.task_scheduler import FileTaskScheduler
from executorlib.task_scheduler.file.subprocess_spawner import execute_in_subprocess
from executorlib.task_scheduler.file.spawner_subprocess import execute_in_subprocess

skip_h5py_test = False
except ImportError:
Expand Down
2 changes: 1 addition & 1 deletion tests/test_cache_fileexecutor_serial.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,7 +7,7 @@
from time import sleep

try:
from executorlib.task_scheduler.file.subprocess_spawner import (
from executorlib.task_scheduler.file.spawner_subprocess import (
execute_in_subprocess,
terminate_subprocess,
)
Expand Down
2 changes: 1 addition & 1 deletion tests/test_fluxclusterexecutor.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,7 +12,7 @@
import flux.job
from executorlib import terminate_tasks_in_cache
from executorlib.standalone.hdf import dump
from executorlib.task_scheduler.file.queue_spawner import execute_with_pysqa
from executorlib.task_scheduler.file.spawner_pysqa import execute_with_pysqa
from executorlib.standalone.scheduler import terminate_with_pysqa

skip_flux_test = "FLUX_URI" not in os.environ
Expand Down
2 changes: 1 addition & 1 deletion tests/test_fluxjobexecutor.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,7 +8,7 @@

try:
import flux.job
from executorlib.task_scheduler.interactive.fluxspawner import FluxPythonSpawner
from executorlib.task_scheduler.interactive.spawner_flux import FluxPythonSpawner

skip_flux_test = "FLUX_URI" not in os.environ
pmi = os.environ.get("EXECUTORLIB_PMIX", None)
Expand Down
2 changes: 1 addition & 1 deletion tests/test_fluxjobexecutor_plot.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,7 +10,7 @@
try:
import pygraphviz
import flux.job
from executorlib.task_scheduler.interactive.fluxspawner import FluxPythonSpawner
from executorlib.task_scheduler.interactive.spawner_flux import FluxPythonSpawner

skip_graphviz_flux_test = "FLUX_URI" not in os.environ
except ImportError:
Expand Down
2 changes: 1 addition & 1 deletion tests/test_fluxpythonspawner.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,7 +12,7 @@

try:
import flux.job
from executorlib.task_scheduler.interactive.fluxspawner import FluxPythonSpawner
from executorlib.task_scheduler.interactive.spawner_flux import FluxPythonSpawner

skip_flux_test = "FLUX_URI" not in os.environ
pmi = os.environ.get("EXECUTORLIB_PMIX", None)
Expand Down
2 changes: 1 addition & 1 deletion tests/test_interactive_slurmspawner.py
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
import unittest
from executorlib.standalone.slurm_command import generate_slurm_command
from executorlib.standalone.command import generate_slurm_command

try:
from executorlib.standalone.scheduler import pysqa_execute_command
Expand Down
2 changes: 1 addition & 1 deletion tests/test_standalone_interactive_backend.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,7 @@

from executorlib.standalone.interactive.backend import parse_arguments
from executorlib.standalone.interactive.spawner import MpiExecSpawner
from executorlib.task_scheduler.interactive.slurmspawner import SrunSpawner
from executorlib.task_scheduler.interactive.spawner_slurm import SrunSpawner


class TestParser(unittest.TestCase):
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion executorlib/__init__.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -55,7 +55,7 @@ def terminate_tasks_in_cache(
config_directory (str, optional): path to the config directory.
backend (str, optional): name of the backend used to spawn tasks ["slurm", "flux"].
"""
from executorlib.task_scheduler.file.queue_spawner import terminate_tasks_in_cache
from executorlib.task_scheduler.file.spawner_pysqa import terminate_tasks_in_cache

return terminate_tasks_in_cache(
cache_directory=cache_directory,
Expand Down
2 changes: 1 addition & 1 deletion executorlib/executor/flux.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -458,7 +458,7 @@ def create_flux_executor(
Returns:
InteractiveStepExecutor/ InteractiveExecutor
"""
from executorlib.task_scheduler.interactive.fluxspawner import (
from executorlib.task_scheduler.interactive.spawner_flux import (
FluxPythonSpawner,
validate_max_workers,
)
Expand Down
2 changes: 1 addition & 1 deletion executorlib/executor/single.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -314,7 +314,7 @@ def __init__(
{k: v for k, v in default_resource_dict.items() if k not in resource_dict}
)
if not plot_dependency_graph:
from executorlib.task_scheduler.file.subprocess_spawner import (
from executorlib.task_scheduler.file.spawner_subprocess import (
execute_in_subprocess,
)
from executorlib.task_scheduler.file.task_scheduler import (
Expand Down
2 changes: 1 addition & 1 deletion executorlib/executor/slurm.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,7 +13,7 @@
)
from executorlib.task_scheduler.interactive.dependency import DependencyTaskScheduler
from executorlib.task_scheduler.interactive.onetoone import OneProcessTaskScheduler
from executorlib.task_scheduler.interactive.slurmspawner import (
from executorlib.task_scheduler.interactive.spawner_slurm import (
SrunSpawner,
validate_max_workers,
)
Expand Down
50 changes: 50 additions & 0 deletions executorlib/standalone/command.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,6 +3,8 @@
import sys
from typing import Optional

SLURM_COMMAND = "srun"


def get_command_path(executable: str) -> str:
"""
Expand DownExpand Up@@ -112,3 +114,51 @@ def get_interactive_execute_command(
else:
command_lst += [get_command_path(executable="interactive_serial.py")]
return command_lst


def generate_slurm_command(
cores: int,
cwd: Optional[str],
threads_per_core: int = 1,
gpus_per_core: int = 0,
num_nodes: Optional[int] = None,
exclusive: bool = False,
openmpi_oversubscribe: bool = False,
slurm_cmd_args: Optional[list[str]] = None,
pmi_mode: Optional[str] = None,
) -> list[str]:
"""
Generate the command list for the SLURM interface.

Args:
cores (int): The number of cores.
cwd (str): The current working directory.
threads_per_core (int, optional): The number of threads per core. Defaults to 1.
gpus_per_core (int, optional): The number of GPUs per core. Defaults to 0.
num_nodes (int, optional): The number of compute nodes to use for executing the task. Defaults to None.
exclusive (bool): Whether to exclusively reserve the compute nodes, or allow sharing compute notes. Defaults to False.
openmpi_oversubscribe (bool, optional): Whether to oversubscribe the cores. Defaults to False.
slurm_cmd_args (list[str], optional): Additional command line arguments. Defaults to [].
pmi_mode (str): PMI interface to use (OpenMPI v5 requires pmix) default is None

Returns:
list[str]: The generated command list.
"""
command_prepend_lst = [SLURM_COMMAND, "-n", str(cores)]
if cwd is not None:
command_prepend_lst += ["-D", cwd]
if pmi_mode is not None:
command_prepend_lst += ["--mpi=" + pmi_mode]
if num_nodes is not None:
command_prepend_lst += ["-N", str(num_nodes)]
if threads_per_core > 1:
command_prepend_lst += ["--cpus-per-task=" + str(threads_per_core)]
if gpus_per_core > 0:
command_prepend_lst += ["--gpus-per-task=" + str(gpus_per_core)]
if exclusive:
command_prepend_lst += ["--exact"]
if openmpi_oversubscribe:
command_prepend_lst += ["--oversubscribe"]
if slurm_cmd_args is not None and len(slurm_cmd_args) > 0:
command_prepend_lst += slurm_cmd_args
return command_prepend_lst
51 changes: 0 additions & 51 deletions executorlib/standalone/slurm_command.py

This file was deleted.

2 changes: 1 addition & 1 deletion executorlib/task_scheduler/file/shared.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,7 +7,7 @@
from executorlib.standalone.command import get_cache_execute_command
from executorlib.standalone.hdf import get_cache_files, get_output
from executorlib.standalone.serialize import serialize_funct
from executorlib.task_scheduler.file.subprocess_spawner import terminate_subprocess
from executorlib.task_scheduler.file.spawner_subprocess import terminate_subprocess


class FutureItem:
Expand Down
4 changes: 2 additions & 2 deletions executorlib/task_scheduler/file/task_scheduler.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,14 +11,14 @@
)
from executorlib.task_scheduler.base import TaskSchedulerBase
from executorlib.task_scheduler.file.shared import execute_tasks_h5
from executorlib.task_scheduler.file.subprocess_spawner import (
from executorlib.task_scheduler.file.spawner_subprocess import (
execute_in_subprocess,
terminate_subprocess,
)

try:
from executorlib.standalone.scheduler import terminate_with_pysqa
from executorlib.task_scheduler.file.queue_spawner import execute_with_pysqa
from executorlib.task_scheduler.file.spawner_pysqa import execute_with_pysqa
except ImportError:
# If pysqa is not available fall back to executing tasks in a subprocess
execute_with_pysqa = execute_in_subprocess # type: ignore
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
import os
from typing import Optional

from executorlib.standalone.command import generate_slurm_command

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Exclusive flag likely mapped to the wrong srun option in generate_slurm_command.

Srun’s exclusive allocation uses --exclusive, not --exact. Since this module now depends on generate_slurm_command, verify and fix there to avoid incorrect scheduling semantics.

Proposed fix in executorlib/standalone/command.py (illustrative):

# inside generate_slurm_command(...)ifexclusive:
command_prepend_lst+= ["--exclusive"] # not "--exact"

If --exact was intentional, please document the rationale and rename the parameter to avoid confusion.


🏁 Script executed:

#!/bin/bash# Locate generate_slurm_command definition with context
rg -nP --type=py 'def generate_slurm_command' -C10 executorlib/standalone/command.py
# Check for "--exact" usage in this file
rg -n '"--exact"' -C3 executorlib/standalone/command.py
# Check for "exclusive" parameter or flag handling
rg -n 'exclusive' -C10 executorlib/standalone/command.py

Length of output: 6979


Replace --exact with --exclusive for exclusive allocations

  • In executorlib/standalone/command.py → get_cache_execute_command (around lines 58–60), change
    command_prepend += ["--exact"]
    to
    command_prepend += ["--exclusive"]
  • In executorlib/standalone/command.py → generate_slurm_command (around lines 158–159), change
    command_prepend_lst += ["--exact"]
    to
    command_prepend_lst += ["--exclusive"]
    If --exact was intentional, document why and rename the exclusive parameter to avoid confusion.

from executorlib.standalone.interactive.spawner import SubprocessSpawner
from executorlib.standalone.slurm_command import generate_slurm_command


def validate_max_workers(max_workers: int, cores: int, threads_per_core: int):
Expand Down
2 changes: 1 addition & 1 deletion tests/test_cache_fileexecutor_mpi.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,7 +5,7 @@

try:
from executorlib.task_scheduler.file.task_scheduler import FileTaskScheduler
from executorlib.task_scheduler.file.subprocess_spawner import execute_in_subprocess
from executorlib.task_scheduler.file.spawner_subprocess import execute_in_subprocess

skip_h5py_test = False
except ImportError:
Expand Down
2 changes: 1 addition & 1 deletion tests/test_cache_fileexecutor_serial.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,7 +7,7 @@
from time import sleep

try:
from executorlib.task_scheduler.file.subprocess_spawner import (
from executorlib.task_scheduler.file.spawner_subprocess import (
execute_in_subprocess,
terminate_subprocess,
)
Expand Down
2 changes: 1 addition & 1 deletion tests/test_fluxclusterexecutor.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,7 +12,7 @@
import flux.job
from executorlib import terminate_tasks_in_cache
from executorlib.standalone.hdf import dump
from executorlib.task_scheduler.file.queue_spawner import execute_with_pysqa
from executorlib.task_scheduler.file.spawner_pysqa import execute_with_pysqa
from executorlib.standalone.scheduler import terminate_with_pysqa

skip_flux_test = "FLUX_URI" not in os.environ
Expand Down
2 changes: 1 addition & 1 deletion tests/test_fluxjobexecutor.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,7 +8,7 @@

try:
import flux.job
from executorlib.task_scheduler.interactive.fluxspawner import FluxPythonSpawner
from executorlib.task_scheduler.interactive.spawner_flux import FluxPythonSpawner

skip_flux_test = "FLUX_URI" not in os.environ
pmi = os.environ.get("EXECUTORLIB_PMIX", None)
Expand Down
2 changes: 1 addition & 1 deletion tests/test_fluxjobexecutor_plot.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,7 +10,7 @@
try:
import pygraphviz
import flux.job
from executorlib.task_scheduler.interactive.fluxspawner import FluxPythonSpawner
from executorlib.task_scheduler.interactive.spawner_flux import FluxPythonSpawner

skip_graphviz_flux_test = "FLUX_URI" not in os.environ
except ImportError:
Expand Down
2 changes: 1 addition & 1 deletion tests/test_fluxpythonspawner.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,7 +12,7 @@

try:
import flux.job
from executorlib.task_scheduler.interactive.fluxspawner import FluxPythonSpawner
from executorlib.task_scheduler.interactive.spawner_flux import FluxPythonSpawner

skip_flux_test = "FLUX_URI" not in os.environ
pmi = os.environ.get("EXECUTORLIB_PMIX", None)
Expand Down
2 changes: 1 addition & 1 deletion tests/test_interactive_slurmspawner.py
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
import unittest
from executorlib.standalone.slurm_command import generate_slurm_command
from executorlib.standalone.command import generate_slurm_command

try:
from executorlib.standalone.scheduler import pysqa_execute_command
Expand Down
2 changes: 1 addition & 1 deletion tests/test_standalone_interactive_backend.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,7 @@

from executorlib.standalone.interactive.backend import parse_arguments
from executorlib.standalone.interactive.spawner import MpiExecSpawner
from executorlib.task_scheduler.interactive.slurmspawner import SrunSpawner
from executorlib.task_scheduler.interactive.spawner_slurm import SrunSpawner


class TestParser(unittest.TestCase):
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion executorlib/__init__.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -55,7 +55,7 @@ def terminate_tasks_in_cache(
config_directory (str, optional): path to the config directory.
backend (str, optional): name of the backend used to spawn tasks ["slurm", "flux"].
"""
from executorlib.task_scheduler.file.queue_spawner import terminate_tasks_in_cache
from executorlib.task_scheduler.file.spawner_pysqa import terminate_tasks_in_cache

return terminate_tasks_in_cache(
cache_directory=cache_directory,
Expand Down
2 changes: 1 addition & 1 deletion executorlib/executor/flux.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -458,7 +458,7 @@ def create_flux_executor(
Returns:
InteractiveStepExecutor/ InteractiveExecutor
"""
from executorlib.task_scheduler.interactive.fluxspawner import (
from executorlib.task_scheduler.interactive.spawner_flux import (
FluxPythonSpawner,
validate_max_workers,
)
Expand Down
2 changes: 1 addition & 1 deletion executorlib/executor/single.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -314,7 +314,7 @@ def __init__(
{k: v for k, v in default_resource_dict.items() if k not in resource_dict}
)
if not plot_dependency_graph:
from executorlib.task_scheduler.file.subprocess_spawner import (
from executorlib.task_scheduler.file.spawner_subprocess import (
execute_in_subprocess,
)
from executorlib.task_scheduler.file.task_scheduler import (
Expand Down
2 changes: 1 addition & 1 deletion executorlib/executor/slurm.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,7 +13,7 @@
)
from executorlib.task_scheduler.interactive.dependency import DependencyTaskScheduler
from executorlib.task_scheduler.interactive.onetoone import OneProcessTaskScheduler
from executorlib.task_scheduler.interactive.slurmspawner import (
from executorlib.task_scheduler.interactive.spawner_slurm import (
SrunSpawner,
validate_max_workers,
)
Expand Down
50 changes: 50 additions & 0 deletions executorlib/standalone/command.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,6 +3,8 @@
import sys
from typing import Optional

SLURM_COMMAND = "srun"


def get_command_path(executable: str) -> str:
"""
Expand DownExpand Up@@ -112,3 +114,51 @@ def get_interactive_execute_command(
else:
command_lst += [get_command_path(executable="interactive_serial.py")]
return command_lst


def generate_slurm_command(
cores: int,
cwd: Optional[str],
threads_per_core: int = 1,
gpus_per_core: int = 0,
num_nodes: Optional[int] = None,
exclusive: bool = False,
openmpi_oversubscribe: bool = False,
slurm_cmd_args: Optional[list[str]] = None,
pmi_mode: Optional[str] = None,
) -> list[str]:
"""
Generate the command list for the SLURM interface.

Args:
cores (int): The number of cores.
cwd (str): The current working directory.
threads_per_core (int, optional): The number of threads per core. Defaults to 1.
gpus_per_core (int, optional): The number of GPUs per core. Defaults to 0.
num_nodes (int, optional): The number of compute nodes to use for executing the task. Defaults to None.
exclusive (bool): Whether to exclusively reserve the compute nodes, or allow sharing compute notes. Defaults to False.
openmpi_oversubscribe (bool, optional): Whether to oversubscribe the cores. Defaults to False.
slurm_cmd_args (list[str], optional): Additional command line arguments. Defaults to [].
pmi_mode (str): PMI interface to use (OpenMPI v5 requires pmix) default is None

Returns:
list[str]: The generated command list.
"""
command_prepend_lst = [SLURM_COMMAND, "-n", str(cores)]
if cwd is not None:
command_prepend_lst += ["-D", cwd]
if pmi_mode is not None:
command_prepend_lst += ["--mpi=" + pmi_mode]
if num_nodes is not None:
command_prepend_lst += ["-N", str(num_nodes)]
if threads_per_core > 1:
command_prepend_lst += ["--cpus-per-task=" + str(threads_per_core)]
if gpus_per_core > 0:
command_prepend_lst += ["--gpus-per-task=" + str(gpus_per_core)]
if exclusive:
command_prepend_lst += ["--exact"]
if openmpi_oversubscribe:
command_prepend_lst += ["--oversubscribe"]
if slurm_cmd_args is not None and len(slurm_cmd_args) > 0:
command_prepend_lst += slurm_cmd_args
return command_prepend_lst
51 changes: 0 additions & 51 deletions executorlib/standalone/slurm_command.py

This file was deleted.

2 changes: 1 addition & 1 deletion executorlib/task_scheduler/file/shared.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,7 +7,7 @@
from executorlib.standalone.command import get_cache_execute_command
from executorlib.standalone.hdf import get_cache_files, get_output
from executorlib.standalone.serialize import serialize_funct
from executorlib.task_scheduler.file.subprocess_spawner import terminate_subprocess
from executorlib.task_scheduler.file.spawner_subprocess import terminate_subprocess


class FutureItem:
Expand Down
4 changes: 2 additions & 2 deletions executorlib/task_scheduler/file/task_scheduler.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,14 +11,14 @@
)
from executorlib.task_scheduler.base import TaskSchedulerBase
from executorlib.task_scheduler.file.shared import execute_tasks_h5
from executorlib.task_scheduler.file.subprocess_spawner import (
from executorlib.task_scheduler.file.spawner_subprocess import (
execute_in_subprocess,
terminate_subprocess,
)

try:
from executorlib.standalone.scheduler import terminate_with_pysqa
from executorlib.task_scheduler.file.queue_spawner import execute_with_pysqa
from executorlib.task_scheduler.file.spawner_pysqa import execute_with_pysqa
except ImportError:
# If pysqa is not available fall back to executing tasks in a subprocess
execute_with_pysqa = execute_in_subprocess # type: ignore
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
import os
from typing import Optional

from executorlib.standalone.command import generate_slurm_command

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Exclusive flag likely mapped to the wrong srun option in generate_slurm_command.

Srun’s exclusive allocation uses --exclusive, not --exact. Since this module now depends on generate_slurm_command, verify and fix there to avoid incorrect scheduling semantics.

Proposed fix in executorlib/standalone/command.py (illustrative):

# inside generate_slurm_command(...)ifexclusive:
command_prepend_lst+= ["--exclusive"] # not "--exact"

If --exact was intentional, please document the rationale and rename the parameter to avoid confusion.


🏁 Script executed:

#!/bin/bash# Locate generate_slurm_command definition with context
rg -nP --type=py 'def generate_slurm_command' -C10 executorlib/standalone/command.py
# Check for "--exact" usage in this file
rg -n '"--exact"' -C3 executorlib/standalone/command.py
# Check for "exclusive" parameter or flag handling
rg -n 'exclusive' -C10 executorlib/standalone/command.py

Length of output: 6979


Replace --exact with --exclusive for exclusive allocations

  • In executorlib/standalone/command.py → get_cache_execute_command (around lines 58–60), change
    command_prepend += ["--exact"]
    to
    command_prepend += ["--exclusive"]
  • In executorlib/standalone/command.py → generate_slurm_command (around lines 158–159), change
    command_prepend_lst += ["--exact"]
    to
    command_prepend_lst += ["--exclusive"]
    If --exact was intentional, document why and rename the exclusive parameter to avoid confusion.

from executorlib.standalone.interactive.spawner import SubprocessSpawner
from executorlib.standalone.slurm_command import generate_slurm_command


def validate_max_workers(max_workers: int, cores: int, threads_per_core: int):
Expand Down
2 changes: 1 addition & 1 deletion tests/test_cache_fileexecutor_mpi.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,7 +5,7 @@

try:
from executorlib.task_scheduler.file.task_scheduler import FileTaskScheduler
from executorlib.task_scheduler.file.subprocess_spawner import execute_in_subprocess
from executorlib.task_scheduler.file.spawner_subprocess import execute_in_subprocess

skip_h5py_test = False
except ImportError:
Expand Down
2 changes: 1 addition & 1 deletion tests/test_cache_fileexecutor_serial.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,7 +7,7 @@
from time import sleep

try:
from executorlib.task_scheduler.file.subprocess_spawner import (
from executorlib.task_scheduler.file.spawner_subprocess import (
execute_in_subprocess,
terminate_subprocess,
)
Expand Down
2 changes: 1 addition & 1 deletion tests/test_fluxclusterexecutor.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,7 +12,7 @@
import flux.job
from executorlib import terminate_tasks_in_cache
from executorlib.standalone.hdf import dump
from executorlib.task_scheduler.file.queue_spawner import execute_with_pysqa
from executorlib.task_scheduler.file.spawner_pysqa import execute_with_pysqa
from executorlib.standalone.scheduler import terminate_with_pysqa

skip_flux_test = "FLUX_URI" not in os.environ
Expand Down
2 changes: 1 addition & 1 deletion tests/test_fluxjobexecutor.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,7 +8,7 @@

try:
import flux.job
from executorlib.task_scheduler.interactive.fluxspawner import FluxPythonSpawner
from executorlib.task_scheduler.interactive.spawner_flux import FluxPythonSpawner

skip_flux_test = "FLUX_URI" not in os.environ
pmi = os.environ.get("EXECUTORLIB_PMIX", None)
Expand Down
2 changes: 1 addition & 1 deletion tests/test_fluxjobexecutor_plot.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,7 +10,7 @@
try:
import pygraphviz
import flux.job
from executorlib.task_scheduler.interactive.fluxspawner import FluxPythonSpawner
from executorlib.task_scheduler.interactive.spawner_flux import FluxPythonSpawner

skip_graphviz_flux_test = "FLUX_URI" not in os.environ
except ImportError:
Expand Down
2 changes: 1 addition & 1 deletion tests/test_fluxpythonspawner.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,7 +12,7 @@

try:
import flux.job
from executorlib.task_scheduler.interactive.fluxspawner import FluxPythonSpawner
from executorlib.task_scheduler.interactive.spawner_flux import FluxPythonSpawner

skip_flux_test = "FLUX_URI" not in os.environ
pmi = os.environ.get("EXECUTORLIB_PMIX", None)
Expand Down
2 changes: 1 addition & 1 deletion tests/test_interactive_slurmspawner.py
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
import unittest
from executorlib.standalone.slurm_command import generate_slurm_command
from executorlib.standalone.command import generate_slurm_command

try:
from executorlib.standalone.scheduler import pysqa_execute_command
Expand Down
2 changes: 1 addition & 1 deletion tests/test_standalone_interactive_backend.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,7 @@

from executorlib.standalone.interactive.backend import parse_arguments
from executorlib.standalone.interactive.spawner import MpiExecSpawner
from executorlib.task_scheduler.interactive.slurmspawner import SrunSpawner
from executorlib.task_scheduler.interactive.spawner_slurm import SrunSpawner


class TestParser(unittest.TestCase):
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion executorlib/__init__.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -55,7 +55,7 @@ def terminate_tasks_in_cache(
config_directory (str, optional): path to the config directory.
backend (str, optional): name of the backend used to spawn tasks ["slurm", "flux"].
"""
from executorlib.task_scheduler.file.queue_spawner import terminate_tasks_in_cache
from executorlib.task_scheduler.file.spawner_pysqa import terminate_tasks_in_cache

return terminate_tasks_in_cache(
cache_directory=cache_directory,
Expand Down
2 changes: 1 addition & 1 deletion executorlib/executor/flux.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -458,7 +458,7 @@ def create_flux_executor(
Returns:
InteractiveStepExecutor/ InteractiveExecutor
"""
from executorlib.task_scheduler.interactive.fluxspawner import (
from executorlib.task_scheduler.interactive.spawner_flux import (
FluxPythonSpawner,
validate_max_workers,
)
Expand Down
2 changes: 1 addition & 1 deletion executorlib/executor/single.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -314,7 +314,7 @@ def __init__(
{k: v for k, v in default_resource_dict.items() if k not in resource_dict}
)
if not plot_dependency_graph:
from executorlib.task_scheduler.file.subprocess_spawner import (
from executorlib.task_scheduler.file.spawner_subprocess import (
execute_in_subprocess,
)
from executorlib.task_scheduler.file.task_scheduler import (
Expand Down
2 changes: 1 addition & 1 deletion executorlib/executor/slurm.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,7 +13,7 @@
)
from executorlib.task_scheduler.interactive.dependency import DependencyTaskScheduler
from executorlib.task_scheduler.interactive.onetoone import OneProcessTaskScheduler
from executorlib.task_scheduler.interactive.slurmspawner import (
from executorlib.task_scheduler.interactive.spawner_slurm import (
SrunSpawner,
validate_max_workers,
)
Expand Down
50 changes: 50 additions & 0 deletions executorlib/standalone/command.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,6 +3,8 @@
import sys
from typing import Optional

SLURM_COMMAND = "srun"


def get_command_path(executable: str) -> str:
"""
Expand DownExpand Up@@ -112,3 +114,51 @@ def get_interactive_execute_command(
else:
command_lst += [get_command_path(executable="interactive_serial.py")]
return command_lst


def generate_slurm_command(
cores: int,
cwd: Optional[str],
threads_per_core: int = 1,
gpus_per_core: int = 0,
num_nodes: Optional[int] = None,
exclusive: bool = False,
openmpi_oversubscribe: bool = False,
slurm_cmd_args: Optional[list[str]] = None,
pmi_mode: Optional[str] = None,
) -> list[str]:
"""
Generate the command list for the SLURM interface.

Args:
cores (int): The number of cores.
cwd (str): The current working directory.
threads_per_core (int, optional): The number of threads per core. Defaults to 1.
gpus_per_core (int, optional): The number of GPUs per core. Defaults to 0.
num_nodes (int, optional): The number of compute nodes to use for executing the task. Defaults to None.
exclusive (bool): Whether to exclusively reserve the compute nodes, or allow sharing compute notes. Defaults to False.
openmpi_oversubscribe (bool, optional): Whether to oversubscribe the cores. Defaults to False.
slurm_cmd_args (list[str], optional): Additional command line arguments. Defaults to [].
pmi_mode (str): PMI interface to use (OpenMPI v5 requires pmix) default is None

Returns:
list[str]: The generated command list.
"""
command_prepend_lst = [SLURM_COMMAND, "-n", str(cores)]
if cwd is not None:
command_prepend_lst += ["-D", cwd]
if pmi_mode is not None:
command_prepend_lst += ["--mpi=" + pmi_mode]
if num_nodes is not None:
command_prepend_lst += ["-N", str(num_nodes)]
if threads_per_core > 1:
command_prepend_lst += ["--cpus-per-task=" + str(threads_per_core)]
if gpus_per_core > 0:
command_prepend_lst += ["--gpus-per-task=" + str(gpus_per_core)]
if exclusive:
command_prepend_lst += ["--exact"]
if openmpi_oversubscribe:
command_prepend_lst += ["--oversubscribe"]
if slurm_cmd_args is not None and len(slurm_cmd_args) > 0:
command_prepend_lst += slurm_cmd_args
return command_prepend_lst
51 changes: 0 additions & 51 deletions executorlib/standalone/slurm_command.py

This file was deleted.

2 changes: 1 addition & 1 deletion executorlib/task_scheduler/file/shared.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,7 +7,7 @@
from executorlib.standalone.command import get_cache_execute_command
from executorlib.standalone.hdf import get_cache_files, get_output
from executorlib.standalone.serialize import serialize_funct
from executorlib.task_scheduler.file.subprocess_spawner import terminate_subprocess
from executorlib.task_scheduler.file.spawner_subprocess import terminate_subprocess


class FutureItem:
Expand Down
4 changes: 2 additions & 2 deletions executorlib/task_scheduler/file/task_scheduler.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,14 +11,14 @@
)
from executorlib.task_scheduler.base import TaskSchedulerBase
from executorlib.task_scheduler.file.shared import execute_tasks_h5
from executorlib.task_scheduler.file.subprocess_spawner import (
from executorlib.task_scheduler.file.spawner_subprocess import (
execute_in_subprocess,
terminate_subprocess,
)

try:
from executorlib.standalone.scheduler import terminate_with_pysqa
from executorlib.task_scheduler.file.queue_spawner import execute_with_pysqa
from executorlib.task_scheduler.file.spawner_pysqa import execute_with_pysqa
except ImportError:
# If pysqa is not available fall back to executing tasks in a subprocess
execute_with_pysqa = execute_in_subprocess # type: ignore
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
import os
from typing import Optional

from executorlib.standalone.command import generate_slurm_command

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Exclusive flag likely mapped to the wrong srun option in generate_slurm_command.

Srun’s exclusive allocation uses --exclusive, not --exact. Since this module now depends on generate_slurm_command, verify and fix there to avoid incorrect scheduling semantics.

Proposed fix in executorlib/standalone/command.py (illustrative):

# inside generate_slurm_command(...)ifexclusive:
command_prepend_lst+= ["--exclusive"] # not "--exact"

If --exact was intentional, please document the rationale and rename the parameter to avoid confusion.


🏁 Script executed:

#!/bin/bash# Locate generate_slurm_command definition with context
rg -nP --type=py 'def generate_slurm_command' -C10 executorlib/standalone/command.py
# Check for "--exact" usage in this file
rg -n '"--exact"' -C3 executorlib/standalone/command.py
# Check for "exclusive" parameter or flag handling
rg -n 'exclusive' -C10 executorlib/standalone/command.py

Length of output: 6979


Replace --exact with --exclusive for exclusive allocations

  • In executorlib/standalone/command.py → get_cache_execute_command (around lines 58–60), change
    command_prepend += ["--exact"]
    to
    command_prepend += ["--exclusive"]
  • In executorlib/standalone/command.py → generate_slurm_command (around lines 158–159), change
    command_prepend_lst += ["--exact"]
    to
    command_prepend_lst += ["--exclusive"]
    If --exact was intentional, document why and rename the exclusive parameter to avoid confusion.

from executorlib.standalone.interactive.spawner import SubprocessSpawner
from executorlib.standalone.slurm_command import generate_slurm_command


def validate_max_workers(max_workers: int, cores: int, threads_per_core: int):
Expand Down
2 changes: 1 addition & 1 deletion tests/test_cache_fileexecutor_mpi.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,7 +5,7 @@

try:
from executorlib.task_scheduler.file.task_scheduler import FileTaskScheduler
from executorlib.task_scheduler.file.subprocess_spawner import execute_in_subprocess
from executorlib.task_scheduler.file.spawner_subprocess import execute_in_subprocess

skip_h5py_test = False
except ImportError:
Expand Down
2 changes: 1 addition & 1 deletion tests/test_cache_fileexecutor_serial.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,7 +7,7 @@
from time import sleep

try:
from executorlib.task_scheduler.file.subprocess_spawner import (
from executorlib.task_scheduler.file.spawner_subprocess import (
execute_in_subprocess,
terminate_subprocess,
)
Expand Down
2 changes: 1 addition & 1 deletion tests/test_fluxclusterexecutor.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,7 +12,7 @@
import flux.job
from executorlib import terminate_tasks_in_cache
from executorlib.standalone.hdf import dump
from executorlib.task_scheduler.file.queue_spawner import execute_with_pysqa
from executorlib.task_scheduler.file.spawner_pysqa import execute_with_pysqa
from executorlib.standalone.scheduler import terminate_with_pysqa

skip_flux_test = "FLUX_URI" not in os.environ
Expand Down
2 changes: 1 addition & 1 deletion tests/test_fluxjobexecutor.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,7 +8,7 @@

try:
import flux.job
from executorlib.task_scheduler.interactive.fluxspawner import FluxPythonSpawner
from executorlib.task_scheduler.interactive.spawner_flux import FluxPythonSpawner

skip_flux_test = "FLUX_URI" not in os.environ
pmi = os.environ.get("EXECUTORLIB_PMIX", None)
Expand Down
2 changes: 1 addition & 1 deletion tests/test_fluxjobexecutor_plot.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,7 +10,7 @@
try:
import pygraphviz
import flux.job
from executorlib.task_scheduler.interactive.fluxspawner import FluxPythonSpawner
from executorlib.task_scheduler.interactive.spawner_flux import FluxPythonSpawner

skip_graphviz_flux_test = "FLUX_URI" not in os.environ
except ImportError:
Expand Down
2 changes: 1 addition & 1 deletion tests/test_fluxpythonspawner.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,7 +12,7 @@

try:
import flux.job
from executorlib.task_scheduler.interactive.fluxspawner import FluxPythonSpawner
from executorlib.task_scheduler.interactive.spawner_flux import FluxPythonSpawner

skip_flux_test = "FLUX_URI" not in os.environ
pmi = os.environ.get("EXECUTORLIB_PMIX", None)
Expand Down
2 changes: 1 addition & 1 deletion tests/test_interactive_slurmspawner.py
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
import unittest
from executorlib.standalone.slurm_command import generate_slurm_command
from executorlib.standalone.command import generate_slurm_command

try:
from executorlib.standalone.scheduler import pysqa_execute_command
Expand Down
2 changes: 1 addition & 1 deletion tests/test_standalone_interactive_backend.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,7 @@

from executorlib.standalone.interactive.backend import parse_arguments
from executorlib.standalone.interactive.spawner import MpiExecSpawner
from executorlib.task_scheduler.interactive.slurmspawner import SrunSpawner
from executorlib.task_scheduler.interactive.spawner_slurm import SrunSpawner


class TestParser(unittest.TestCase):
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion executorlib/__init__.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -55,7 +55,7 @@ def terminate_tasks_in_cache(
config_directory (str, optional): path to the config directory.
backend (str, optional): name of the backend used to spawn tasks ["slurm", "flux"].
"""
from executorlib.task_scheduler.file.queue_spawner import terminate_tasks_in_cache
from executorlib.task_scheduler.file.spawner_pysqa import terminate_tasks_in_cache

return terminate_tasks_in_cache(
cache_directory=cache_directory,
Expand Down
2 changes: 1 addition & 1 deletion executorlib/executor/flux.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -458,7 +458,7 @@ def create_flux_executor(
Returns:
InteractiveStepExecutor/ InteractiveExecutor
"""
from executorlib.task_scheduler.interactive.fluxspawner import (
from executorlib.task_scheduler.interactive.spawner_flux import (
FluxPythonSpawner,
validate_max_workers,
)
Expand Down
2 changes: 1 addition & 1 deletion executorlib/executor/single.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -314,7 +314,7 @@ def __init__(
{k: v for k, v in default_resource_dict.items() if k not in resource_dict}
)
if not plot_dependency_graph:
from executorlib.task_scheduler.file.subprocess_spawner import (
from executorlib.task_scheduler.file.spawner_subprocess import (
execute_in_subprocess,
)
from executorlib.task_scheduler.file.task_scheduler import (
Expand Down
2 changes: 1 addition & 1 deletion executorlib/executor/slurm.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,7 +13,7 @@
)
from executorlib.task_scheduler.interactive.dependency import DependencyTaskScheduler
from executorlib.task_scheduler.interactive.onetoone import OneProcessTaskScheduler
from executorlib.task_scheduler.interactive.slurmspawner import (
from executorlib.task_scheduler.interactive.spawner_slurm import (
SrunSpawner,
validate_max_workers,
)
Expand Down
50 changes: 50 additions & 0 deletions executorlib/standalone/command.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,6 +3,8 @@
import sys
from typing import Optional

SLURM_COMMAND = "srun"


def get_command_path(executable: str) -> str:
"""
Expand DownExpand Up@@ -112,3 +114,51 @@ def get_interactive_execute_command(
else:
command_lst += [get_command_path(executable="interactive_serial.py")]
return command_lst


def generate_slurm_command(
cores: int,
cwd: Optional[str],
threads_per_core: int = 1,
gpus_per_core: int = 0,
num_nodes: Optional[int] = None,
exclusive: bool = False,
openmpi_oversubscribe: bool = False,
slurm_cmd_args: Optional[list[str]] = None,
pmi_mode: Optional[str] = None,
) -> list[str]:
"""
Generate the command list for the SLURM interface.

Args:
cores (int): The number of cores.
cwd (str): The current working directory.
threads_per_core (int, optional): The number of threads per core. Defaults to 1.
gpus_per_core (int, optional): The number of GPUs per core. Defaults to 0.
num_nodes (int, optional): The number of compute nodes to use for executing the task. Defaults to None.
exclusive (bool): Whether to exclusively reserve the compute nodes, or allow sharing compute notes. Defaults to False.
openmpi_oversubscribe (bool, optional): Whether to oversubscribe the cores. Defaults to False.
slurm_cmd_args (list[str], optional): Additional command line arguments. Defaults to [].
pmi_mode (str): PMI interface to use (OpenMPI v5 requires pmix) default is None

Returns:
list[str]: The generated command list.
"""
command_prepend_lst = [SLURM_COMMAND, "-n", str(cores)]
if cwd is not None:
command_prepend_lst += ["-D", cwd]
if pmi_mode is not None:
command_prepend_lst += ["--mpi=" + pmi_mode]
if num_nodes is not None:
command_prepend_lst += ["-N", str(num_nodes)]
if threads_per_core > 1:
command_prepend_lst += ["--cpus-per-task=" + str(threads_per_core)]
if gpus_per_core > 0:
command_prepend_lst += ["--gpus-per-task=" + str(gpus_per_core)]
if exclusive:
command_prepend_lst += ["--exact"]
if openmpi_oversubscribe:
command_prepend_lst += ["--oversubscribe"]
if slurm_cmd_args is not None and len(slurm_cmd_args) > 0:
command_prepend_lst += slurm_cmd_args
return command_prepend_lst
51 changes: 0 additions & 51 deletions executorlib/standalone/slurm_command.py

This file was deleted.

2 changes: 1 addition & 1 deletion executorlib/task_scheduler/file/shared.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,7 +7,7 @@
from executorlib.standalone.command import get_cache_execute_command
from executorlib.standalone.hdf import get_cache_files, get_output
from executorlib.standalone.serialize import serialize_funct
from executorlib.task_scheduler.file.subprocess_spawner import terminate_subprocess
from executorlib.task_scheduler.file.spawner_subprocess import terminate_subprocess


class FutureItem:
Expand Down
4 changes: 2 additions & 2 deletions executorlib/task_scheduler/file/task_scheduler.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,14 +11,14 @@
)
from executorlib.task_scheduler.base import TaskSchedulerBase
from executorlib.task_scheduler.file.shared import execute_tasks_h5
from executorlib.task_scheduler.file.subprocess_spawner import (
from executorlib.task_scheduler.file.spawner_subprocess import (
execute_in_subprocess,
terminate_subprocess,
)

try:
from executorlib.standalone.scheduler import terminate_with_pysqa
from executorlib.task_scheduler.file.queue_spawner import execute_with_pysqa
from executorlib.task_scheduler.file.spawner_pysqa import execute_with_pysqa
except ImportError:
# If pysqa is not available fall back to executing tasks in a subprocess
execute_with_pysqa = execute_in_subprocess # type: ignore
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
import os
from typing import Optional

from executorlib.standalone.command import generate_slurm_command

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Exclusive flag likely mapped to the wrong srun option in generate_slurm_command.

Srun’s exclusive allocation uses --exclusive, not --exact. Since this module now depends on generate_slurm_command, verify and fix there to avoid incorrect scheduling semantics.

Proposed fix in executorlib/standalone/command.py (illustrative):

# inside generate_slurm_command(...)ifexclusive:
command_prepend_lst+= ["--exclusive"] # not "--exact"

If --exact was intentional, please document the rationale and rename the parameter to avoid confusion.


🏁 Script executed:

#!/bin/bash# Locate generate_slurm_command definition with context
rg -nP --type=py 'def generate_slurm_command' -C10 executorlib/standalone/command.py
# Check for "--exact" usage in this file
rg -n '"--exact"' -C3 executorlib/standalone/command.py
# Check for "exclusive" parameter or flag handling
rg -n 'exclusive' -C10 executorlib/standalone/command.py

Length of output: 6979


Replace --exact with --exclusive for exclusive allocations

  • In executorlib/standalone/command.py → get_cache_execute_command (around lines 58–60), change
    command_prepend += ["--exact"]
    to
    command_prepend += ["--exclusive"]
  • In executorlib/standalone/command.py → generate_slurm_command (around lines 158–159), change
    command_prepend_lst += ["--exact"]
    to
    command_prepend_lst += ["--exclusive"]
    If --exact was intentional, document why and rename the exclusive parameter to avoid confusion.

from executorlib.standalone.interactive.spawner import SubprocessSpawner
from executorlib.standalone.slurm_command import generate_slurm_command


def validate_max_workers(max_workers: int, cores: int, threads_per_core: int):
Expand Down
2 changes: 1 addition & 1 deletion tests/test_cache_fileexecutor_mpi.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,7 +5,7 @@

try:
from executorlib.task_scheduler.file.task_scheduler import FileTaskScheduler
from executorlib.task_scheduler.file.subprocess_spawner import execute_in_subprocess
from executorlib.task_scheduler.file.spawner_subprocess import execute_in_subprocess

skip_h5py_test = False
except ImportError:
Expand Down
2 changes: 1 addition & 1 deletion tests/test_cache_fileexecutor_serial.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,7 +7,7 @@
from time import sleep

try:
from executorlib.task_scheduler.file.subprocess_spawner import (
from executorlib.task_scheduler.file.spawner_subprocess import (
execute_in_subprocess,
terminate_subprocess,
)
Expand Down
2 changes: 1 addition & 1 deletion tests/test_fluxclusterexecutor.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,7 +12,7 @@
import flux.job
from executorlib import terminate_tasks_in_cache
from executorlib.standalone.hdf import dump
from executorlib.task_scheduler.file.queue_spawner import execute_with_pysqa
from executorlib.task_scheduler.file.spawner_pysqa import execute_with_pysqa
from executorlib.standalone.scheduler import terminate_with_pysqa

skip_flux_test = "FLUX_URI" not in os.environ
Expand Down
2 changes: 1 addition & 1 deletion tests/test_fluxjobexecutor.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,7 +8,7 @@

try:
import flux.job
from executorlib.task_scheduler.interactive.fluxspawner import FluxPythonSpawner
from executorlib.task_scheduler.interactive.spawner_flux import FluxPythonSpawner

skip_flux_test = "FLUX_URI" not in os.environ
pmi = os.environ.get("EXECUTORLIB_PMIX", None)
Expand Down
2 changes: 1 addition & 1 deletion tests/test_fluxjobexecutor_plot.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,7 +10,7 @@
try:
import pygraphviz
import flux.job
from executorlib.task_scheduler.interactive.fluxspawner import FluxPythonSpawner
from executorlib.task_scheduler.interactive.spawner_flux import FluxPythonSpawner

skip_graphviz_flux_test = "FLUX_URI" not in os.environ
except ImportError:
Expand Down
2 changes: 1 addition & 1 deletion tests/test_fluxpythonspawner.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,7 +12,7 @@

try:
import flux.job
from executorlib.task_scheduler.interactive.fluxspawner import FluxPythonSpawner
from executorlib.task_scheduler.interactive.spawner_flux import FluxPythonSpawner

skip_flux_test = "FLUX_URI" not in os.environ
pmi = os.environ.get("EXECUTORLIB_PMIX", None)
Expand Down
2 changes: 1 addition & 1 deletion tests/test_interactive_slurmspawner.py
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
import unittest
from executorlib.standalone.slurm_command import generate_slurm_command
from executorlib.standalone.command import generate_slurm_command

try:
from executorlib.standalone.scheduler import pysqa_execute_command
Expand Down
2 changes: 1 addition & 1 deletion tests/test_standalone_interactive_backend.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,7 @@

from executorlib.standalone.interactive.backend import parse_arguments
from executorlib.standalone.interactive.spawner import MpiExecSpawner
from executorlib.task_scheduler.interactive.slurmspawner import SrunSpawner
from executorlib.task_scheduler.interactive.spawner_slurm import SrunSpawner


class TestParser(unittest.TestCase):
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion executorlib/__init__.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -55,7 +55,7 @@ def terminate_tasks_in_cache(
config_directory (str, optional): path to the config directory.
backend (str, optional): name of the backend used to spawn tasks ["slurm", "flux"].
"""
from executorlib.task_scheduler.file.queue_spawner import terminate_tasks_in_cache
from executorlib.task_scheduler.file.spawner_pysqa import terminate_tasks_in_cache

return terminate_tasks_in_cache(
cache_directory=cache_directory,
Expand Down
2 changes: 1 addition & 1 deletion executorlib/executor/flux.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -458,7 +458,7 @@ def create_flux_executor(
Returns:
InteractiveStepExecutor/ InteractiveExecutor
"""
from executorlib.task_scheduler.interactive.fluxspawner import (
from executorlib.task_scheduler.interactive.spawner_flux import (
FluxPythonSpawner,
validate_max_workers,
)
Expand Down
2 changes: 1 addition & 1 deletion executorlib/executor/single.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -314,7 +314,7 @@ def __init__(
{k: v for k, v in default_resource_dict.items() if k not in resource_dict}
)
if not plot_dependency_graph:
from executorlib.task_scheduler.file.subprocess_spawner import (
from executorlib.task_scheduler.file.spawner_subprocess import (
execute_in_subprocess,
)
from executorlib.task_scheduler.file.task_scheduler import (
Expand Down
2 changes: 1 addition & 1 deletion executorlib/executor/slurm.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,7 +13,7 @@
)
from executorlib.task_scheduler.interactive.dependency import DependencyTaskScheduler
from executorlib.task_scheduler.interactive.onetoone import OneProcessTaskScheduler
from executorlib.task_scheduler.interactive.slurmspawner import (
from executorlib.task_scheduler.interactive.spawner_slurm import (
SrunSpawner,
validate_max_workers,
)
Expand Down
50 changes: 50 additions & 0 deletions executorlib/standalone/command.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,6 +3,8 @@
import sys
from typing import Optional

SLURM_COMMAND = "srun"


def get_command_path(executable: str) -> str:
"""
Expand DownExpand Up@@ -112,3 +114,51 @@ def get_interactive_execute_command(
else:
command_lst += [get_command_path(executable="interactive_serial.py")]
return command_lst


def generate_slurm_command(
cores: int,
cwd: Optional[str],
threads_per_core: int = 1,
gpus_per_core: int = 0,
num_nodes: Optional[int] = None,
exclusive: bool = False,
openmpi_oversubscribe: bool = False,
slurm_cmd_args: Optional[list[str]] = None,
pmi_mode: Optional[str] = None,
) -> list[str]:
"""
Generate the command list for the SLURM interface.

Args:
cores (int): The number of cores.
cwd (str): The current working directory.
threads_per_core (int, optional): The number of threads per core. Defaults to 1.
gpus_per_core (int, optional): The number of GPUs per core. Defaults to 0.
num_nodes (int, optional): The number of compute nodes to use for executing the task. Defaults to None.
exclusive (bool): Whether to exclusively reserve the compute nodes, or allow sharing compute notes. Defaults to False.
openmpi_oversubscribe (bool, optional): Whether to oversubscribe the cores. Defaults to False.
slurm_cmd_args (list[str], optional): Additional command line arguments. Defaults to [].
pmi_mode (str): PMI interface to use (OpenMPI v5 requires pmix) default is None

Returns:
list[str]: The generated command list.
"""
command_prepend_lst = [SLURM_COMMAND, "-n", str(cores)]
if cwd is not None:
command_prepend_lst += ["-D", cwd]
if pmi_mode is not None:
command_prepend_lst += ["--mpi=" + pmi_mode]
if num_nodes is not None:
command_prepend_lst += ["-N", str(num_nodes)]
if threads_per_core > 1:
command_prepend_lst += ["--cpus-per-task=" + str(threads_per_core)]
if gpus_per_core > 0:
command_prepend_lst += ["--gpus-per-task=" + str(gpus_per_core)]
if exclusive:
command_prepend_lst += ["--exact"]
if openmpi_oversubscribe:
command_prepend_lst += ["--oversubscribe"]
if slurm_cmd_args is not None and len(slurm_cmd_args) > 0:
command_prepend_lst += slurm_cmd_args
return command_prepend_lst
51 changes: 0 additions & 51 deletions executorlib/standalone/slurm_command.py

This file was deleted.

2 changes: 1 addition & 1 deletion executorlib/task_scheduler/file/shared.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,7 +7,7 @@
from executorlib.standalone.command import get_cache_execute_command
from executorlib.standalone.hdf import get_cache_files, get_output
from executorlib.standalone.serialize import serialize_funct
from executorlib.task_scheduler.file.subprocess_spawner import terminate_subprocess
from executorlib.task_scheduler.file.spawner_subprocess import terminate_subprocess


class FutureItem:
Expand Down
4 changes: 2 additions & 2 deletions executorlib/task_scheduler/file/task_scheduler.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,14 +11,14 @@
)
from executorlib.task_scheduler.base import TaskSchedulerBase
from executorlib.task_scheduler.file.shared import execute_tasks_h5
from executorlib.task_scheduler.file.subprocess_spawner import (
from executorlib.task_scheduler.file.spawner_subprocess import (
execute_in_subprocess,
terminate_subprocess,
)

try:
from executorlib.standalone.scheduler import terminate_with_pysqa
from executorlib.task_scheduler.file.queue_spawner import execute_with_pysqa
from executorlib.task_scheduler.file.spawner_pysqa import execute_with_pysqa
except ImportError:
# If pysqa is not available fall back to executing tasks in a subprocess
execute_with_pysqa = execute_in_subprocess # type: ignore
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
import os
from typing import Optional

from executorlib.standalone.command import generate_slurm_command

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Exclusive flag likely mapped to the wrong srun option in generate_slurm_command.

Srun’s exclusive allocation uses --exclusive, not --exact. Since this module now depends on generate_slurm_command, verify and fix there to avoid incorrect scheduling semantics.

Proposed fix in executorlib/standalone/command.py (illustrative):

# inside generate_slurm_command(...)ifexclusive:
command_prepend_lst+= ["--exclusive"] # not "--exact"

If --exact was intentional, please document the rationale and rename the parameter to avoid confusion.


🏁 Script executed:

#!/bin/bash# Locate generate_slurm_command definition with context
rg -nP --type=py 'def generate_slurm_command' -C10 executorlib/standalone/command.py
# Check for "--exact" usage in this file
rg -n '"--exact"' -C3 executorlib/standalone/command.py
# Check for "exclusive" parameter or flag handling
rg -n 'exclusive' -C10 executorlib/standalone/command.py

Length of output: 6979


Replace --exact with --exclusive for exclusive allocations

  • In executorlib/standalone/command.py → get_cache_execute_command (around lines 58–60), change
    command_prepend += ["--exact"]
    to
    command_prepend += ["--exclusive"]
  • In executorlib/standalone/command.py → generate_slurm_command (around lines 158–159), change
    command_prepend_lst += ["--exact"]
    to
    command_prepend_lst += ["--exclusive"]
    If --exact was intentional, document why and rename the exclusive parameter to avoid confusion.

from executorlib.standalone.interactive.spawner import SubprocessSpawner
from executorlib.standalone.slurm_command import generate_slurm_command


def validate_max_workers(max_workers: int, cores: int, threads_per_core: int):
Expand Down
2 changes: 1 addition & 1 deletion tests/test_cache_fileexecutor_mpi.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,7 +5,7 @@

try:
from executorlib.task_scheduler.file.task_scheduler import FileTaskScheduler
from executorlib.task_scheduler.file.subprocess_spawner import execute_in_subprocess
from executorlib.task_scheduler.file.spawner_subprocess import execute_in_subprocess

skip_h5py_test = False
except ImportError:
Expand Down
2 changes: 1 addition & 1 deletion tests/test_cache_fileexecutor_serial.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,7 +7,7 @@
from time import sleep

try:
from executorlib.task_scheduler.file.subprocess_spawner import (
from executorlib.task_scheduler.file.spawner_subprocess import (
execute_in_subprocess,
terminate_subprocess,
)
Expand Down
2 changes: 1 addition & 1 deletion tests/test_fluxclusterexecutor.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,7 +12,7 @@
import flux.job
from executorlib import terminate_tasks_in_cache
from executorlib.standalone.hdf import dump
from executorlib.task_scheduler.file.queue_spawner import execute_with_pysqa
from executorlib.task_scheduler.file.spawner_pysqa import execute_with_pysqa
from executorlib.standalone.scheduler import terminate_with_pysqa

skip_flux_test = "FLUX_URI" not in os.environ
Expand Down
2 changes: 1 addition & 1 deletion tests/test_fluxjobexecutor.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,7 +8,7 @@

try:
import flux.job
from executorlib.task_scheduler.interactive.fluxspawner import FluxPythonSpawner
from executorlib.task_scheduler.interactive.spawner_flux import FluxPythonSpawner

skip_flux_test = "FLUX_URI" not in os.environ
pmi = os.environ.get("EXECUTORLIB_PMIX", None)
Expand Down
2 changes: 1 addition & 1 deletion tests/test_fluxjobexecutor_plot.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,7 +10,7 @@
try:
import pygraphviz
import flux.job
from executorlib.task_scheduler.interactive.fluxspawner import FluxPythonSpawner
from executorlib.task_scheduler.interactive.spawner_flux import FluxPythonSpawner

skip_graphviz_flux_test = "FLUX_URI" not in os.environ
except ImportError:
Expand Down
2 changes: 1 addition & 1 deletion tests/test_fluxpythonspawner.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,7 +12,7 @@

try:
import flux.job
from executorlib.task_scheduler.interactive.fluxspawner import FluxPythonSpawner
from executorlib.task_scheduler.interactive.spawner_flux import FluxPythonSpawner

skip_flux_test = "FLUX_URI" not in os.environ
pmi = os.environ.get("EXECUTORLIB_PMIX", None)
Expand Down
2 changes: 1 addition & 1 deletion tests/test_interactive_slurmspawner.py
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
import unittest
from executorlib.standalone.slurm_command import generate_slurm_command
from executorlib.standalone.command import generate_slurm_command

try:
from executorlib.standalone.scheduler import pysqa_execute_command
Expand Down
2 changes: 1 addition & 1 deletion tests/test_standalone_interactive_backend.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,7 @@

from executorlib.standalone.interactive.backend import parse_arguments
from executorlib.standalone.interactive.spawner import MpiExecSpawner
from executorlib.task_scheduler.interactive.slurmspawner import SrunSpawner
from executorlib.task_scheduler.interactive.spawner_slurm import SrunSpawner


class TestParser(unittest.TestCase):
Expand Down
Loading