From 203377e85d66e91de834d9c8c4b96f41f9c72d8d Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Wed, 26 Apr 2023 00:13:44 +0200 Subject: [PATCH] Replace flake8, bugbear, isort, and pyupgrade with ruff (#568) --- .github/dependabot.yml | 1 - .github/workflows/build.yaml | 8 ++ .pre-commit-config.yaml | 34 ++++---- contributing.rst | 11 ++- docs/decorators.rst | 2 +- docs/index.rst | 1 - docs/plugins/collect.rst | 1 - docs/plugins/doctests.rst | 2 - docs/plugins/functions.rst | 1 - docs/plugins/junitxml.rst | 1 - docs/plugins/outcomes.rst | 1 - docs/plugins/testid.rst | 1 - nose2/plugins/junitxml.py | 4 +- nose2/plugins/layers.py | 2 +- nose2/plugins/mp.py | 2 +- nose2/session.py | 2 +- nose2/sphinxext.py | 4 +- .../test_prettyassert_ignore_passing.py | 2 +- nose2/tests/unit/test_junitxml.py | 2 +- nose2/tests/unit/test_layers_plugin.py | 4 +- nose2/tests/unit/test_testid_plugin.py | 4 +- pyproject.toml | 80 +++++++++++++++++++ setup.cfg | 15 ---- 23 files changed, 124 insertions(+), 61 deletions(-) create mode 100644 pyproject.toml delete mode 100644 setup.cfg diff --git a/.github/dependabot.yml b/.github/dependabot.yml index c0f8fbb0..83880288 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,4 +5,3 @@ updates: directory: "/" schedule: interval: "weekly" - diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index a7fccced..f50a5fe4 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -6,6 +6,13 @@ on: schedule: - cron: '0 4 * * 1' jobs: + ruff: # https://beta.ruff.rs + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - run: pip install --user ruff + - run: ruff --format=github . + # this job ensures that tests can run from the packaged version, which means # that nose2 is correctly packaging and distributing its tests test-sdist: @@ -27,6 +34,7 @@ jobs: nose2 -v --pretty-assert test: + needs: ruff strategy: fail-fast: false matrix: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0a2917c3..6485e83e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,6 +3,9 @@ repos: rev: v4.4.0 hooks: - id: check-merge-conflict + - id: check-toml + - id: check-yaml + - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/python-jsonschema/check-jsonschema rev: 0.22.0 @@ -10,29 +13,26 @@ repos: - id: check-github-workflows - id: check-dependabot - id: check-readthedocs -- repo: https://github.com/asottile/pyupgrade - rev: v3.3.1 - hooks: - - id: pyupgrade - args: ["--py36-plus"] - repo: https://github.com/psf/black rev: 23.3.0 hooks: - id: black language_version: python3 -- repo: https://github.com/PyCQA/flake8 - rev: 6.0.0 - hooks: - - id: flake8 - language_version: python3 - additional_dependencies: ['flake8-bugbear==22.7.1'] -- repo: https://github.com/PyCQA/isort - rev: 5.12.0 - hooks: - - id: isort - language_version: python3 - types: [python] - repo: https://github.com/codespell-project/codespell rev: v2.2.4 hooks: - id: codespell + additional_dependencies: + - tomli +- repo: https://github.com/charliermarsh/ruff-pre-commit + rev: v0.0.261 + hooks: + - id: ruff +- repo: https://github.com/abravalheri/validate-pyproject + rev: v0.12.2 + hooks: + - id: validate-pyproject +- repo: https://github.com/tox-dev/pyproject-fmt + rev: 0.9.2 + hooks: + - id: pyproject-fmt diff --git a/contributing.rst b/contributing.rst index 62cc7b41..ef9aaad6 100644 --- a/contributing.rst +++ b/contributing.rst @@ -42,8 +42,8 @@ You can also use ``make test`` and ``make lint`` for these. Linting +++++++ -nose2 uses `black`_, `isort`_, and `flake8`_ to enforce linting and code -style rules, and `pre-commit`_ to run these tools. +nose2 uses `black`_ and `ruff`_ to enforce code formatting and linting and +`pre-commit`_ to run these tools. For the best development experience, we recommend setting up integrations with your editor and git. @@ -59,9 +59,8 @@ have ``pre-commit`` installed and run: If you need to bypass pre-commit hooks after setting this up, you can commit with ``--no-verify`` -.. _github: https://github.com/nose-devs/nose2 -.. _tox: http://pypi.python.org/pypi/tox .. _black: https://black.readthedocs.io/ -.. _isort: https://pycqa.github.io/isort/ -.. _flake8: https://flake8.pycqa.org/ +.. _github: https://github.com/nose-devs/nose2 .. _pre-commit: https://pre-commit.com/ +.. _ruff: https://beta.ruff.rs/docs/ +.. _tox: http://pypi.python.org/pypi/tox diff --git a/docs/decorators.rst b/docs/decorators.rst index 250fe18a..b73d921d 100644 --- a/docs/decorators.rst +++ b/docs/decorators.rst @@ -8,4 +8,4 @@ Setup & Teardown ================ .. autofunction :: nose2.tools.decorators.with_setup -.. autofunction :: nose2.tools.decorators.with_teardown \ No newline at end of file +.. autofunction :: nose2.tools.decorators.with_teardown diff --git a/docs/index.rst b/docs/index.rst index e9e08c9a..ce9210b8 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -2,4 +2,3 @@ .. include :: ../README.rst .. include :: contents.rst.inc - diff --git a/docs/plugins/collect.rst b/docs/plugins/collect.rst index 6c51e10a..e94de643 100644 --- a/docs/plugins/collect.rst +++ b/docs/plugins/collect.rst @@ -3,4 +3,3 @@ Collecting tests without running them ===================================== .. autoplugin :: nose2.plugins.collect.CollectOnly - diff --git a/docs/plugins/doctests.rst b/docs/plugins/doctests.rst index 6bb7a579..8e3a8df0 100644 --- a/docs/plugins/doctests.rst +++ b/docs/plugins/doctests.rst @@ -3,5 +3,3 @@ Loader: Doctests ================ .. autoplugin :: nose2.plugins.doctests.DocTestLoader - - diff --git a/docs/plugins/functions.rst b/docs/plugins/functions.rst index 55ae3af9..75260492 100644 --- a/docs/plugins/functions.rst +++ b/docs/plugins/functions.rst @@ -3,4 +3,3 @@ Loader: Test Functions ====================== .. autoplugin :: nose2.plugins.loader.functions.Functions - diff --git a/docs/plugins/junitxml.rst b/docs/plugins/junitxml.rst index 2a898e71..c6fdf94a 100644 --- a/docs/plugins/junitxml.rst +++ b/docs/plugins/junitxml.rst @@ -88,4 +88,3 @@ The XML test report for nose2's sample scenario with tests in a package looks li - diff --git a/docs/plugins/outcomes.rst b/docs/plugins/outcomes.rst index 99b21891..ab872269 100644 --- a/docs/plugins/outcomes.rst +++ b/docs/plugins/outcomes.rst @@ -3,4 +3,3 @@ Mapping exceptions to test outcomes =================================== .. autoplugin :: nose2.plugins.outcomes.Outcomes - diff --git a/docs/plugins/testid.rst b/docs/plugins/testid.rst index faee79e0..bee425f9 100644 --- a/docs/plugins/testid.rst +++ b/docs/plugins/testid.rst @@ -3,4 +3,3 @@ Using Test IDs ============== .. autoplugin :: nose2.plugins.testid.TestId - diff --git a/nose2/plugins/junitxml.py b/nose2/plugins/junitxml.py index 1836a903..b56b8966 100644 --- a/nose2/plugins/junitxml.py +++ b/nose2/plugins/junitxml.py @@ -346,10 +346,10 @@ def _iso_timestamp(self): ] ILLEGAL_REGEX_STR = ( - "[" + "".join([f"{chr(l)}-{chr(h)}" for (l, h) in ILLEGAL_RANGES]) + "]" + "[" + "".join([f"{chr(lo)}-{chr(hi)}" for (lo, hi) in ILLEGAL_RANGES]) + "]" ) RESTRICTED_REGEX_STR = ( - "[" + "".join([f"{chr(l)}-{chr(h)}" for (l, h) in RESTRICTED_RANGES]) + "]" + "[" + "".join([f"{chr(lo)}-{chr(hi)}" for (lo, hi) in RESTRICTED_RANGES]) + "]" ) _ILLEGAL_REGEX = re.compile(ILLEGAL_REGEX_STR, re.U) diff --git a/nose2/plugins/layers.py b/nose2/plugins/layers.py index 8177487a..a5172ed0 100644 --- a/nose2/plugins/layers.py +++ b/nose2/plugins/layers.py @@ -147,7 +147,7 @@ def add_layer_to_tree(cls, tree, layer): raise exceptions.LoadTestsFailure(err.format(layer)) for parent in parents: if parent not in tree and parent is not object: - raise MissingParentLayer() + raise MissingParentLayer # if we reached that point, then all the parents are in the tree # if there are multiple parents, we first try to get the closest # to the current layer. diff --git a/nose2/plugins/mp.py b/nose2/plugins/mp.py index b948b094..d035bd96 100644 --- a/nose2/plugins/mp.py +++ b/nose2/plugins/mp.py @@ -343,7 +343,7 @@ def procserver(session_export, conn): # XXX If there a need to protect the loop? try/except? rlog.debug("Execute test %s (%s)", testid, test) executor(test, event.result) - events = [e for e in ssn.hooks.flush()] + events = list(ssn.hooks.flush()) try: conn.send((testid, events)) rlog.debug("Log for %s returned", testid) diff --git a/nose2/session.py b/nose2/session.py index 67c06799..1078a0c3 100644 --- a/nose2/session.py +++ b/nose2/session.py @@ -90,7 +90,7 @@ def __init__(self): self.testResult = None self.testLoader = None self.logLevel = logging.WARN - self.configCache = dict() + self.configCache = {} def get(self, section): """Get a config section. diff --git a/nose2/sphinxext.py b/nose2/sphinxext.py index dacf100a..0c61dbf2 100644 --- a/nose2/sphinxext.py +++ b/nose2/sphinxext.py @@ -119,8 +119,8 @@ def add_config(self, rst, config, plugin): info = config.vars[var] rst.append(".. rst:configvar :: %s" % var, AD) rst.append(" ", AD) - rst.append(" :Default: %(default)s" % info, AD) - rst.append(" :Type: %(type)s" % info, AD) + rst.append(" :Default: {default}".format(**info), AD) + rst.append(" :Type: {type}".format(**info), AD) rst.append("", AD) self.headline(rst, "Sample configuration", "-") diff --git a/nose2/tests/functional/support/scenario/pretty_asserts/ignore_passing/test_prettyassert_ignore_passing.py b/nose2/tests/functional/support/scenario/pretty_asserts/ignore_passing/test_prettyassert_ignore_passing.py index 38f27766..99e3f4ad 100644 --- a/nose2/tests/functional/support/scenario/pretty_asserts/ignore_passing/test_prettyassert_ignore_passing.py +++ b/nose2/tests/functional/support/scenario/pretty_asserts/ignore_passing/test_prettyassert_ignore_passing.py @@ -6,7 +6,7 @@ def test_failing_assert(self): x = True y = False # fmt: off - # flake8: noqa + # ruff: noqa assert x; assert y # fmt: on diff --git a/nose2/tests/unit/test_junitxml.py b/nose2/tests/unit/test_junitxml.py index dd9e5148..63bd3d6e 100644 --- a/nose2/tests/unit/test_junitxml.py +++ b/nose2/tests/unit/test_junitxml.py @@ -90,7 +90,7 @@ def test_fail(self): assert False def test_err(self): - 1 / 0 + 1 / 0 # noqa: B018 useless expression def test_skip(self): self.skipTest("skip") diff --git a/nose2/tests/unit/test_layers_plugin.py b/nose2/tests/unit/test_layers_plugin.py index 74883aea..d24a180b 100644 --- a/nose2/tests/unit/test_layers_plugin.py +++ b/nose2/tests/unit/test_layers_plugin.py @@ -424,7 +424,7 @@ def test(self): self.plugin.startTestRun(event) def names(self, suite): - return [n for n in self.iternames(suite)] + return list(self.iternames(suite)) def iternames(self, suite): for t in suite: @@ -434,7 +434,7 @@ def iternames(self, suite): test_method = t._testMethodName yield f"{test_method} ({test_module}.{test_class})" else: - yield [n for n in self.iternames(t)] + yield list(self.iternames(t)) def _listset(self, lst): n = set() diff --git a/nose2/tests/unit/test_testid_plugin.py b/nose2/tests/unit/test_testid_plugin.py index a192e9a8..29e78ed6 100644 --- a/nose2/tests/unit/test_testid_plugin.py +++ b/nose2/tests/unit/test_testid_plugin.py @@ -50,8 +50,8 @@ def test___init__(self): self.assertEqual( val, exp_val, - "Attribute %s should have value " - "'%s', but has value %s" % (name, exp_val, val), + "Attribute {} should have value " + "'{}', but has value {}".format(name, exp_val, val), ) def test_start_test(self): diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..e7484461 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,80 @@ +[tool.ruff] +select = [ + "B", # flake8-bugbear + "C4", # flake8-comprehensions + "C90", # McCabe cyclomatic complexity + "E", # pycodestyle + "EXE", # flake8-executable + "F", # Pyflakes + "I", # isort + "ICN", # flake8-import-conventions + "INT", # flake8-gettext + "PLE", # Pylint errors + "PLR091", # Pylint Refactor just for max-args, max-branches, etc. + "PYI", # flake8-pyi + "RSE", # flake8-raise + "RUF", # Ruff-specific rules + "TCH", # flake8-type-checking + "TID", # flake8-tidy-imports + "UP", # pyupgrade + "W", # pycodestyle + "YTT", # flake8-2020 + # "A", # flake8-builtins + # "ANN", # flake8-annotations + # "ARG", # flake8-unused-arguments + # "BLE", # flake8-blind-except + # "COM", # flake8-commas + # "D", # pydocstyle + # "DJ", # flake8-django + # "DTZ", # flake8-datetimez + # "EM", # flake8-errmsg + # "ERA", # eradicate + # "FBT", # flake8-boolean-trap + # "G", # flake8-logging-format + # "INP", # flake8-no-pep420 + # "ISC", # flake8-implicit-str-concat + # "N", # pep8-naming + # "NPY", # NumPy-specific rules + # "PD", # pandas-vet + # "PGH", # pygrep-hooks + # "PIE", # flake8-pie + # "PT", # flake8-pytest-style + # "PTH", # flake8-use-pathlib + # "Q", # flake8-quotes + # "RET", # flake8-return + # "S", # flake8-bandit + # "SIM", # flake8-simplify + # "SLF", # flake8-self + # "T10", # flake8-debugger + # "T20", # flake8-print + # "TRY", # tryceratops +] +ignore = [ + "B904", + "RUF005", + "RUF100" +] +target-version = "py37" + +[tool.ruff.isort] +known-third-party = [ + "coverage", + "mock", + "six", +] + +[tool.ruff.mccabe] +max-complexity = 15 + +[tool.ruff.pylint] +max-args = 9 +max-branches = 15 +max-statements = 66 + +[tool.ruff.per-file-ignores] +"nose2/tests/*" = ["B011"] +# FIXME: resolving these could change behavior -- do it in a separate +# cleanup and note it in the changelog +"nose2/plugins/loader/*.py" = ["B004"] +"nose2/plugins/mp.py" = ["UP031"] +"nose2/tests/functional/support/such/test_regression_same_havings.py" = ["EXE001"] diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index ae7cbd20..00000000 --- a/setup.cfg +++ /dev/null @@ -1,15 +0,0 @@ -[isort] -profile = black -known_third_party=six,coverage,mock - -[flake8] # black and isort compatible rules -extend-ignore = E203 -max-line-length = 88 -per-file-ignores = - # allow use of `assert False` in tests -- it's fine - nose2/tests/*.py:B011 - # FIXME: resolving these could change behavior -- do it in a separate - # cleanup and note it in the changelog - nose2/plugins/loader/*.py:B004 -# proposed, but not yet accepted: max-complexity -# max-complexity = 17