Skip to content

Drop python 3.8, add tests with python 3.13 #3538

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

Merged
merged 9 commits into from
Jun 17, 2025
Merged
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: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,4 @@ body:
* Executor _(eg. slurm, local, awsbatch)_
* OS _(eg. CentOS Linux, macOS, Linux Mint)_
* Version of nf-core/tools _(eg. 1.10, 1.12.1, 1.13)_
* Python version _(eg. 3.11, 3.12)_
* Python version _(eg. 3.12, 3.13)_
9 changes: 4 additions & 5 deletions .github/workflows/changelog.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import re
import sys
from pathlib import Path
from typing import List, Tuple

REPO_URL = "https://github.com/nf-core/tools"

Expand Down Expand Up @@ -51,7 +50,7 @@
sys.exit(0)


def _determine_change_type(pr_title) -> Tuple[str, str]:
def _determine_change_type(pr_title) -> tuple[str, str]:
"""
Determine the type of the PR: Template, Download, Linting, Modules, Subworkflows, or General
Returns a tuple of the section name and the module info.
Expand Down Expand Up @@ -99,7 +98,7 @@ def _determine_change_type(pr_title) -> Tuple[str, str]:
# entry, corresponding to this new PR.
with changelog_path.open("r") as f:
orig_lines = f.readlines()
updated_lines: List[str] = []
updated_lines: list[str] = []


def _skip_existing_entry_for_this_pr(line: str, same_section: bool = True) -> str:
Expand Down Expand Up @@ -189,7 +188,7 @@ def _skip_existing_entry_for_this_pr(line: str, same_section: bool = True) -> st
sys.exit(1)
updated_lines.append(line)
# Collecting lines until the next section.
section_lines: List[str] = []
section_lines: list[str] = []
while True:
line = orig_lines.pop(0)
if line.startswith("#"):
Expand All @@ -215,7 +214,7 @@ def _skip_existing_entry_for_this_pr(line: str, same_section: bool = True) -> st
updated_lines.append(line)


def collapse_newlines(lines: List[str]) -> List[str]:
def collapse_newlines(lines: list[str]) -> list[str]:
updated = []
for idx in range(len(lines)):
if idx != 0 and not lines[idx].strip() and not lines[idx - 1].strip():
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/changelog.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ jobs:
git diff --exit-code ${GITHUB_WORKSPACE}/CHANGELOG.md || echo "changed=YES" >> $GITHUB_ENV
echo "File changed: ${{ env.changed }}"

- name: Set up Python 3.12
- name: Set up Python 3.13
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.13"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/create-lint-wf.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ jobs:
name: Check out source-code repository

# Set up nf-core/tools
- name: Set up Python 3.12
- name: Set up Python 3.13
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.13"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/create-test-wf.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ jobs:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
name: Check out source-code repository

- name: Set up Python 3.12
- name: Set up Python 3.13
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.13"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/deploy-pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
name: Check out source-code repository

- name: Set up Python 3.12
- name: Set up Python 3.13
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.13"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/lint-code.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4

- name: Set up Python 3.12
- name: Set up Python 3.13
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.13"
Expand Down
17 changes: 3 additions & 14 deletions .github/workflows/pytest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,13 @@ jobs:
runs-on: "ubuntu-latest"
strategy:
matrix:
python-version: ["3.8", "3.12"]
python-version: ["3.9", "3.13"]
runner: ["ubuntu-latest"]
include:
- python-version: "3.8"
runner: "ubuntu-20.04"

steps:
- name: Check conditions
id: conditions
run: echo "run-tests=${{ github.ref == 'refs/heads/main' || (matrix.runner == 'ubuntu-20.04' && matrix.python-version == '3.8') }}" >> "$GITHUB_OUTPUT"
run: echo "run-tests=${{ github.ref == 'refs/heads/main' || (matrix.runner == 'ubuntu-20.04' && matrix.python-version == '3.9') }}" >> "$GITHUB_OUTPUT"

outputs:
python-version: ${{ matrix.python-version }}
Expand Down Expand Up @@ -97,14 +94,6 @@ jobs:
python -m pip install --upgrade pip -r requirements-dev.txt
pip install -e .

- name: Downgrade git to the Ubuntu official repository's version
if: ${{ needs.setup.outputs.runner == 'ubuntu-20.04' && needs.setup.outputs.python-version == '3.8' }}
run: |
sudo apt update
sudo apt remove -y git git-man
sudo add-apt-repository --remove ppa:git-core/ppa
sudo apt install -y git

- name: Set up Singularity
if: ${{ matrix.test == 'test_download.py'}}
uses: eWaterCycle/setup-singularity@931d4e31109e875b13309ae1d07c70ca8fbc8537 # v7
Expand Down Expand Up @@ -169,7 +158,7 @@ jobs:
cd pytest

- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Set up Python 3.12
- name: Set up Python 3.13
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
env:
AGENT_TOOLSDIRECTORY: /opt/actions-runner/_work/tools/tools/
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/sync.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ jobs:
path: nf-core/${{ matrix.pipeline }}
fetch-depth: "0"

- name: Set up Python 3.12
- name: Set up Python 3.13
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.13"
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
### General

- Add conda cache dir ([#3610](https://github.com/nf-core/tools/pull/3610))
- Drop python 3.8, add tests with python 3.13 ([#3538](https://github.com/nf-core/tools/pull/3538))
- Fixes a bug with the test-datasets subcommand [#3617](https://github.com/nf-core/tools/issues/3617)

## [v3.3.1 - Tungsten Tamarin Patch](https://github.com/nf-core/tools/releases/tag/3.3.1) - [2025-06-02]
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM python:3.12-slim@sha256:fd95fa221297a88e1cf49c55ec1828edd7c5a428187e67b5d1805692d11588db
FROM python:3.13-slim
LABEL authors="phil.ewels@seqera.io,erik.danielsson@scilifelab.se" \
description="Docker image containing requirements for nf-core/tools"

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ conda install nf-core
Alternatively, you can create a new environment with both nf-core/tools and nextflow:

```bash
conda create --name nf-core python=3.12 nf-core nextflow
conda create --name nf-core python=3.13 nf-core nextflow
conda activate nf-core
```

Expand Down
3 changes: 1 addition & 2 deletions docs/api/_src/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
#
import os
import sys
from typing import Dict

import nf_core

Expand Down Expand Up @@ -114,7 +113,7 @@

# -- Options for LaTeX output ------------------------------------------------

latex_elements: Dict[str, str] = {
latex_elements: dict[str, str] = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
Expand Down
14 changes: 7 additions & 7 deletions nf_core/components/components_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import os
import shutil
from pathlib import Path
from typing import Dict, List, Optional, Union
from typing import Optional, Union

import nf_core.utils
from nf_core.modules.modules_json import ModulesJson
Expand Down Expand Up @@ -65,7 +65,7 @@ def _configure_repo_and_paths(self, nf_dir_req: bool = True) -> None:
self.default_subworkflows_path = Path("subworkflows", self.org)
self.default_subworkflows_tests_path = Path("tests", "subworkflows", self.org)

def get_local_components(self) -> List[str]:
def get_local_components(self) -> list[str]:
"""
Get the local modules/subworkflows in a pipeline
"""
Expand All @@ -78,7 +78,7 @@ def get_local_components(self) -> List[str]:
str(path.relative_to(local_component_dir)) for path in local_component_dir.iterdir() if path.suffix == ".nf"
]

def get_components_clone_modules(self) -> List[str]:
def get_components_clone_modules(self) -> list[str]:
"""
Get the modules/subworkflows repository available in a clone of nf-core/modules
"""
Expand Down Expand Up @@ -142,7 +142,7 @@ def clear_component_dir(self, component_name: str, component_dir: Union[str, Pat
log.error(f"Could not remove {self.component_type[:-1]} {component_name}: {e}")
return False

def components_from_repo(self, install_dir: str) -> List[str]:
def components_from_repo(self, install_dir: str) -> list[str]:
"""
Gets the modules/subworkflows installed from a certain repository

