Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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 @@ -21,6 +21,8 @@

### Modules

- sort meta.yml based on keys in schema ([#3958](https://github.com/nf-core/tools/pull/3958))

### Subworkflows

- Update to new topic version handling ([#3929](https://github.com/nf-core/tools/pull/3929))
Expand Down
73 changes: 46 additions & 27 deletions nf_core/modules/lint/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
import logging
import os
import re
from collections.abc import Mapping
from pathlib import Path
from typing import Any

import questionary
import rich
Expand Down Expand Up @@ -79,6 +81,7 @@ def __init__(
registry=registry,
hide_progress=hide_progress,
)
self.meta_schema: Mapping[str, Any] | None = None

def lint(
self,
Expand Down Expand Up @@ -287,6 +290,30 @@ def lint_module(

self.failed += [LintResult(mod, *m) for m in mod.failed]

def load_meta_schema(self) -> Mapping[str, Any]:
"""
Load the meta.yml JSON schema from the local modules repository cache.
The schema is cached in self.meta_schema to avoid reloading.

Returns:
dict: The meta.yml JSON schema

Raises:
LookupError: If the local module cache is not found
"""
# Return cached schema if already loaded
if self.meta_schema is not None:
return self.meta_schema

if self.modules_repo.local_repo_dir is None:
raise LookupError("Local module cache not found")

import json

with open(Path(self.modules_repo.local_repo_dir, "modules/meta-schema.json")) as fh:
self.meta_schema = json.load(fh)
return self.meta_schema

def update_meta_yml_file(self, mod):
"""
Update the meta.yml file with the correct inputs and outputs
Expand Down Expand Up @@ -324,33 +351,25 @@ def _find_meta_info(meta_yml: dict, element_name: str, is_output=False) -> dict:
return {}

def _sort_meta_yml(meta_yml: dict) -> dict:
"""Ensure topics comes after input/output and before authors"""
# Early return if no topics to reorder
if "topics" not in meta_yml:
return meta_yml

result = {}
topics_value = meta_yml["topics"]
topics_added = False

for key, value in meta_yml.items():
if key == "topics":
continue # Skip topics, we'll add it in the right place

# Add topics before authors key (if not already added)
if key == "authors" and not topics_added:
result["topics"] = topics_value
topics_added = True

result[key] = value

# Add topics after output (preferred) or after input (if no output)
if key == "output":
result["topics"] = topics_value
topics_added = True
elif key == "input" and "output" not in meta_yml:
result["topics"] = topics_value
topics_added = True
"""Sort meta.yml keys according to the schema's property order"""
# Get the schema to determine the correct key order
try:
schema = self.load_meta_schema()
schema_keys = list(schema["properties"].keys())
except (LintExceptionError, KeyError) as e:
raise UserWarning("Failed to load meta schema", e)

result: dict = {}

# First, add keys in the order they appear in the schema
for key in schema_keys:
if key in meta_yml:
result[key] = meta_yml[key]

# Then add any keys that aren't in the schema (to preserve custom keys)
for key in meta_yml.keys():
if key not in result:
result[key] = meta_yml[key]

return result

Expand Down
12 changes: 8 additions & 4 deletions nf_core/modules/lint/meta_yml.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import json
from __future__ import annotations

import logging
from pathlib import Path
from typing import TYPE_CHECKING

import ruamel.yaml
from jsonschema import exceptions, validators
Expand All @@ -9,10 +11,13 @@
from nf_core.components.lint import ComponentLint, LintExceptionError
from nf_core.components.nfcore_component import NFCoreComponent

if TYPE_CHECKING:
from nf_core.modules.lint import ModuleLint

log = logging.getLogger(__name__)


def meta_yml(module_lint_object: ComponentLint, module: NFCoreComponent, allow_missing: bool = False) -> None:
def meta_yml(module_lint_object: ModuleLint, module: NFCoreComponent, allow_missing: bool = False) -> None:
"""
Lint a ``meta.yml`` file

Expand Down Expand Up @@ -76,8 +81,7 @@ def meta_yml(module_lint_object: ComponentLint, module: NFCoreComponent, allow_m
# Confirm that the meta.yml file is valid according to the JSON schema
valid_meta_yml = False
try:
with open(Path(module_lint_object.modules_repo.local_repo_dir, "modules/meta-schema.json")) as fh:
schema = json.load(fh)
schema = module_lint_object.load_meta_schema()
validators.validate(instance=meta_yaml, schema=schema)
module.passed.append(("meta_yml", "meta_yml_valid", "Module `meta.yml` is valid", module.meta_yml))
valid_meta_yml = True
Expand Down
Loading