diff --git a/snakemake/exceptions.py b/snakemake/exceptions.py index 591a670c9..929065ef7 100644 --- a/snakemake/exceptions.py +++ b/snakemake/exceptions.py @@ -530,3 +530,17 @@ def __init__(self, rule, targetfile): class CacheMissException(Exception): pass + + +class LockException(WorkflowError): + def __init__(self): + super().__init__( + "Error: Directory cannot be locked. Please make " + "sure that no other Snakemake process is trying to create " + "the same files in the following directory:\n{}\n" + "If you are sure that no other " + "instances of snakemake are running on this directory, " + "the remaining lock was likely caused by a kill signal or " + "a power loss. It can be removed with " + "the --unlock argument.".format(os.getcwd()) + ) diff --git a/snakemake/persistence.py b/snakemake/persistence.py index c292c2c0a..4292c7c7a 100755 --- a/snakemake/persistence.py +++ b/snakemake/persistence.py @@ -15,6 +15,7 @@ from itertools import filterfalse, count from pathlib import Path +import snakemake.exceptions from snakemake.logging import logger from snakemake.jobs import jobfiles from snakemake.utils import listfiles @@ -167,7 +168,7 @@ def lock_warn_only(self): def lock(self): if self.locked: - raise IOError("Another snakemake process " "has locked this directory.") + raise snakemake.exceptions.LockException() self._lock(self.all_inputfiles(), "input") self._lock(self.all_outputfiles(), "output") diff --git a/snakemake/workflow.py b/snakemake/workflow.py index 8eeaa8065..982b9dcb7 100644 --- a/snakemake/workflow.py +++ b/snakemake/workflow.py @@ -798,20 +798,7 @@ def files(items): dag.update_checkpoint_dependencies() dag.check_dynamic() - try: - self.persistence.lock() - except IOError: - logger.error( - "Error: Directory cannot be locked. Please make " - "sure that no other Snakemake process is trying to create " - "the same files in the following directory:\n{}\n" - "If you are sure that no other " - "instances of snakemake are running on this directory, " - "the remaining lock was likely caused by a kill signal or " - "a power loss. It can be removed with " - "the --unlock argument.".format(os.getcwd()) - ) - return False + self.persistence.lock() if cleanup_shadow: self.persistence.cleanup_shadow() diff --git a/tests/testapi.py b/tests/testapi.py index e3904ac78..f2e608ff3 100644 --- a/tests/testapi.py +++ b/tests/testapi.py @@ -80,3 +80,17 @@ def test_dicts_in_config(): "test": {"this_dict": "shoult_not_either"}, }, ) + +def test_lockexception(): + from snakemake.persistence import Persistence + from snakemake.exceptions import LockException + + persistence = Persistence() + persistence.all_inputfiles = lambda: ["A.txt"] + persistence.all_outputfiles = lambda: ["B.txt"] + persistence.lock() + try: + persistence.lock() + except LockException as e: + return True + assert False