Expand Down Expand Up @@ -200,7 +200,7 @@ def check_modules_structure(self) -> None:
modules/nf-core/modules/TOOL/SUBTOOL
"""
if self.repo_type == "pipeline":
wrong_location_modules: List[Path] = []
wrong_location_modules: list[Path] = []
for directory, _, files in os.walk(Path(self.directory, "modules")):
if "main.nf" in files:
module_path = Path(directory).relative_to(Path(self.directory, "modules"))
Expand Down Expand Up @@ -263,7 +263,7 @@ def check_patch_paths(self, patch_path: Path, module_name: str) -> None:
][module_name]["patch"] = str(patch_path.relative_to(self.directory.resolve()))
modules_json.dump()

def check_if_in_include_stmts(self, component_path: str) -> Dict[str, List[Dict[str, Union[int, str]]]]:
def check_if_in_include_stmts(self, component_path: str) -> dict[str, list[dict[str, Union[int, str]]]]:
"""
Checks for include statements in the main.nf file of the pipeline and a list of line numbers where the component is included
Args:
Expand All @@ -272,7 +272,7 @@ def check_if_in_include_stmts(self, component_path: str) -> Dict[str, List[Dict[
Returns:
(list): A list of dictionaries, with the workflow file and the line number where the component is included
"""
include_stmts: Dict[str, List[Dict[str, Union[int, str]]]] = {}
include_stmts: dict[str, list[dict[str, Union[int, str]]]] = {}
if self.repo_type == "pipeline":
workflow_files = Path(self.directory, "workflows").glob("*.nf")
for workflow_file in workflow_files:
Expand Down
8 changes: 4 additions & 4 deletions nf_core/components/components_differ.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import logging
import os
from pathlib import Path
from typing import Dict, List, Union
from typing import Union

from rich import box
from rich.console import Console, Group, RenderableType
Expand Down Expand Up @@ -308,7 +308,7 @@ def print_diff(
)

@staticmethod
def per_file_patch(patch_fn: Union[str, Path]) -> Dict[str, List[str]]:
def per_file_patch(patch_fn: Union[str, Path]) -> dict[str, list[str]]:
"""
Splits a patch file for several files into one patch per file.

Expand All @@ -324,7 +324,7 @@ def per_file_patch(patch_fn: Union[str, Path]) -> Dict[str, List[str]]:

patches = {}
i = 0
patch_lines: List[str] = []
patch_lines: list[str] = []
key = "preamble"
while i < len(lines):
line = lines[i]
Expand Down Expand Up @@ -470,7 +470,7 @@ def try_apply_patch(
patch_path: Union[str, Path],
component_dir: Path,
reverse: bool = False,
) -> Dict[str, List[str]]:
) -> dict[str, list[str]]:
"""
Try applying a full patch file to a module or subworkflow

Expand Down
4 changes: 2 additions & 2 deletions nf_core/components/components_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import os
import re
from pathlib import Path
from typing import List, Optional
from typing import Optional

import questionary
from rich import print
Expand Down Expand Up @@ -79,7 +79,7 @@ def __init__(
self.component_name = component_name
self.remote_url = remote_url
self.branch = branch
self.errors: List[str] = []
self.errors: list[str] = []
self.verbose = verbose
self.obsolete_snapshots: bool = False
self.update = update
Expand Down
18 changes: 9 additions & 9 deletions nf_core/components/components_utils.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import logging
import re
from pathlib import Path
from typing import Dict, List, Optional, Tuple, Union
from typing import Optional, Union

import questionary
import requests
Expand All @@ -22,7 +22,7 @@
yaml.indent(mapping=2, sequence=2, offset=0)


def get_repo_info(directory: Path, use_prompt: Optional[bool] = True) -> Tuple[Path, Optional[str], str]:
def get_repo_info(directory: Path, use_prompt: Optional[bool] = True) -> tuple[Path, Optional[str], str]:
"""
Determine whether this is a pipeline repository or a clone of
nf-core/modules
Expand Down Expand Up @@ -147,12 +147,12 @@ def prompt_component_version_sha(

def get_components_to_install(
subworkflow_dir: Union[str, Path],
) -> Tuple[List[Dict[str, str]], List[Dict[str, str]]]:
) -> tuple[list[dict[str, str]], list[dict[str, str]]]:
"""
Parse the subworkflow main.nf file to retrieve all imported modules and subworkflows.
"""
modules: Dict[str, Dict[str, str]] = {}
subworkflows: Dict[str, Dict[str, str]] = {}
modules: dict[str, dict[str, str]] = {}
subworkflows: dict[str, dict[str, str]] = {}

with open(Path(subworkflow_dir, "main.nf")) as fh:
for line in fh:
Expand All @@ -165,7 +165,7 @@ def get_components_to_install(
if link.startswith("../../../"):
name_split = name.lower().split("_")
component_name = "/".join(name_split)
component_dict: Dict[str, str] = {
component_dict: dict[str, str] = {
"name": component_name,
}
modules[component_name] = component_dict
Expand Down Expand Up @@ -198,7 +198,7 @@ def get_components_to_install(
return list(modules.values()), list(subworkflows.values())


def get_biotools_response(tool_name: str) -> Optional[Dict]:
def get_biotools_response(tool_name: str) -> Optional[dict]:
"""
Try to get bio.tools information for 'tool'
"""
Expand Down Expand Up @@ -232,12 +232,12 @@ def get_biotools_id(data: dict, tool_name: str) -> str:
return ""


DictWithStrAndTuple = Dict[str, Tuple[List[str], List[str], List[str]]]
DictWithStrAndTuple = dict[str, tuple[list[str], list[str], list[str]]]


def get_channel_info_from_biotools(
data: dict, tool_name: str
) -> Optional[Tuple[DictWithStrAndTuple, DictWithStrAndTuple]]:
) -> Optional[tuple[DictWithStrAndTuple, DictWithStrAndTuple]]:
"""
Try to find input and output channels and the respective EDAM ontology terms

Expand Down
6 changes: 3 additions & 3 deletions nf_core/components/create.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import shutil
import subprocess
from pathlib import Path
from typing import Dict, Optional
from typing import Optional

import jinja2
import questionary
Expand Down Expand Up @@ -59,7 +59,7 @@ def __init__(
self.bioconda = None
self.singularity_container = None
self.docker_container = None
self.file_paths: Dict[str, Path] = {}
self.file_paths: dict[str, Path] = {}
self.not_empty_template = not empty_template
self.migrate_pytest = migrate_pytest
self.tool_identifier = ""
Expand Down Expand Up @@ -357,7 +357,7 @@ def _collect_name_prompt(self):
elif self.component_type == "subworkflows":
self.component = rich.prompt.Prompt.ask("[violet]Name of subworkflow").strip()

def _get_component_dirs(self) -> Dict[str, Path]:
def _get_component_dirs(self) -> dict[str, Path]:
"""Given a directory and a tool/subtool or subworkflow, set the file paths and check if they already exist

Returns dict: keys are relative paths to template files, vals are target paths.
Expand Down
Loading
Loading