diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index fc73294b5a..897d21130b 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -73,7 +73,7 @@ any code does not adhere to isort formatting. ### pre-commit hooks -This repository comes with [pre-commit](https://pre-commit.com/) hooks for black, isort and Prettier. pre-commit automatically runs checks before a commit is committed into the git history. If all checks pass, the commit is made, if files are changed by the pre-commit hooks, the user is informed and has to stage the changes and attempt the commit again. +This repository comes with [pre-commit](https://pre-commit.com/) hooks for black, ruff and Prettier. pre-commit automatically runs checks before a commit is committed into the git history. If all checks pass, the commit is made, if files are changed by the pre-commit hooks, the user is informed and has to stage the changes and attempt the commit again. You can use the pre-commit hooks if you like, but you don't have to. The CI on Github will run the same checks as the tools installed with pre-commit. If the pre-commit checks pass, then the same checks in the CI will pass, too. diff --git a/.github/workflows/create-lint-wf.yml b/.github/workflows/create-lint-wf.yml index d09bdfd822..61f4cb0417 100644 --- a/.github/workflows/create-lint-wf.yml +++ b/.github/workflows/create-lint-wf.yml @@ -47,12 +47,17 @@ jobs: with: version: ${{ matrix.NXF_VER }} - # Install the Prettier linting tools + # Install the Prettier & Ruff linting tools - uses: actions/setup-node@v3 - name: Install Prettier run: npm install -g prettier + - name: Install Ruff + run: | + python -m pip install --upgrade pip + pip install ruff + # Install the editorconfig linting tools - name: Install editorconfig-checker run: npm install -g editorconfig-checker @@ -76,6 +81,9 @@ jobs: - name: Run ECLint check run: editorconfig-checker -exclude README.md $(find nf-core-testpipeline/.* -type f | grep -v '.git\|.py\|md\|json\|yml\|yaml\|html\|css\|work\|.nextflow\|build\|nf_core.egg-info\|log.txt\|Makefile') + - name: Run Ruff + run: ruff . + # Update modules to the latest version - name: nf-core modules update run: nf-core --log-file log.txt modules update --dir nf-core-testpipeline --all --no-preview diff --git a/.github/workflows/fix-linting.yml b/.github/workflows/fix-linting.yml index 30cf965af0..8a4cb7847c 100644 --- a/.github/workflows/fix-linting.yml +++ b/.github/workflows/fix-linting.yml @@ -42,11 +42,12 @@ jobs: uses: actions/setup-python@v4 with: python-version: 3.8 - - name: python-isort - uses: isort/isort-action@v1.0.0 - with: - isortVersion: "latest" - requirementsFiles: "requirements.txt requirements-dev.txt" + - name: Install Ruff + run: | + python -m pip install --upgrade pip + pip install ruff + - name: Run Ruff + run: ruff check --fix . - name: Commit & push changes run: | diff --git a/.github/workflows/lint-code.yml b/.github/workflows/lint-code.yml index 869d8898d9..b49566cb62 100644 --- a/.github/workflows/lint-code.yml +++ b/.github/workflows/lint-code.yml @@ -70,7 +70,7 @@ jobs: repo-token: ${{ secrets.GITHUB_TOKEN }} allow-repeats: false - isort: + ruff: runs-on: ubuntu-latest steps: - name: Check out source-code repository @@ -80,8 +80,9 @@ jobs: uses: actions/setup-python@v4 with: python-version: 3.8 - - name: python-isort - uses: isort/isort-action@v1.1.0 - with: - isortVersion: "latest" - requirementsFiles: "requirements.txt requirements-dev.txt" + - name: Install Ruff + run: | + python -m pip install --upgrade pip + pip install ruff + - name: Run Ruff + run: ruff . diff --git a/.github/workflows/pytest-frozen-ubuntu-20.04.yml b/.github/workflows/pytest-frozen-ubuntu-20.04.yml index 6d49145ed7..b015376633 100644 --- a/.github/workflows/pytest-frozen-ubuntu-20.04.yml +++ b/.github/workflows/pytest-frozen-ubuntu-20.04.yml @@ -33,6 +33,7 @@ jobs: - name: Downgrade git to the Ubuntu official repository's version run: | + sudo apt update sudo apt remove git git-man sudo add-apt-repository --remove ppa:git-core/ppa sudo apt install git diff --git a/.gitpod.yml b/.gitpod.yml index 263fcc41db..e7bed37096 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -17,3 +17,4 @@ vscode: # - nextflow.nextflow # Nextflow syntax highlighting - oderwat.indent-rainbow # Highlight indentation level - streetsidesoftware.code-spell-checker # Spelling checker for source code + - charliermarsh.ruff # Code linter Ruff (v0.0.239) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b7aeeb5bc9..f8427cff44 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,11 +3,13 @@ repos: rev: 23.1.0 hooks: - id: black - - repo: https://github.com/pycqa/isort - rev: 5.12.0 - hooks: - - id: isort + language_version: python3.9 - repo: https://github.com/pre-commit/mirrors-prettier rev: "v2.7.1" hooks: - id: prettier + - repo: https://github.com/charliermarsh/ruff-pre-commit + rev: "v0.0.239" + hooks: + - id: ruff + args: [--fix] diff --git a/CHANGELOG.md b/CHANGELOG.md index 67e6ada896..011f110688 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ ### Linting +- Add Ruff to lint library imports and python code ([#2174](https://github.com/nf-core/tools/pull/2174)) + ### Modules - Add an `--empty-template` option to create a module without TODO statements or examples ([#2175](https://github.com/nf-core/tools/pull/2175) & [#2177](https://github.com/nf-core/tools/pull/2177)) diff --git a/README.md b/README.md index 60dc102996..1684c80c88 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ [![codecov](https://codecov.io/gh/nf-core/tools/branch/master/graph/badge.svg)](https://codecov.io/gh/nf-core/tools) [![code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) [![code style: prettier](https://img.shields.io/badge/code%20style-prettier-ff69b4.svg)](https://github.com/prettier/prettier) +[![code style: Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v1.json)](https://github.com/charliermarsh/ruff) [![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://pycqa.github.io/isort/) [![install with Bioconda](https://img.shields.io/badge/install%20with-bioconda-brightgreen.svg)](https://bioconda.github.io/recipes/nf-core/README.html) diff --git a/nf_core/components/components_utils.py b/nf_core/components/components_utils.py index 9a0565296e..30df147c18 100644 --- a/nf_core/components/components_utils.py +++ b/nf_core/components/components_utils.py @@ -1,5 +1,4 @@ import logging -import os import re from pathlib import Path diff --git a/nf_core/gitpod/gitpod.Dockerfile b/nf_core/gitpod/gitpod.Dockerfile index fa435c3612..01cbc004dc 100644 --- a/nf_core/gitpod/gitpod.Dockerfile +++ b/nf_core/gitpod/gitpod.Dockerfile @@ -43,6 +43,7 @@ RUN conda config --add channels defaults && \ nf-test \ black \ prettier \ + ruff \ pytest-workflow && \ mamba clean --all -f -y diff --git a/nf_core/lint/actions_ci.py b/nf_core/lint/actions_ci.py index 9aa18135d9..83f4ebae70 100644 --- a/nf_core/lint/actions_ci.py +++ b/nf_core/lint/actions_ci.py @@ -1,5 +1,4 @@ import os -import re import yaml diff --git a/nf_core/modules/test_yml_builder.py b/nf_core/modules/test_yml_builder.py index c9a6273a58..ef79d447bd 100644 --- a/nf_core/modules/test_yml_builder.py +++ b/nf_core/modules/test_yml_builder.py @@ -26,7 +26,6 @@ from nf_core.components.components_command import ComponentCommand from ..lint_utils import run_prettier_on_file -from .modules_repo import ModulesRepo log = logging.getLogger(__name__) @@ -294,7 +293,8 @@ def get_md5_sums(self, command, results_dir=None, results_dir_repeat=None): if test_files[i].get("md5sum") and not test_files[i].get("md5sum") == test_files_repeat[i]["md5sum"]: test_files[i].pop("md5sum") test_files[i]["contains"] = [ - "# TODO nf-core: file md5sum was variable, please replace this text with a string found in the file instead " + "# TODO nf-core: file md5sum was variable, please replace this text with a " + + "string found in the file instead " ] if len(test_files) == 0: diff --git a/nf_core/pipeline-template/pyproject.toml b/nf_core/pipeline-template/pyproject.toml index 0d62beb6f9..f7699e5b3a 100644 --- a/nf_core/pipeline-template/pyproject.toml +++ b/nf_core/pipeline-template/pyproject.toml @@ -4,7 +4,16 @@ line-length = 120 target_version = ["py37", "py38", "py39", "py310"] -[tool.isort] -profile = "black" -known_first_party = ["nf_core"] -multi_line_output = 3 +[tool.ruff] +select = [ + # Pyflakes + "F401", # imported but unused + "F841", # Local variable is assigned to but never used + # isort + "I001" +] +line-length = 120 +target-version = "py37" + +[tool.ruff.isort] +known-first-party = ["nf_core"] diff --git a/pyproject.toml b/pyproject.toml index 2380073107..b62f9aee86 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,7 +16,19 @@ markers = [ testpaths = ["tests"] norecursedirs = [ ".*", "build", "dist", "*.egg", "data", "__pycache__", ".github", "nf_core", "docs"] -[tool.isort] -profile = "black" -known_first_party = ["nf_core"] -multi_line_output = 3 +[tool.ruff] +select = [ + # Pyflakes + "F401", # imported but unused + "F841", # Local variable is assigned to but never used + # isort + "I001" +] +line-length = 120 +target-version = "py37" + +[tool.ruff.isort] +known-first-party = ["nf_core"] + +[tool.ruff.per-file-ignores] +"__init__.py" = ["E402", "F401"] diff --git a/requirements-dev.txt b/requirements-dev.txt index 42ce780ce4..f8be4d18f9 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,5 +1,5 @@ black -isort +ruff myst_parser pytest-cov pytest-datafiles diff --git a/tests/modules/create_test_yml.py b/tests/modules/create_test_yml.py index d444ff841a..9468c81d64 100644 --- a/tests/modules/create_test_yml.py +++ b/tests/modules/create_test_yml.py @@ -1,11 +1,10 @@ import os -from pathlib import Path import pytest import nf_core.modules -from ..utils import GITLAB_DEFAULT_BRANCH, GITLAB_URL, with_temporary_folder +from ..utils import with_temporary_folder @with_temporary_folder diff --git a/tests/modules/lint.py b/tests/modules/lint.py index 476481a109..8d0a2163f2 100644 --- a/tests/modules/lint.py +++ b/tests/modules/lint.py @@ -1,4 +1,3 @@ -import os from pathlib import Path import pytest diff --git a/tests/modules/modules_test.py b/tests/modules/modules_test.py index eb207fa28b..c246e560d9 100644 --- a/tests/modules/modules_test.py +++ b/tests/modules/modules_test.py @@ -1,5 +1,4 @@ """Test the 'modules test' command which runs module pytests.""" -import os import shutil from pathlib import Path diff --git a/tests/subworkflows/remove.py b/tests/subworkflows/remove.py index 53a948778b..dec67875bd 100644 --- a/tests/subworkflows/remove.py +++ b/tests/subworkflows/remove.py @@ -1,7 +1,5 @@ from pathlib import Path -from rich.console import Console - from nf_core.modules.modules_json import ModulesJson @@ -18,7 +16,7 @@ def test_subworkflows_remove_subworkflow(self): bam_sort_stats_samtools_path = Path(subworkflow_path, "bam_sort_stats_samtools") bam_stats_samtools_path = Path(subworkflow_path, "bam_stats_samtools") samtools_index_path = Path(self.subworkflow_install.dir, "modules", "nf-core", "samtools", "index") - mod_json_obj = ModulesJson(self.pipeline_dir) + ModulesJson(self.pipeline_dir) mod_json_before = ModulesJson(self.pipeline_dir).get_modules_json() assert self.subworkflow_remove.remove("bam_sort_stats_samtools") mod_json_after = ModulesJson(self.pipeline_dir).get_modules_json() diff --git a/tests/subworkflows/subworkflows_test.py b/tests/subworkflows/subworkflows_test.py index adb0989b33..26671be7c3 100644 --- a/tests/subworkflows/subworkflows_test.py +++ b/tests/subworkflows/subworkflows_test.py @@ -1,5 +1,4 @@ """Test the 'subworkflows test' command which runs module pytests.""" -import os import shutil from pathlib import Path diff --git a/tests/subworkflows/update.py b/tests/subworkflows/update.py index 698086e186..f9cc89bd43 100644 --- a/tests/subworkflows/update.py +++ b/tests/subworkflows/update.py @@ -8,7 +8,6 @@ import nf_core.utils from nf_core.modules.modules_json import ModulesJson from nf_core.modules.modules_repo import NF_CORE_MODULES_NAME, NF_CORE_MODULES_REMOTE -from nf_core.modules.remove import ModuleRemove from nf_core.modules.update import ModuleUpdate from nf_core.subworkflows.update import SubworkflowUpdate diff --git a/tests/test_download.py b/tests/test_download.py index e2ae882394..733bbe4d7b 100644 --- a/tests/test_download.py +++ b/tests/test_download.py @@ -1,7 +1,6 @@ """Tests for the download subcommand of nf-core tools """ -import hashlib import os import shutil import tempfile @@ -14,7 +13,7 @@ import nf_core.utils from nf_core.download import DownloadWorkflow -from .utils import with_temporary_file, with_temporary_folder +from .utils import with_temporary_folder class DownloadTest(unittest.TestCase): diff --git a/tests/test_lint.py b/tests/test_lint.py index e4e93bd1f4..6a89146e30 100644 --- a/tests/test_lint.py +++ b/tests/test_lint.py @@ -55,7 +55,7 @@ def test_run_linting_function(self): We don't really check any of this code as it's just a series of function calls and we're testing each of those individually. This is mostly to check for syntax errors.""" - lint_obj = nf_core.lint.run_linting(self.test_pipeline_dir, False) + nf_core.lint.run_linting(self.test_pipeline_dir, False) def test_init_PipelineLint(self): """Simply create a PipelineLint object. diff --git a/tests/test_schema.py b/tests/test_schema.py index d3b4fda817..3f74bcf4ad 100644 --- a/tests/test_schema.py +++ b/tests/test_schema.py @@ -305,7 +305,7 @@ def test_build_schema(self): Build a new schema param from a pipeline Run code to ensure it doesn't crash. Individual functions tested separately. """ - param = self.schema_obj.build_schema(self.template_dir, True, False, None) + self.schema_obj.build_schema(self.template_dir, True, False, None) @with_temporary_folder def test_build_schema_from_scratch(self, tmp_dir): @@ -319,7 +319,7 @@ def test_build_schema_from_scratch(self, tmp_dir): shutil.copytree(self.template_dir, test_pipeline_dir) os.remove(os.path.join(test_pipeline_dir, "nextflow_schema.json")) - param = self.schema_obj.build_schema(test_pipeline_dir, True, False, None) + self.schema_obj.build_schema(test_pipeline_dir, True, False, None) @mock.patch("requests.post") def test_launch_web_builder_timeout(self, mock_post): diff --git a/tests/test_subworkflows.py b/tests/test_subworkflows.py index 552a2ab176..7f4b479e65 100644 --- a/tests/test_subworkflows.py +++ b/tests/test_subworkflows.py @@ -30,7 +30,7 @@ def create_modules_repo_dummy(tmp_dir): with open(os.path.join(root_dir, ".nf-core.yml"), "w") as fh: fh.writelines(["repository_type: modules", "\n", "org_path: nf-core", "\n"]) - with requests_mock.Mocker() as mock: + with requests_mock.Mocker(): subworkflow_create = nf_core.subworkflows.SubworkflowCreate(root_dir, "test_subworkflow", "@author", True) subworkflow_create.create()