nf-core / nf-core/tools

Schema lint: Handle exceptions for `TypeError`s

Open
#2,896 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
Python
Stars
322
Forks
255
Avg merge
2d 3h
Merged PRs (30d)
5

Description

Description of the bug

See this actions run for example, where an integer field had null set as default.

This should fail, but it should be handled nicely with an error message pointing to the relevant field. Rather than an unhandled traceback.

Command used and terminal output
$ nf-core lint

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

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



INFO     Testing pipeline: /home/runner/work/pangenome/pangenome                

╭───────────────────── Traceback (most recent call last) ──────────────────────╮
│ /opt/hostedtoolcache/Python/3.11.8/x64/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/hostedtoolcache/Python/3.11.8/x64/lib/python3.11/site-packages/nf_core/ │
│ __main__.py:141 in run_nf_core                                               │
│                                                                              │
│    138 │   │   │   log.debug(f"Could not check latest version: {e}")         │
│    139 │   │   stderr.print("\n")                                            │
│    140 │   # Launch the click cli                                            │
│ ❱  141 │   nf_core_cli(auto_envvar_prefix="NFCORE")                          │
│    142                                                                       │
│    143                                                                       │
│    144 @tui()                                                                │
│                                                                              │
│ /opt/hostedtoolcache/Python/3.11.8/x64/lib/python3.11/site-packages/click/co │
│ re.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/hostedtoolcache/Python/3.11.8/x64/lib/python3.11/site-packages/rich_cli │
│ ck/rich_command.py:126 in main                                               │
│                                                                              │
│   123 │   │   try:                                                           │
│   124 │   │   │   try:                                                       │
│   125 │   │   │   │   with self.make_context(prog_name, args, **extra) as ct │
│ ❱ 126 │   │   │   │   │   rv = self.invoke(ctx)                              │
│   127 │   │   │   │   │   if not standalone_mode:                            │
│   128 │   │   │   │   │   │   return rv                                      │
│   129 │   │   │   │   │   # it's not safe to `ctx.exit(rv)` here!            │
│                                                                              │
│ /opt/hostedtoolcache/Python/3.11.8/x64/lib/python3.11/site-packages/click/co │
│ re.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 │
│   1689 │   │                                                                 │
│   1690 │   │   # In chain mode we create the contexts step by step, but afte │
│   1691 │   │   # base command has been invoked.  Because at that point we do │
│                                                                              │
│ /opt/hostedtoolcache/Python/3.11.8/x64/lib/python3.11/site-packages/click/co │
│ re.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 │
│   1437 │   │   """Return a list of completions for the incomplete value. Loo │
│                                                                              │
│ /opt/hostedtoolcache/Python/3.11.8/x64/lib/python3.11/site-packages/click/co │
│ re.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  # no │
│                                                                              │
│ /opt/hostedtoolcache/Python/3.11.8/x64/lib/python3.11/site-packages/click/de │
│ corators.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/hostedtoolcache/Python/3.11.8/x64/lib/python3.11/site-packages/nf_core/ │
│ __main__.py:607 in lint                                                      │
│                                                                              │
│    604 │                                                                     │
│    605 │   # Run the lint tests!                                             │
│    606 │   try:                                                              │
│ ❱  607 │   │   lint_obj, module_lint_obj, subworkflow_lint_obj = run_linting │
│    608 │   │   │   dir,                                                      │
│    609 │   │   │   release,                                                  │
│    610 │   │   │   fix,                                                      │
│                                                                              │
│ /opt/hostedtoolcache/Python/3.11.8/x64/lib/python3.11/site-packages/nf_core/ │
│ lint/__init__.py:132 in run_linting                                          │
│                                                                              │
│   129 │                                                                      │
│   130 │   # Run the pipeline linting tests                                   │
│   131 │   try:                                                               │
│ ❱ 132 │   │   lint_obj._lint_pipeline()                                      │
│   133 │   except AssertionError as e:                                        │
│   134 │   │   log.critical(f"Critical error: {e}")                           │
│   135 │   │   log.info("Stopping tests...")                                  │
│                                                                              │
│ /opt/hostedtoolcache/Python/3.11.8/x64/lib/python3.11/site-packages/nf_core/ │
│ lint/__init__.py:364 in _lint_pipeline                                       │
│                                                                              │
│   361 │   │   │   │   │   continue                                           │
│   362 │   │   │   │   self.progress_bar.update(lint_progress, advance=1, tes │
│   363 │   │   │   │   log.debug(f"Running lint test: {test_name}")           │
│ ❱ 364 │   │   │   │   test_results = getattr(self, test_name)()              │
│   365 │   │   │   │   for test in test_results.get("passed", []):            │
│   366 │   │   │   │   │   self.passed.append((test_name, test))              │
│   367 │   │   │   │   for test in test_results.get("ignored", []):           │
│                                                                              │
│ /opt/hostedtoolcache/Python/3.11.8/x64/lib/python3.11/site-packages/nf_core/ │
│ lint/nextflow_config.py:376 in nextflow_config                               │
│                                                                              │
│   373 │   schema.schema_filename = schema_path                               │
│   374 │   schema.no_prompts = True                                           │
│   375 │   schema.load_schema()                                               │
│ ❱ 376 │   schema.get_schema_defaults()  # Get default values from schema     │
│   377 │   schema.get_schema_types()  # Get types from schema                 │
│   378 │   self.nf_config.keys()  # Params in nextflow.config                 │
│   379 │   for param_name in schema.schema_defaults.keys():                   │
│                                                                              │
│ /opt/hostedtoolcache/Python/3.11.8/x64/lib/python3.11/site-packages/nf_core/ │
│ schema.py:174 in get_schema_defaults                                         │
│                                                                              │
│   171 │   │   │   for p_key, param in definition.get("properties", {}).items │
│   172 │   │   │   │   self.schema_params[p_key] = ("definitions", defn_name, │
│   173 │   │   │   │   if "default" in param:                                 │
│ ❱ 174 │   │   │   │   │   param = self.sanitise_param_default(param)         │
│   175 │   │   │   │   │   if param["default"] is not None:                   │
│   176 │   │   │   │   │   │   self.schema_defaults[p_key] = param["default"] │
│   177                                                                        │
│                                                                              │
│ /opt/hostedtoolcache/Python/3.11.8/x64/lib/python3.11/site-packages/nf_core/ │
│ schema.py:139 in sanitise_param_default                                      │
│                                                                              │
│   136 │   │                                                                  │
│   137 │   │   # Integers                                                     │
│   138 │   │   if param["type"] == "integer":                                 │
│ ❱ 139 │   │   │   param["default"] = int(param["default"])                   │
│   140 │   │   │   return param                                               │
│   141 │   │                                                                  │
│   142 │   │   # Numbers                                                      │
╰──────────────────────────────────────────────────────────────────────────────╯
TypeError: int() argument must be a string, a bytes-like object or a real 
number, not 'NoneType'
System information

No response

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with nf_core/schema.py around get_schema_defaults(), then follow its call from nf_core/lint/nextflow_config.py. Reproduce the integer field with a null default using nf-core lint and ensure the invalid field produces a useful error message instead of an unhandled traceback.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
cli, testing-qa
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.