nf-core / tools

Python package with helper tools for the nf-core community.
https://nf-co.re
MIT License
241 stars 190 forks source link

nf-core lint stack trace error list index out of range #3000

Closed mahesh-panchal closed 3 months ago

mahesh-panchal commented 5 months ago

Description of the bug

After doing a template sync and merge, I ran nf-core lint. It tried to regenerate the modules.json, and then threw an error.

Command used and terminal output

$ nf-core lint

                                          ,--./,-.
          ___     __   __   __   ___     /,-._.--~\
    |\ | |__  __ /  ` /  \ |__) |__         }  {
    | \| |       \__, \__/ |  \ |___     \`-._,-`-,
                                          `._,._,'

    nf-core/tools version 2.14.1 - https://nf-co.re

INFO     Recomputing commit SHAs for modules which were missing from 'modules.json':                      
         'krona/ktimporttaxonomy', 'krona/ktupdatetaxonomy', 'cutadapt', 'bowtie2/align', 'bowtie2/build',
         'malt/build', 'malt/run', 'maltextract', 'mapdamage2', 'samtools/sort', 'samtools/idxstats',     
         'samtools/faidx', 'samtools/view', 'samtools/index', 'samtools/flagstat', 'samtools/stats',      
         'krakenuniq/preloadedkrakenuniq', 'krakenuniq/build'                                             
INFO     Recomputing commit SHAs for subworkflows which were missing from 'modules.json':                 
         'bam_stats_samtools', 'fastq_align_bowtie2', 'bam_sort_stats_samtools'                           
INFO     Recomputing commit SHAs for subworkflows which were missing from 'modules.json':                 
         'bam_stats_samtools', 'fastq_align_bowtie2', 'bam_sort_stats_samtools'                           
INFO     Testing pipeline: .                                                                              
╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮
│ /opt/conda/bin/nf-core:8 in <module>                                                             │
│                                                                                                  │
│   5 from nf_core.__main__ import run_nf_core                                                     │
│   6 if __name__ == '__main__':                                                                   │
│   7 │   sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])                         │
│ ❱ 8 │   sys.exit(run_nf_core())                                                                  │
│   9                                                                                              │
│                                                                                                  │
│ /opt/conda/lib/python3.12/site-packages/nf_core/__main__.py:150 in run_nf_core                   │
│                                                                                                  │
│    147 │   │   │   log.debug(f"Could not check latest version: {e}")                             │
│    148 │   │   stderr.print("\n")                                                                │
│    149 │   # Launch the click cli                                                                │
│ ❱  150 │   nf_core_cli(auto_envvar_prefix="NFCORE")                                              │
│    151                                                                                           │
│    152                                                                                           │
│    153 @tui()                                                                                    │
│                                                                                                  │
│ /opt/conda/lib/python3.12/site-packages/rich_click/rich_command.py:367 in __call__               │
│                                                                                                  │
│   364 │   │   # Include this here because I run into a false warning                             │
│   365 │   │   # in the PyCharm IDE otherwise; for some reason PyCharm doesn't                    │
│   366 │   │   # seem to think RichGroups are callable. (No issues with Mypy, though.)            │
│ ❱ 367 │   │   return super().__call__(*args, **kwargs)                                           │
│   368                                                                                            │
│   369                                                                                            │
│   370 class RichCommandCollection(CommandCollection, RichGroup):                                 │
│                                                                                                  │
│ /opt/conda/lib/python3.12/site-packages/click/core.py:1157 in __call__                           │
│                                                                                                  │
│   1154 │                                                                                         │
│   1155 │   def __call__(self, *args: t.Any, **kwargs: t.Any) -> t.Any:                           │
│   1156 │   │   """Alias for :meth:`main`."""                                                     │
│ ❱ 1157 │   │   return self.main(*args, **kwargs)                                                 │
│   1158                                                                                           │
│   1159                                                                                           │
│   1160 class Command(BaseCommand):                                                               │
│                                                                                                  │
│ /opt/conda/lib/python3.12/site-packages/rich_click/rich_command.py:152 in main                   │
│                                                                                                  │
│   149 │   │   try:                                                                               │
│   150 │   │   │   try:                                                                           │
│   151 │   │   │   │   with self.make_context(prog_name, args, **extra) as ctx:                   │
│ ❱ 152 │   │   │   │   │   rv = self.invoke(ctx)                                                  │
│   153 │   │   │   │   │   if not standalone_mode:                                                │
│   154 │   │   │   │   │   │   return rv                                                          │
│   155 │   │   │   │   │   # it's not safe to `ctx.exit(rv)` here!                                │
│                                                                                                  │
│ /opt/conda/lib/python3.12/site-packages/click/core.py:1688 in invoke                             │
│                                                                                                  │
│   1685 │   │   │   │   super().invoke(ctx)                                                       │
│   1686 │   │   │   │   sub_ctx = cmd.make_context(cmd_name, args, parent=ctx)                    │
│   1687 │   │   │   │   with sub_ctx:                                                             │
│ ❱ 1688 │   │   │   │   │   return _process_result(sub_ctx.command.invoke(sub_ctx))               │
│   1689 │   │                                                                                     │
│   1690 │   │   # In chain mode we create the contexts step by step, but after the                │
│   1691 │   │   # base command has been invoked.  Because at that point we do not                 │
│                                                                                                  │
│ /opt/conda/lib/python3.12/site-packages/click/core.py:1434 in invoke                             │
│                                                                                                  │
│   1431 │   │   │   echo(style(message, fg="red"), err=True)                                      │
│   1432 │   │                                                                                     │
│   1433 │   │   if self.callback is not None:                                                     │
│ ❱ 1434 │   │   │   return ctx.invoke(self.callback, **ctx.params)                                │
│   1435 │                                                                                         │
│   1436 │   def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]:  │
│   1437 │   │   """Return a list of completions for the incomplete value. Looks                   │
│                                                                                                  │
│ /opt/conda/lib/python3.12/site-packages/click/core.py:783 in invoke                              │
│                                                                                                  │
│    780 │   │                                                                                     │
│    781 │   │   with augment_usage_errors(__self):                                                │
│    782 │   │   │   with ctx:                                                                     │
│ ❱  783 │   │   │   │   return __callback(*args, **kwargs)                                        │
│    784 │                                                                                         │
│    785 │   def forward(                                                                          │
│    786 │   │   __self, __cmd: "Command", *args: t.Any, **kwargs: t.Any  # noqa: B902             │
│                                                                                                  │
│ /opt/conda/lib/python3.12/site-packages/click/decorators.py:33 in new_func                       │
│                                                                                                  │
│    30 │   """                                                                                    │
│    31 │                                                                                          │
│    32 │   def new_func(*args: "P.args", **kwargs: "P.kwargs") -> "R":                            │
│ ❱  33 │   │   return f(get_current_context(), *args, **kwargs)                                   │
│    34 │                                                                                          │
│    35 │   return update_wrapper(new_func, f)                                                     │
│    36                                                                                            │
│                                                                                                  │
│ /opt/conda/lib/python3.12/site-packages/nf_core/__main__.py:635 in lint                          │
│                                                                                                  │
│    632 │                                                                                         │
│    633 │   # Run the lint tests!                                                                 │
│    634 │   try:                                                                                  │
│ ❱  635 │   │   lint_obj, module_lint_obj, subworkflow_lint_obj = run_linting(                    │
│    636 │   │   │   dir,                                                                          │
│    637 │   │   │   release,                                                                      │
│    638 │   │   │   fix,                                                                          │
│                                                                                                  │
│ /opt/conda/lib/python3.12/site-packages/nf_core/lint/__init__.py:611 in run_linting              │
│                                                                                                  │
│   608 │                                                                                          │
│   609 │   # Run the module lint tests                                                            │
│   610 │   if len(module_lint_obj.all_local_components) > 0:                                      │
│ ❱ 611 │   │   module_lint_obj.lint_modules(module_lint_obj.all_local_components, local=True)     │
│   612 │   if len(module_lint_obj.all_remote_components) > 0:                                     │
│   613 │   │   module_lint_obj.lint_modules(module_lint_obj.all_remote_components, local=False)   │
│   614 │   # Run the subworkflows lint tests                                                      │
│                                                                                                  │
│ /opt/conda/lib/python3.12/site-packages/nf_core/modules/lint/__init__.py:185 in lint_modules     │
│                                                                                                  │
│   182 │   │   │                                                                                  │
│   183 │   │   │   for mod in modules:                                                            │
│   184 │   │   │   │   progress_bar.update(lint_progress, advance=1, test_name=mod.component_na   │
│ ❱ 185 │   │   │   │   self.lint_module(mod, progress_bar, registry=registry, local=local, fix_   │
│   186 │                                                                                          │
│   187 │   def lint_module(self, mod, progress_bar, registry, local=False, fix_version=False):    │
│   188 │   │   """                                                                                │
│                                                                                                  │
│ /opt/conda/lib/python3.12/site-packages/nf_core/modules/lint/__init__.py:206 in lint_module      │
│                                                                                                  │
│   203 │   │   # TODO: consider unifying modules and subworkflows lint_module() function and ad   │
│   204 │   │   # Only check the main script in case of a local module                             │
│   205 │   │   if local:                                                                          │
│ ❱ 206 │   │   │   self.main_nf(mod, fix_version, self.registry, progress_bar)                    │
│   207 │   │   │   self.passed += [LintResult(mod, *m) for m in mod.passed]                       │
│   208 │   │   │   warned = [LintResult(mod, *m) for m in (mod.warned + mod.failed)]              │
│   209 │   │   │   if not self.fail_warned:                                                       │
│                                                                                                  │
│ /opt/conda/lib/python3.12/site-packages/nf_core/modules/lint/main_nf.py:107 in main_nf           │
│                                                                                                  │
│   104 │   │   if state == "process" and not _is_empty(line):                                     │
│   105 │   │   │   process_lines.append(line)                                                     │
│   106 │   │   if state == "input" and not _is_empty(line):                                       │
│ ❱ 107 │   │   │   inputs.extend(_parse_input(module, line))                                      │
│   108 │   │   if state == "output" and not _is_empty(line):                                      │
│   109 │   │   │   outputs += _parse_output(module, line)                                         │
│   110 │   │   │   outputs = list(set(outputs))  # remove duplicate 'meta's                       │
│                                                                                                  │
│ /opt/conda/lib/python3.12/site-packages/nf_core/modules/lint/main_nf.py:573 in _parse_input      │
│                                                                                                  │
│   570 │   │   │   if match:                                                                      │
│   571 │   │   │   │   inputs.append(match.group(1))                                              │
│   572 │   │   else:                                                                              │
│ ❱ 573 │   │   │   inputs.append(line.split()[1])                                                 │
│   574 │   return inputs                                                                          │
│   575                                                                                            │
│   576                                                                                            │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
IndexError: list index out of range

System information

mahesh-panchal commented 5 months ago

This comes about because long input lines are folded across multiple lines:

  tuple( 
    val(meta), 
    path(node_list, stageAs: 'node_list.txt'), 
    path(read_length, stageAs:'read_length.txt'), 
    path(pmd_scores, stageAs: 'PMDscores.txt'), 
    path(breadth_of_coverage, stageAs: 'breadth_of_coverage'), 
    path(name_list, stageAs: 'name_list.txt'), 
    path(maltextract_results, stageAs: 'MaltExtract_output') 
  )

And the code is only checking one line for the full input.