Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
fix: more details on input and output exceptions (missing input, prot…
…ected output, etc.) (#1453)
  • Loading branch information
johanneskoester committed Mar 3, 2022
1 parent 417f40d commit 8d64af2
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 30 deletions.
4 changes: 2 additions & 2 deletions snakemake/dag.py
Expand Up @@ -516,7 +516,7 @@ def check_and_touch_output(
if (f.is_directory and not f.remote_object and not os.path.isdir(f)) or (
not f.remote_object and os.path.isdir(f) and not f.is_directory
):
raise ImproperOutputException(job.rule, [f])
raise ImproperOutputException(job, [f])

# It is possible, due to archive expansion or cluster clock skew, that
# the files appear older than the input. But we know they must be new,
Expand Down Expand Up @@ -921,7 +921,7 @@ def update_(

if missing_input:
self.delete_job(job, recursive=False) # delete job from tree
raise MissingInputException(job.rule, missing_input)
raise MissingInputException(job, missing_input)

if skip_until_dynamic:
self._dynamic.add(job)
Expand Down
47 changes: 20 additions & 27 deletions snakemake/exceptions.py
Expand Up @@ -245,18 +245,23 @@ def __init__(


class IOException(RuleException):
def __init__(self, prefix, rule, files, include=None, lineno=None, snakefile=None):
message = (
"{} for rule {}:\n{}".format(prefix, rule, "\n".join(files))
if files
else ""
)
def __init__(self, prefix, job, files, include=None, lineno=None, snakefile=None):
from snakemake.logging import format_wildcards

msg = ""
if files:
msg = f"{prefix} for rule {job.rule}:"
if job.output:
msg += "\n" + f" output: {', '.join(job.output)}"
if job.wildcards:
msg += "\n" + f" wildcards: {format_wildcards(job.wildcards)}"
msg += "\n affected files:\n " + "\n ".join(files)
super().__init__(
message=message,
message=msg,
include=include,
lineno=lineno,
snakefile=snakefile,
rule=rule,
rule=job.rule,
)


Expand All @@ -277,27 +282,28 @@ def __init__(


class MissingInputException(IOException):
def __init__(self, rule, files, include=None, lineno=None, snakefile=None):
def __init__(self, job, files, include=None, lineno=None, snakefile=None):
msg = "Missing input files"

if any(map(lambda f: f.startswith("~"), files)):
msg += (
"(Using '~' in your paths is not allowed as such platform "
"specific syntax is not resolved by Snakemake. In general, "
"try sticking to relative paths for everything inside the "
"working directory.)"
)
super().__init__(msg, rule, files, include, lineno=lineno, snakefile=snakefile)
super().__init__(msg, job, files, include, lineno=lineno, snakefile=snakefile)


class PeriodicWildcardError(RuleException):
pass


class ProtectedOutputException(IOException):
def __init__(self, rule, files, include=None, lineno=None, snakefile=None):
def __init__(self, job, files, include=None, lineno=None, snakefile=None):
super().__init__(
"Write-protected output files",
rule,
job,
files,
include,
lineno=lineno,
Expand All @@ -306,24 +312,11 @@ def __init__(self, rule, files, include=None, lineno=None, snakefile=None):


class ImproperOutputException(IOException):
def __init__(self, rule, files, include=None, lineno=None, snakefile=None):
def __init__(self, job, files, include=None, lineno=None, snakefile=None):
super().__init__(
"Outputs of incorrect type (directories when expecting files or vice versa). "
"Output directories must be flagged with directory().",
rule,
files,
include,
lineno=lineno,
snakefile=snakefile,
)


class UnexpectedOutputException(IOException):
def __init__(self, rule, files, include=None, lineno=None, snakefile=None):
super().__init__(
"Unexpectedly present output files "
"(accidentally created by other rule?)",
rule,
job,
files,
include,
lineno=lineno,
Expand Down
2 changes: 1 addition & 1 deletion snakemake/jobs.py
Expand Up @@ -703,7 +703,7 @@ def existing_output(self):
def check_protected_output(self):
protected = list(filter(lambda f: f.protected, self.expanded_output))
if protected:
raise ProtectedOutputException(self.rule, protected)
raise ProtectedOutputException(self, protected)

def remove_existing_output(self):
"""Clean up both dynamic and regular output before rules actually run"""
Expand Down

0 comments on commit 8d64af2

Please sign in to comment.