diff --git a/snakemake/api.py b/snakemake/api.py index ef96bdd0e..6ba3e2bd4 100644 --- a/snakemake/api.py +++ b/snakemake/api.py @@ -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) @@ -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, @@ -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, @@ -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): @@ -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, diff --git a/snakemake/cli.py b/snakemake/cli.py index 3defcf787..ffc40ed61 100644 --- a/snakemake/cli.py +++ b/snakemake/cli.py @@ -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 diff --git a/snakemake/exceptions.py b/snakemake/exceptions.py index a2664905f..7a810106b 100644 --- a/snakemake/exceptions.py +++ b/snakemake/exceptions.py @@ -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: @@ -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): diff --git a/snakemake/report/__init__.py b/snakemake/report/__init__.py index e0e022259..ae394bf43 100644 --- a/snakemake/report/__init__.py +++ b/snakemake/report/__init__.py @@ -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] @@ -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]: @@ -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()} diff --git a/snakemake/settings.py b/snakemake/settings.py index 1e993174a..bf3102532 100644 --- a/snakemake/settings.py +++ b/snakemake/settings.py @@ -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 diff --git a/snakemake/workflow.py b/snakemake/workflow.py index 68a509948..5fec696e1 100644 --- a/snakemake/workflow.py +++ b/snakemake/workflow.py @@ -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 diff --git a/tests/test01/Snakefile b/tests/test01/Snakefile index e09780a69..e00788aed 100644 --- a/tests/test01/Snakefile +++ b/tests/test01/Snakefile @@ -1,4 +1,3 @@ - from snakemake.utils import min_version from snakemake import __version__