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

allow mixed str and dict in lint config #3228

Open
wants to merge 17 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

### Linting

- allow mixed str and dict in lint config ([#3228](https://github.com/nf-core/tools/pull/3228))

### Modules

### Subworkflows
Expand Down
4 changes: 2 additions & 2 deletions nf_core/components/lint/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from nf_core.components.nfcore_component import NFCoreComponent
from nf_core.modules.modules_json import ModulesJson
from nf_core.pipelines.lint_utils import console
from nf_core.utils import LintConfigType
from nf_core.utils import NFCoreYamlLintConfig
from nf_core.utils import plural_s as _s

log = logging.getLogger(__name__)
Expand Down Expand Up @@ -80,7 +80,7 @@ def __init__(
self.failed: List[LintResult] = []
self.all_local_components: List[NFCoreComponent] = []

self.lint_config: Optional[LintConfigType] = None
self.lint_config: Optional[NFCoreYamlLintConfig] = None
self.modules_json: Optional[ModulesJson] = None

if self.component_type == "modules":
Expand Down
16 changes: 8 additions & 8 deletions nf_core/pipelines/create/create.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import re
import shutil
from pathlib import Path
from typing import Dict, List, Optional, Tuple, Union, cast
from typing import Dict, List, Optional, Tuple, Union

import git
import git.config
Expand All @@ -21,7 +21,7 @@
from nf_core.pipelines.create.utils import CreateConfig, features_yml_path, load_features_yaml
from nf_core.pipelines.create_logo import create_logo
from nf_core.pipelines.lint_utils import run_prettier_on_file
from nf_core.utils import LintConfigType, NFCoreTemplateConfig
from nf_core.utils import NFCoreTemplateConfig, NFCoreYamlLintConfig

log = logging.getLogger(__name__)

Expand Down Expand Up @@ -67,7 +67,7 @@ def __init__(
_, config_yml = nf_core.utils.load_tools_config(outdir if outdir else Path().cwd())
# Obtain a CreateConfig object from `.nf-core.yml` config file
if config_yml is not None and getattr(config_yml, "template", None) is not None:
self.config = CreateConfig(**config_yml["template"].model_dump())
self.config = CreateConfig(**config_yml["template"].model_dump(exclude_none=True))
else:
raise UserWarning("The template configuration was not provided in '.nf-core.yml'.")
# Update the output directory
Expand Down Expand Up @@ -205,7 +205,7 @@ def obtain_jinja_params_dict(
config_yml = None

# Set the parameters for the jinja template
jinja_params = self.config.model_dump()
jinja_params = self.config.model_dump(exclude_none=True)

# Add template areas to jinja params and create list of areas with paths to skip
skip_areas = []
Expand Down Expand Up @@ -363,8 +363,8 @@ def render_template(self) -> None:
config_fn, config_yml = nf_core.utils.load_tools_config(self.outdir)
if config_fn is not None and config_yml is not None:
with open(str(config_fn), "w") as fh:
config_yml.template = NFCoreTemplateConfig(**self.config.model_dump())
yaml.safe_dump(config_yml.model_dump(), fh)
config_yml.template = NFCoreTemplateConfig(**self.config.model_dump(exclude_none=True))
yaml.safe_dump(config_yml.model_dump(exclude_none=True), fh)
log.debug(f"Dumping pipeline template yml to pipeline config file '{config_fn.name}'")

# Run prettier on files
Expand Down Expand Up @@ -395,9 +395,9 @@ def fix_linting(self):
# Add the lint content to the preexisting nf-core config
config_fn, nf_core_yml = nf_core.utils.load_tools_config(self.outdir)
if config_fn is not None and nf_core_yml is not None:
nf_core_yml.lint = cast(LintConfigType, lint_config)
nf_core_yml.lint = NFCoreYamlLintConfig(**lint_config)
with open(self.outdir / config_fn, "w") as fh:
yaml.dump(nf_core_yml.model_dump(), fh, default_flow_style=False, sort_keys=False)
yaml.dump(nf_core_yml.model_dump(exclude_none=True), fh, default_flow_style=False, sort_keys=False)

def make_pipeline_logo(self):
"""Fetch a logo for the new pipeline from the nf-core website"""
Expand Down
14 changes: 6 additions & 8 deletions nf_core/pipelines/lint/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
from nf_core import __version__
from nf_core.components.lint import ComponentLint
from nf_core.pipelines.lint_utils import console
from nf_core.utils import NFCoreYamlConfig, NFCoreYamlLintConfig, strip_ansi_codes
from nf_core.utils import plural_s as _s
from nf_core.utils import strip_ansi_codes

from .actions_awsfulltest import actions_awsfulltest
from .actions_awstest import actions_awstest
Expand Down Expand Up @@ -112,7 +112,7 @@ def __init__(
# Initialise the parent object
super().__init__(wf_path)

self.lint_config = {}
self.lint_config: Optional[NFCoreYamlLintConfig] = None
self.release_mode = release_mode
self.fail_ignored = fail_ignored
self.fail_warned = fail_warned
Expand Down Expand Up @@ -173,13 +173,12 @@ def _load_lint_config(self) -> bool:
Add parsed config to the `self.lint_config` class attribute.
"""
_, tools_config = nf_core.utils.load_tools_config(self.wf_path)
self.lint_config = getattr(tools_config, "lint", {}) or {}
self.lint_config = getattr(tools_config, "lint", None) or None
is_correct = True

# Check if we have any keys that don't match lint test names
if self.lint_config is not None:
for k in self.lint_config:
if k != "nfcore_components" and k not in self.lint_tests:
for k, v in self.lint_config:
if v is not None and k != "nfcore_components" and k not in self.lint_tests:
# nfcore_components is an exception to allow custom pipelines without nf-core components
log.warning(f"Found unrecognised test name '{k}' in pipeline lint config")
is_correct = False
Expand Down Expand Up @@ -594,7 +593,7 @@ def run_linting(
lint_obj._load_lint_config()
lint_obj.load_pipeline_config()

if "nfcore_components" in lint_obj.lint_config and not lint_obj.lint_config["nfcore_components"]:
if lint_obj.lint_config and lint_obj.lint_config["nfcore_components"] is False:
module_lint_obj = None
subworkflow_lint_obj = None
else:
Expand Down Expand Up @@ -679,5 +678,4 @@ def run_linting(
if len(lint_obj.failed) > 0:
if release_mode:
log.info("Reminder: Lint tests were run in --release mode.")

return lint_obj, module_lint_obj, subworkflow_lint_obj
9 changes: 9 additions & 0 deletions nf_core/pipelines/lint/multiqc_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,15 @@ def multiqc_config(self) -> Dict[str, List[str]]:
lint:
multiqc_config: False

To disable this test only for specific sections, you can specify a list of section names.
For example:

.. code-block:: yaml
lint:
multiqc_config:
- report_section_order
- report_comment

"""

passed: List[str] = []
Expand Down
31 changes: 16 additions & 15 deletions nf_core/pipelines/lint/nfcore_yml.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import re
from pathlib import Path
from typing import Dict, List

from ruamel.yaml import YAML

from nf_core import __version__

REPOSITORY_TYPES = ["pipeline", "modules"]
Expand All @@ -26,21 +27,23 @@ def nfcore_yml(self) -> Dict[str, List[str]]:
failed: List[str] = []
ignored: List[str] = []

yaml = YAML()

# Remove field that should be ignored according to the linting config
ignore_configs = self.lint_config.get(".nf-core", []) if self.lint_config is not None else []
try:
with open(Path(self.wf_path, ".nf-core.yml")) as fh:
content = fh.read()
except FileNotFoundError:
with open(Path(self.wf_path, ".nf-core.yaml")) as fh:
content = fh.read()
for ext in (".yml", ".yaml"):
try:
nf_core_yml = yaml.load(Path(self.wf_path) / f".nf-core{ext}")
break
except FileNotFoundError:
continue
else:
raise FileNotFoundError("No `.nf-core.yml` file found.")

if "repository_type" not in ignore_configs:
# Check that the repository type is set in the .nf-core.yml
repo_type_re = r"repository_type: (.+)"
match = re.search(repo_type_re, content)
if match:
repo_type = match.group(1)
if "repository_type" in nf_core_yml:
repo_type = nf_core_yml["repository_type"]
if repo_type not in REPOSITORY_TYPES:
failed.append(
f"Repository type in `.nf-core.yml` is not valid. "
Expand All @@ -55,10 +58,8 @@ def nfcore_yml(self) -> Dict[str, List[str]]:

if "nf_core_version" not in ignore_configs:
# Check that the nf-core version is set in the .nf-core.yml
nf_core_version_re = r"nf_core_version: (.+)"
match = re.search(nf_core_version_re, content)
if match:
nf_core_version = match.group(1).strip('"')
if "nf_core_version" in nf_core_yml:
nf_core_version = nf_core_yml["nf_core_version"]
if nf_core_version != __version__ and "dev" not in nf_core_version:
warned.append(
f"nf-core version in `.nf-core.yml` is not set to the latest version. "
Expand Down
15 changes: 15 additions & 0 deletions nf_core/pipelines/lint/readme.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,21 @@ def readme(self):

* If pipeline is released but still contains a 'zenodo.XXXXXXX' tag, the test fails

To disable this test, add the following to the pipeline's ``.nf-core.yml`` file:

.. code-block:: yaml
lint:
readme: False

To disable subsets of these tests, add the following to the pipeline's ``.nf-core.yml`` file:

.. code-block:: yaml

lint:
readme:
nextflow_badge
zenodo_release

"""
passed = []
warned = []
Expand Down
2 changes: 1 addition & 1 deletion nf_core/pipelines/lint/template_strings.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ def template_strings(self):
ignored = []
# Files that should be ignored according to the linting config
ignore_files = self.lint_config.get("template_strings", []) if self.lint_config is not None else []
files = self.list_files()

files = self.list_files()
# Loop through files, searching for string
num_matches = 0
for fn in files:
Expand Down
6 changes: 3 additions & 3 deletions nf_core/pipelines/sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ def __init__(
with open(template_yaml_path) as f:
self.config_yml.template = yaml.safe_load(f)
with open(self.config_yml_path, "w") as fh:
yaml.safe_dump(self.config_yml.model_dump(), fh)
yaml.safe_dump(self.config_yml.model_dump(exclude_none=True), fh)
log.info(f"Saved pipeline creation settings to '{self.config_yml_path}'")
raise SystemExit(
f"Please commit your changes and delete the {template_yaml_path} file. Then run the sync command again."
Expand Down Expand Up @@ -271,7 +271,7 @@ def make_template_pipeline(self):

self.config_yml.template.force = True
with open(self.config_yml_path, "w") as config_path:
yaml.safe_dump(self.config_yml.model_dump(), config_path)
yaml.safe_dump(self.config_yml.model_dump(exclude_none=True), config_path)

try:
pipeline_create_obj = nf_core.pipelines.create.create.PipelineCreate(
Expand All @@ -291,7 +291,7 @@ def make_template_pipeline(self):
self.config_yml.template.outdir = "."
# Update nf-core version
self.config_yml.nf_core_version = nf_core.__version__
dump_yaml_with_prettier(self.config_yml_path, self.config_yml.model_dump())
dump_yaml_with_prettier(self.config_yml_path, self.config_yml.model_dump(exclude_none=True))

except Exception as err:
# Reset to where you were to prevent git getting messed up.
Expand Down
Loading
Loading