diff --git a/snakemake_executor_plugin_slurm/__init__.py b/snakemake_executor_plugin_slurm/__init__.py index 499679b7..a2a29150 100644 --- a/snakemake_executor_plugin_slurm/__init__.py +++ b/snakemake_executor_plugin_slurm/__init__.py @@ -180,7 +180,9 @@ def run_job(self, job: JobExecutorInterface): group_or_rule = f"group_{job.name}" if job.is_group() else f"rule_{job.name}" try: - wildcard_str = "_".join(job.wildcards) if job.wildcards else "" + wildcard_str = ( + "_".join(job.wildcards).replace("/", "_") if job.wildcards else "" + ) except AttributeError: wildcard_str = "" @@ -264,10 +266,10 @@ def run_job(self, job: JobExecutorInterface): "specified. Assuming 'tasks_per_node=1'." "Probably not what you want." ) + # we need to set cpus-per-task OR cpus-per-gpu, the function # will return a string with the corresponding value call += f" {get_cpu_setting(job, gpu_job)}" - if job.resources.get("slurm_extra"): self.check_slurm_extra(job) call += f" {job.resources.slurm_extra}" diff --git a/tests/tests.py b/tests/tests.py index bba5f398..63a7dcf3 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -16,12 +16,12 @@ def get_executor(self) -> str: return "slurm" def get_executor_settings(self) -> Optional[ExecutorSettingsBase]: - return ExecutorSettings() + return ExecutorSettings(init_seconds_before_status_checks=1) class TestWorkflowsRequeue(TestWorkflows): def get_executor_settings(self) -> Optional[ExecutorSettingsBase]: - return ExecutorSettings(requeue=True) + return ExecutorSettings(requeue=True, init_seconds_before_status_checks=1) class TestGresString: @@ -110,3 +110,38 @@ def test_both_gres_and_gpu_set(self, mock_job): WorkflowError, match="GRES and GPU are set. Please only set one of them." ): set_gres_string(job) + + +class TestWildcardsWithSlashes(snakemake.common.tests.TestWorkflowsLocalStorageBase): + """ + Test handling of wildcards with slashes to ensure log directories are + correctly constructed. + """ + + __test__ = True + + def get_executor(self) -> str: + return "slurm" + + def get_executor_settings(self) -> Optional[ExecutorSettingsBase]: + return ExecutorSettings( + logdir="test_logdir", init_seconds_before_status_checks=1 + ) + + def test_wildcard_slash_replacement(self): + """ + Test that slashes in wildcards are correctly replaced with + underscores in log directory paths. + """ + + # Just test the wildcard sanitization logic directly + wildcards = ["/leading_slash", "middle/slash", "trailing/"] + + # This is the actual logic from the Executor.run_job method + wildcard_str = "_".join(wildcards).replace("/", "_") if wildcards else "" + + # Assert that slashes are correctly replaced with underscores + assert wildcard_str == "_leading_slash_middle_slash_trailing_" + + # Verify no slashes remain in the wildcard string + assert "/" not in wildcard_str