Skip to content

Commit

Permalink
fix: various error handling improvements, fixed logging/error behavio…
Browse files Browse the repository at this point in the history
…r (stdout from dryrun, stderr otherwise) (#2759)

### Description

<!--Add a description of your PR here-->

### QC
<!-- Make sure that you can tick the boxes below. -->

* [x] The PR contains a test case for the changes or the changes are
already covered by an existing test case.
* [x] The documentation (`docs/`) is updated to reflect the changes or
this is not necessary (e.g. if the change does neither modify the
language nor the behavior or functionalities of Snakemake).
  • Loading branch information
johanneskoester committed Mar 15, 2024
1 parent 05ee812 commit d0d1f48
Show file tree
Hide file tree
Showing 7 changed files with 16 additions and 16 deletions.
10 changes: 5 additions & 5 deletions snakemake/api.py
Expand Up @@ -138,7 +138,7 @@ def workflow(

self._check_is_in_context()

self._setup_logger(mode=workflow_settings.exec_mode)
self.setup_logger(mode=workflow_settings.exec_mode)

self._check_default_storage_provider(storage_settings=storage_settings)

Expand Down Expand Up @@ -240,7 +240,7 @@ def print_exception(self, ex: Exception):
linemaps = self._workflow_api._workflow_store.linemaps
print_exception(ex, linemaps)

def _setup_logger(
def setup_logger(
self,
stdout: bool = False,
mode: ExecMode = ExecMode.DEFAULT,
Expand All @@ -254,7 +254,7 @@ def _setup_logger(
debug=self.output_settings.verbose,
printshellcmds=self.output_settings.printshellcmds,
debug_dag=self.output_settings.debug_dag,
stdout=stdout,
stdout=stdout or self.output_settings.stdout,
mode=mode,
show_failed_logs=self.output_settings.show_failed_logs,
dryrun=dryrun,
Expand Down Expand Up @@ -382,11 +382,11 @@ def print_compilation(self):
def _workflow(self):
if self._workflow_store is None:
workflow = self._get_workflow()
self._workflow_store = workflow
workflow.include(
self.snakefile, overwrite_default_target=True, print_compilation=False
)
workflow.check()
self._workflow_store = workflow
return self._workflow_store

def _get_workflow(self, **kwargs):
Expand Down Expand Up @@ -523,7 +523,7 @@ def execute_workflow(
"For local execution, --shared-fs-usage has to be unrestricted."
)

self.snakemake_api._setup_logger(
self.snakemake_api.setup_logger(
stdout=executor_plugin.common_settings.dryrun_exec,
mode=self.workflow_api.workflow_settings.exec_mode,
dryrun=executor_plugin.common_settings.dryrun_exec,
Expand Down
1 change: 1 addition & 0 deletions snakemake/cli.py
Expand Up @@ -1885,6 +1885,7 @@ def args_to_api(args, parser):
show_failed_logs=args.show_failed_logs,
log_handlers=log_handlers,
keep_logger=False,
stdout=args.dryrun,
)
) as snakemake_api:
deployment_method = args.software_deployment_method
Expand Down
8 changes: 5 additions & 3 deletions snakemake/exceptions.py
Expand Up @@ -18,8 +18,10 @@ def format_error(
msg = str(ex)
if linemaps and snakefile and snakefile in linemaps:
lineno = linemaps[snakefile][lineno]
if isinstance(ex, SyntaxError):
msg = ex.msg

if isinstance(ex, SyntaxError):
msg = ex.msg.split("(")[0]
msg = f"{msg}:\n{ex.text}"

location = ""
if lineno and snakefile:
Expand Down Expand Up @@ -157,7 +159,7 @@ def print_exception(ex, linemaps=None):
elif isinstance(ex, KeyboardInterrupt):
logger.info("Cancelling snakemake on user request.")
else:
traceback.print_exception(type(ex), ex, ex.__traceback__)
logger.error(traceback.format_exeption(ex))


def update_lineno(ex: SyntaxError, linemaps):
Expand Down
5 changes: 3 additions & 2 deletions snakemake/report/__init__.py
Expand Up @@ -271,9 +271,8 @@ def __post_init__(self, job, job_rec):
self.conda_env = yaml.load(self._conda_env_raw, Loader=yaml.Loader)
self.n_jobs = 1
self.id = uuid.uuid4()
self._init_source()

def _init_source(self):
def init_source(self):
sources, language = None, None
if self._rule.shellcmd is not None:
sources = [self._rule.shellcmd]
Expand Down Expand Up @@ -632,6 +631,7 @@ def register_file(
rule = RuleRecord(job_rec.job, job_rec)
if job_rec.rule not in rules:
rules[job_rec.rule].append(rule)
rule.init_source()
else:
merged = False
for other in rules[job_rec.rule]:
Expand All @@ -641,6 +641,7 @@ def register_file(
break
if not merged:
rules[job_rec.rule].append(rule)
rule.init_source()
# In theory there could be more than one rule with the same name kept from above.
# For now, we just keep the first.
rules = {rulename: items[0] for rulename, items in rules.items()}
Expand Down
1 change: 1 addition & 0 deletions snakemake/settings.py
Expand Up @@ -340,6 +340,7 @@ class OutputSettings(SettingsBase):
show_failed_logs: bool = False
log_handlers: Sequence[object] = tuple()
keep_logger: bool = False
stdout: bool = False


@dataclass
Expand Down
6 changes: 1 addition & 5 deletions snakemake/workflow.py
Expand Up @@ -1395,11 +1395,7 @@ def include(
# this allows to import modules from the workflow directory
sys.path.insert(0, snakefile.get_basedir().get_path_or_uri())

try:
exec(compile(code, snakefile.get_path_or_uri(), "exec"), self.globals)
except SyntaxError as e:
e = update_lineno(e, self.linemaps)
raise
exec(compile(code, snakefile.get_path_or_uri(), "exec"), self.globals)

if not overwrite_default_target:
self.default_target = default_target
Expand Down
1 change: 0 additions & 1 deletion tests/test01/Snakefile
@@ -1,4 +1,3 @@

from snakemake.utils import min_version

from snakemake import __version__
Expand Down

0 comments on commit d0d1f48

Please sign in to comment.