diff --git a/.github/workflows/labels.yml b/.github/workflows/labels.yml index 52c578c..188466c 100644 --- a/.github/workflows/labels.yml +++ b/.github/workflows/labels.yml @@ -26,4 +26,4 @@ jobs: https://raw.githubusercontent.com/biocommons/.github/main/etc/labels.yml .github/labels.yml - delete-other-labels: false \ No newline at end of file + delete-other-labels: false diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index e511102..40886c8 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -44,6 +44,21 @@ jobs: run: | ruff format --check . + - name: Ensure EOF line + uses: pre-commit/action@v3.0.1 + with: + extra_args: end-of-file-fixer --all-files + + - name: Fix trailing whitespace + uses: pre-commit/action@v3.0.1 + with: + extra_args: trailing-whitespace --all-files + + - name: Fix mixed line endings + uses: pre-commit/action@v3.0.1 + with: + extra_args: mixed-line-ending --all-files + test: runs-on: ubuntu-latest diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f60689b..f1a80e8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,7 +1,20 @@ repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + - id: check-added-large-files + - id: detect-private-key + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-merge-conflict + - id: detect-aws-credentials + args: [ --allow-missing-credentials ] + - id: mixed-line-ending + args: [ --fix=lf ] - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.4.4 + rev: v0.12.8 hooks: - - id: ruff - id: ruff-format - args: [ --check ] + - id: ruff + args: [ --fix, --exit-non-zero-on-fix ] +minimum_pre_commit_version: 4.2.0 diff --git a/Makefile b/Makefile index bbcce4d..32b5a5a 100644 --- a/Makefile +++ b/Makefile @@ -65,7 +65,7 @@ build: %: ############################################################################ #= TESTING -# see test configuration in setup.cfg +# see test configuration in pyproject.toml #=> test: execute tests #=> test-code: test code (including embedded doctests) @@ -126,6 +126,7 @@ cleaner: clean rm -frv **/*.pyc rm -frv **/*.orig rm -frv **/*.rej + rm -fvr .ruff_cache #=> cleanest: remove files and directories that require more time/network fetches to rebuild .PHONY: cleanest diff --git a/pyproject.toml b/pyproject.toml index 9e0da19..2357745 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,8 +22,8 @@ dependencies = ["coloredlogs ~= 15.0", "pyyaml ~= 6.0"] dev = [ "build ~= 0.8", "ipython ~= 8.4", - "pre-commit ~= 3.4", - "ruff == 0.4.4", + "pre-commit >= 4.2.0", + "ruff == 0.12.8", ] tests = [ "pytest-cov ~= 4.1", @@ -104,63 +104,96 @@ exclude_lines = [ [tool.ruff] src = ["src", "tests"] line-length = 100 +exclude = [ + "bin", + "sbin", + "docs", +] [tool.ruff.lint] -select = [ - "F", # https://docs.astral.sh/ruff/rules/#pyflakes-f - "E", - "W", # https://docs.astral.sh/ruff/rules/#pycodestyle-e-w - "I", # https://docs.astral.sh/ruff/rules/#isort-i - "N", # https://docs.astral.sh/ruff/rules/#pep8-naming-n - "UP", # https://docs.astral.sh/ruff/rules/#pyupgrade-up - "YTT", # https://docs.astral.sh/ruff/rules/#flake8-2020-ytt - "S", # https://docs.astral.sh/ruff/rules/#flake8-bandit-s - "B", # https://docs.astral.sh/ruff/rules/#flake8-bugbear-b - "A", # https://docs.astral.sh/ruff/rules/#flake8-builtins-a - "C4", # https://docs.astral.sh/ruff/rules/#flake8-comprehensions-c4 - "DTZ", # https://docs.astral.sh/ruff/rules/#flake8-datetimez-dtz - "EM", # https://docs.astral.sh/ruff/rules/#flake8-errmsg-em - "LOG", # https://docs.astral.sh/ruff/rules/#flake8-logging-log - "G", # https://docs.astral.sh/ruff/rules/#flake8-logging-format-g - "PIE", # https://docs.astral.sh/ruff/rules/#flake8-pie-pie - "PT", # https://docs.astral.sh/ruff/rules/#flake8-pytest-style-pt - "RSE", # https://docs.astral.sh/ruff/rules/#flake8-raise-rse - "RET", # https://docs.astral.sh/ruff/rules/#flake8-return-ret - "SIM", # https://docs.astral.sh/ruff/rules/#flake8-simplify-sim - "ARG", # https://docs.astral.sh/ruff/rules/#flake8-unused-arguments-arg - "PTH", # https://docs.astral.sh/ruff/rules/#flake8-use-pathlib-pth - "PL", # https://docs.astral.sh/ruff/rules/#pylint-pl - "TRY", # https://docs.astral.sh/ruff/rules/#tryceratops-try - "PERF", # https://docs.astral.sh/ruff/rules/#perflint-perf - "RUF", # https://docs.astral.sh/ruff/rules/#ruff-specific-rules-ruf -] -fixable = [ - "F401", - "F541", - "I", - "D", - "UP", - "B", - "C4", - "EM", - "PIE", - "PT", - "RSE", - "RET", - "SIM", - "PERF", - "RUF", -] +select = ["ALL"] ignore = [ + # not used + "AIR", + "ERA", + "FAST", + "YTT", + "FBT", + "CPY", + "DJ", + "EM", + "EXE", + "FIX", + "FA", + "INT", + "PYI", + "TID", + "TD", + "TC", + "C90", + "NPY", + "PD", # ignore for compatibility with formatter + "D206", + "D300", "W191", "E111", "E114", "E117", + "E501", + "W191", "S321", - # other - "PLR0913", + "COM812", + # don't require types on *args, **kwargs + "ANN002", + "ANN003", + # subjective pylint thresholds + "PLR0904", + "PLR091", + "PLR1702", + # excessive docstring requirements + "D105", + "D205", + "D203", + "D213", # conflicts with D212 + "D400", + "D401", + "D403", + "D415", + # excessive exception message requirements + "TRY003", + # excessive type ignore requirements + "PGH003", + # misc unnecessary readability requirements + "RET504", ] [tool.ruff.lint.per-file-ignores] -"tests/*" = ["S101"] +# ANN001 - missing-type-function-argument +# ANN2 - missing-return-type +# D100 - undocumented-public-module +# D101 - undocumented-public-class +# D102 - undocumented-public-method +# D103 - undocumented-public-function +# S101 - assert +# B011 - assert-false +# INP001 - implicit-namespace-package +# SLF001 - private-member-access +# BLE001 - blind-except +"tests/*" = [ + "ANN001", + "ANN2", + "D100", + "D10", + "S101", + "B011", + "INP001", + "SLF001", + "BLE001", +] + +[tool.ruff.lint.flake8-annotations] +mypy-init-return = true + +[tool.ruff.format] +docstring-code-format = true diff --git a/src/biocommons/example/__main__.py b/src/biocommons/example/__main__.py index 24dcdf9..8eaa51a 100644 --- a/src/biocommons/example/__main__.py +++ b/src/biocommons/example/__main__.py @@ -8,9 +8,9 @@ _logger = logging.getLogger(__name__) -def main(): # pragma: no cover +def main() -> None: # pragma: no cover """marvin.example main""" - import coloredlogs + import coloredlogs # noqa: PLC0415 logging.Formatter.converter = time.gmtime coloredlogs.install( @@ -22,8 +22,8 @@ def main(): # pragma: no cover quote = marvin.get_quote() _logger.warning("Got quote from Marvin (len=%s)", len(quote)) - print("Marvin says:") - print(quote) + print("Marvin says:") # noqa: T201 + print(quote) # noqa: T201 if __name__ == "__main__": diff --git a/src/biocommons/example/marvin.py b/src/biocommons/example/marvin.py index f5dfb79..b88b5c0 100644 --- a/src/biocommons/example/marvin.py +++ b/src/biocommons/example/marvin.py @@ -18,7 +18,7 @@ def is_alive() -> bool: - """tests whether Marvin is alive + """Tests whether Marvin is alive >>> is_alive() is True True @@ -28,7 +28,7 @@ def is_alive() -> bool: def get_quote() -> str: - """return random Marvin quote + """Return random Marvin quote eg> get_quote() "There's only one life-form as intelligent..." diff --git a/src/biocommons/example/marvin_adjacent_test.py b/src/biocommons/example/marvin_adjacent_test.py index 5498dd1..ee450fa 100644 --- a/src/biocommons/example/marvin_adjacent_test.py +++ b/src/biocommons/example/marvin_adjacent_test.py @@ -1,8 +1,8 @@ """biocommons.example test, adjacent to code""" -from .marvin import get_quote +from biocommons.example.marvin import get_quote -def test_get_quote(): +def test_get_quote() -> None: """test get_quote""" assert get_quote() is not None # noqa: S101 diff --git a/src/biocommons/example/quotes.yaml b/src/biocommons/example/quotes.yaml index 16b23f6..e85be62 100644 --- a/src/biocommons/example/quotes.yaml +++ b/src/biocommons/example/quotes.yaml @@ -12,15 +12,15 @@ quotes: fifty thousand times more intelligent than you and even I don't know the answer. It gives me a headache just trying to think down to your level. - + - >- There's only one life-form as intelligent as me within thirty parsecs of here and that's me. - + - >- I wish you'd just tell me rather trying to engage my enthusiasm because I haven't got one. - + - >- And then, of course, I've got this terrible pain in all the diodes down my left side. diff --git a/src/biocommons/example/tests/__init__.py b/src/biocommons/example/tests/__init__.py index e69de29..6e03199 100644 --- a/src/biocommons/example/tests/__init__.py +++ b/src/biocommons/example/tests/__init__.py @@ -0,0 +1 @@ +# noqa: D104 diff --git a/src/biocommons/example/tests/conftest.py b/src/biocommons/example/tests/conftest.py index 79d7cd9..4e7dd24 100644 --- a/src/biocommons/example/tests/conftest.py +++ b/src/biocommons/example/tests/conftest.py @@ -1,10 +1,11 @@ +# noqa: D100 import importlib.resources import pytest import yaml -@pytest.fixture() -def all_quotes(): +@pytest.fixture +def all_quotes() -> dict: # noqa: D103 quotes_stream = importlib.resources.files("example").joinpath("quotes.yaml").read_text() return yaml.load(quotes_stream, Loader=yaml.SafeLoader)["quotes"] diff --git a/src/biocommons/example/tests/marvin_subdir_test.py b/src/biocommons/example/tests/marvin_subdir_test.py index ff94067..a7eef8f 100644 --- a/src/biocommons/example/tests/marvin_subdir_test.py +++ b/src/biocommons/example/tests/marvin_subdir_test.py @@ -1,9 +1,9 @@ """biocommons.example test, within tests directory adjacent to source""" -from ..marvin import get_quote +from biocommons.example.marvin import get_quote -def test_get_quote(all_quotes): +def test_get_quote(all_quotes: list[str]) -> None: """test get_quote""" # NB: all_quotes argument is a test fixture; see ./conftest.py assert get_quote() in all_quotes # noqa: S101