diff --git a/docs/api/_src/pipeline_lint_tests/system_exit.md b/docs/api/_src/pipeline_lint_tests/system_exit.md new file mode 100644 index 0000000000..3d0ac20f8d --- /dev/null +++ b/docs/api/_src/pipeline_lint_tests/system_exit.md @@ -0,0 +1,5 @@ +# system_exit + +```{eval-rst} +.. automethod:: nf_core.lint.PipelineLint.system_exit +``` diff --git a/nf_core/lint/__init__.py b/nf_core/lint/__init__.py index e014a933ea..a998c964a0 100644 --- a/nf_core/lint/__init__.py +++ b/nf_core/lint/__init__.py @@ -181,6 +181,7 @@ class PipelineLint(nf_core.utils.Pipeline): from .schema_description import schema_description from .schema_lint import schema_lint from .schema_params import schema_params + from .system_exit import system_exit from .template_strings import template_strings from .version_consistency import version_consistency @@ -223,6 +224,7 @@ def _get_all_lint_tests(release_mode): "template_strings", "schema_lint", "schema_params", + "system_exit", "schema_description", "actions_schema_validation", "merge_markers", diff --git a/nf_core/lint/system_exit.py b/nf_core/lint/system_exit.py new file mode 100644 index 0000000000..56a526d97b --- /dev/null +++ b/nf_core/lint/system_exit.py @@ -0,0 +1,37 @@ +import logging +from pathlib import Path + +log = logging.getLogger(__name__) + + +def system_exit(self): + """Check for System.exit calls in groovy/nextflow code + + Calls to System.exit(1) should be replaced by throwing errors + + This lint test looks for all calls to `System.exit` + in any file with the `.nf` or `.groovy` extension + """ + passed = [] + warned = [] + + root_dir = Path(self.wf_path) + + # Get all groovy and nf files + groovy_files = [f for f in root_dir.rglob("*.groovy")] + nf_files = [f for f in root_dir.rglob("*.nf")] + to_check = nf_files + groovy_files + + for file in to_check: + try: + with file.open() as fh: + for i, l in enumerate(fh.readlines(), start=1): + if "System.exit" in l and not "System.exit(0)" in l: + warned.append(f"`System.exit` in {file.name}: _{l.strip()}_ [line {i}]") + except FileNotFoundError: + log.debug(f"Could not open file {file.name} in system_exit lint test") + + if len(warned) == 0: + passed.append("No `System.exit` calls found") + + return {"passed": passed, "warned": warned} diff --git a/nf_core/pipeline-template/lib/NfcoreSchema.groovy b/nf_core/pipeline-template/lib/NfcoreSchema.groovy index 33cd4f6e8d..4d29681431 100755 --- a/nf_core/pipeline-template/lib/NfcoreSchema.groovy +++ b/nf_core/pipeline-template/lib/NfcoreSchema.groovy @@ -2,6 +2,7 @@ // This file holds several functions used to perform JSON parameter validation, help and summary rendering for the nf-core pipeline template. // +import nextflow.Nextflow import org.everit.json.schema.Schema import org.everit.json.schema.loader.SchemaLoader import org.everit.json.schema.ValidationException @@ -177,7 +178,7 @@ class NfcoreSchema { } if (has_error) { - System.exit(1) + Nextflow.error('Exiting!') } } diff --git a/nf_core/pipeline-template/lib/WorkflowMain.groovy b/nf_core/pipeline-template/lib/WorkflowMain.groovy index 05db418b2d..2024e95c79 100755 --- a/nf_core/pipeline-template/lib/WorkflowMain.groovy +++ b/nf_core/pipeline-template/lib/WorkflowMain.groovy @@ -2,6 +2,8 @@ // This file holds several functions specific to the main.nf workflow in the {{ name }} pipeline // +import nextflow.Nextflow + class WorkflowMain { // @@ -85,8 +87,7 @@ class WorkflowMain { // Check input has been provided if (!params.input) { - log.error "Please provide an input samplesheet to the pipeline e.g. '--input samplesheet.csv'" - System.exit(1) + Nextflow.error("Please provide an input samplesheet to the pipeline e.g. '--input samplesheet.csv'") } } {% if igenomes -%} diff --git a/nf_core/pipeline-template/lib/WorkflowPipeline.groovy b/nf_core/pipeline-template/lib/WorkflowPipeline.groovy index 252f127d80..600a655439 100755 --- a/nf_core/pipeline-template/lib/WorkflowPipeline.groovy +++ b/nf_core/pipeline-template/lib/WorkflowPipeline.groovy @@ -2,6 +2,7 @@ // This file holds several functions specific to the workflow/{{ short_name }}.nf in the {{ name }} pipeline // +import nextflow.Nextflow import groovy.text.SimpleTemplateEngine class Workflow{{ short_name[0]|upper }}{{ short_name[1:] }} { @@ -15,8 +16,7 @@ class Workflow{{ short_name[0]|upper }}{{ short_name[1:] }} { {% endif %} if (!params.fasta) { - log.error "Genome fasta file not specified with e.g. '--fasta genome.fa' or via a detectable config file." - System.exit(1) + Nextflow.error "Genome fasta file not specified with e.g. '--fasta genome.fa' or via a detectable config file." } } @@ -70,12 +70,12 @@ class Workflow{{ short_name[0]|upper }}{{ short_name[1:] }} { // private static void genomeExistsError(params, log) { if (params.genomes && params.genome && !params.genomes.containsKey(params.genome)) { - log.error "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + + def error_string = "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + " Genome '${params.genome}' not found in any config files provided to the pipeline.\n" + " Currently, the available genome keys are:\n" + " ${params.genomes.keySet().join(", ")}\n" + "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" - System.exit(1) + Nextflow.error(error_string) } } {% endif -%}}