Skip to content

Commit 68364ff

Browse files
committed
handle dicts during dependency sorting correctly
1 parent b28be77 commit 68364ff

File tree

3 files changed

+88
-3
lines changed

3 files changed

+88
-3
lines changed

nf_core/modules/lint/environment_yml.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,28 @@ def environment_yml(module_lint_object: ComponentLint, module: NFCoreComponent,
8383

8484
if valid_env_yml:
8585
# Check that the dependencies section is sorted alphabetically
86-
if sorted(env_yml["dependencies"]) == env_yml["dependencies"]:
86+
def sort_recursively(obj):
87+
"""Simple recursive sort for nested structures."""
88+
if isinstance(obj, list):
89+
90+
def get_key(x):
91+
if isinstance(x, dict):
92+
# For dicts like {"pip": [...]}, use the key "pip"
93+
return (list(x.keys())[0], 1)
94+
else:
95+
# For strings like "pip=23.3.1", use "pip" and for bioconda::samtools=1.15.1, use "bioconda::samtools"
96+
return (str(x).split("=")[0], 0)
97+
98+
return sorted([sort_recursively(item) for item in obj], key=get_key)
99+
elif isinstance(obj, dict):
100+
return {k: sort_recursively(v) for k, v in obj.items()}
101+
else:
102+
return obj
103+
104+
sorted_dependencies = sort_recursively(env_yml["dependencies"])
105+
106+
# Direct comparison of sorted vs original dependencies
107+
if sorted_dependencies == env_yml["dependencies"]:
87108
module.passed.append(
88109
(
89110
"environment_yml_sorted",
@@ -96,6 +117,6 @@ def environment_yml(module_lint_object: ComponentLint, module: NFCoreComponent,
96117
log.info(
97118
f"Dependencies in {module.component_name}'s environment.yml were not sorted alphabetically. Sorting them now."
98119
)
99-
env_yml["dependencies"].sort()
120+
env_yml["dependencies"] = sorted_dependencies
100121
with open(Path(module.component_dir, "environment.yml"), "w") as fh:
101122
yaml.dump(env_yml, fh, Dumper=custom_yaml_dumper())

requirements.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ jsonschema>=4.0
77
markdown>=3.3
88
packaging
99
pillow
10-
pdiff
1110
pre-commit
1211
prompt_toolkit<=3.0.51
1312
pydantic>=2.2.1

tests/modules/test_lint.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,71 @@ def test_modules_environment_yml_file_not_array(self):
505505
assert len(module_lint.warned) >= 0
506506
assert module_lint.failed[0].lint_test == "environment_yml_valid"
507507

508+
def test_modules_environment_yml_file_mixed_dependencies(self):
509+
"""Test linting a module with mixed-type dependencies (strings and pip dict)"""
510+
with open(
511+
Path(
512+
self.nfcore_modules,
513+
"modules",
514+
"nf-core",
515+
"bpipe",
516+
"test",
517+
"environment.yml",
518+
)
519+
) as fh:
520+
yaml_content = yaml.safe_load(fh)
521+
522+
# Create mixed dependencies with strings and pip dict in wrong order
523+
yaml_content["dependencies"] = [
524+
"python=3.8",
525+
{"pip": ["zzz-package==1.0.0", "aaa-package==2.0.0"]},
526+
"bioconda::samtools=1.15.1",
527+
"bioconda::fastqc=0.12.1",
528+
"pip=23.3.1",
529+
]
530+
531+
with open(
532+
Path(
533+
self.nfcore_modules,
534+
"modules",
535+
"nf-core",
536+
"bpipe",
537+
"test",
538+
"environment.yml",
539+
),
540+
"w",
541+
) as fh:
542+
fh.write(yaml.dump(yaml_content))
543+
544+
module_lint = nf_core.modules.lint.ModuleLint(directory=self.nfcore_modules)
545+
module_lint.lint(print_results=False, module="bpipe/test")
546+
547+
# Check that the dependencies were sorted correctly
548+
with open(
549+
Path(
550+
self.nfcore_modules,
551+
"modules",
552+
"nf-core",
553+
"bpipe",
554+
"test",
555+
"environment.yml",
556+
)
557+
) as fh:
558+
sorted_yaml = yaml.safe_load(fh)
559+
560+
expected_deps = [
561+
"bioconda::fastqc=0.12.1",
562+
"bioconda::samtools=1.15.1",
563+
"pip=23.3.1",
564+
{"pip": ["aaa-package==2.0.0", "zzz-package==1.0.0"]},
565+
"python=3.8",
566+
]
567+
568+
assert sorted_yaml["dependencies"] == expected_deps
569+
assert len(module_lint.failed) == 0, f"Linting failed with {[x.__dict__ for x in module_lint.failed]}"
570+
assert len(module_lint.passed) > 0
571+
assert len(module_lint.warned) >= 0
572+
508573
def test_modules_meta_yml_incorrect_licence_field(self):
509574
"""Test linting a module with an incorrect Licence field in meta.yml"""
510575
with open(Path(self.nfcore_modules, "modules", "nf-core", "bpipe", "test", "meta.yml")) as fh:

0 commit comments

Comments
 (0)