Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: more details on input and output exceptions (missing input, protected output, etc.) #1453

Merged
merged 1 commit into from Mar 3, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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