Skip to content

Commit

Permalink
Add support for the ruff linter and formatter
Browse files Browse the repository at this point in the history
This is a new linter that is gaining in popularity quickly, let's add a
predefined step for it.

This also now sets CLICOLOR_FORCE=1 when color is requested, as this is
what ruff listens for, and is a standard for quite a few tools
  • Loading branch information
BenjaminSchubert committed Dec 30, 2023
1 parent 4579155 commit 23c06cf
Show file tree
Hide file tree
Showing 13 changed files with 177 additions and 12 deletions.
1 change: 1 addition & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@
.. _mypy: https://mypy.readthedocs.io/en/stable/
.. _pylint: https://pylint.pycqa.org/en/latest/
.. _pytest: https://docs.pytest.org/en/stable/
.. _ruff: https://docs.astral.sh/ruff/
.. _sphinx: https://www.sphinx-doc.org/
.. _twine: https://twine.readthedocs.io/en/stable/
.. _the Unimport formatter: https://unimport.hakancelik.dev/
Expand Down
26 changes: 24 additions & 2 deletions dwasfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,27 @@
requires=["isort:fix", "docformatter:fix"],
run_by_default=False,
)
dwas.register_managed_step(
dwas.predefined.ruff(
files=PYTHON_FILES,
additional_arguments=["check", "--fix", "--show-fixes", "--fix-only"],
),
dependencies=["ruff"],
python=OLDEST_SUPPORTED_PYTHON,
name="ruff:fix",
requires=["black:fix"],
run_by_default=False,
)
dwas.register_step_group(
name="fix",
description="Fix all auto-fixable issues on the project",
requires=["unimport:fix", "isort:fix", "docformatter:fix", "black:fix"],
requires=[
"unimport:fix",
"isort:fix",
"docformatter:fix",
"black:fix",
"ruff:fix",
],
run_by_default=False,
)

Expand Down Expand Up @@ -106,7 +123,12 @@
],
python=OLDEST_SUPPORTED_PYTHON,
)
dwas.register_step_group("lint", ["mypy", "pylint"])
dwas.register_managed_step(
dwas.predefined.ruff(files=PYTHON_FILES),
dependencies=["ruff"],
python=OLDEST_SUPPORTED_PYTHON,
)
dwas.register_step_group("lint", ["mypy", "pylint", "ruff"])

##
# Packaging
Expand Down
6 changes: 6 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,12 @@ disable = [
[tool.pylint.variables]
"init-import" = true

##
# Ruff
[tool.ruff]
target-version = "py38"



##
# Testing
Expand Down
1 change: 1 addition & 0 deletions src/dwas/_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ def __init__(
if self.colors:
self.environ["PY_COLORS"] = "1"
self.environ["FORCE_COLOR"] = "1"
self.environ["CLICOLOR_FORCE"] = "1"
else:
self.environ["PY_COLORS"] = "0"
self.environ["NO_COLOR"] = "0"
Expand Down
2 changes: 2 additions & 0 deletions src/dwas/predefined/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from ._package import package
from ._pylint import pylint
from ._pytest import pytest
from ._ruff import ruff
from ._sphinx import sphinx
from ._twine import twine
from ._unimport import unimport
Expand All @@ -35,6 +36,7 @@
"package",
"pylint",
"pytest",
"ruff",
"sphinx",
"twine",
"unimport",
Expand Down
89 changes: 89 additions & 0 deletions src/dwas/predefined/_ruff.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
from typing import List, Optional, Sequence

# XXX: All imports here should be done from the top level. If we need it,
# users might need it
from .. import Step, StepRunner, build_parameters, set_defaults


@set_defaults(
{
"dependencies": ["ruff"],
"files": ["."],
"additional_arguments": ["check"],
}
)
class Ruff(Step):
def __init__(self) -> None:
self.__name__ = "ruff"

def __call__(
self,
step: StepRunner,
files: Sequence[str],
additional_arguments: List[str],
) -> None:
step.run(
["ruff", *additional_arguments, *files],
env={"RUFF_CACHE_DIR": str(step.cache_path / "ruff-cache")},
)


def ruff(
*,
files: Optional[Sequence[str]] = None,
additional_arguments: Optional[List[str]] = None,
) -> Step:
"""
Run `Ruff`_ against your python source code.
By default, it will depend on :python:`["ruff"]`, when registered with
:py:func:`dwas.register_managed_step`.
:param files: The list of files or directories to run ``ruff`` against.
Defaults to :python:`["."]`.
:param additional_arguments: Additional arguments to pass to the ``ruff``
invocation. Defaults to :python:`["check"]`.
Defaults to :python:`["--check", "--diff", "-W1"]`.
:return: The step so that you can add additional parameters to it if needed.
:Examples:
In order to verify your code but not change it, for a step
named **ruff**:
.. code-block::
register_managed_step(dwas.predefined.ruff())
Or, in order to automatically fix your code, but only if requested:
.. code-block::
register_managed_step(
dwas.predefined.ruff(additional_arguments=["check", "--fix"]),
# NOTE: this name is arbitrary, you could omit it, or specify
# something else. We suffix in our documentation all
# operations that will have destructive effect on the source
# code by ``:fix``
name="ruff:fix",
run_by_default=False,
)
Similarly, if you want to use ruff to format your code you could do:
.. code-block::
# To check the formatting
register_managed_step(
dwas.predefined.ruff(additional_arguments=["format", "--diff"]),
name="ruff:format-check",
)
# To autoformat
register_managed_step(
dwas.predefined.ruff(additional_arguments=["format"]),
name="ruff:format",
)
"""
return build_parameters(
files=files, additional_arguments=additional_arguments
)(Ruff())
2 changes: 1 addition & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
# pylint: disable=wrong-import-position
pytest.register_assert_rewrite("tests.predefined.mixins", "tests._utils")

from ._utils import isolated_context
from ._utils import isolated_context # noqa: E402


def pytest_collection_modifyitems(items):
Expand Down
2 changes: 1 addition & 1 deletion tests/predefined/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ def test_respects_color_settings(
assert not COLOR_ESCAPE_CODE.search(result.stdout)


class BaseFormatterTest(BaseLinterTest):
class BaseLinterWithAutofixTest(BaseLinterTest):
@property
@abstractmethod
def autofix_step(self) -> str:
Expand Down
4 changes: 2 additions & 2 deletions tests/predefined/test_black.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from .mixins import BaseFormatterTest
from .mixins import BaseLinterWithAutofixTest


class TestBlack(BaseFormatterTest):
class TestBlack(BaseLinterWithAutofixTest):
dwasfile = """\
from dwas import register_managed_step
from dwas.predefined import black
Expand Down
4 changes: 2 additions & 2 deletions tests/predefined/test_docformatter.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import pytest

from .mixins import BaseFormatterTest
from .mixins import BaseLinterWithAutofixTest


class TestDocformatter(BaseFormatterTest):
class TestDocformatter(BaseLinterWithAutofixTest):
dwasfile = """\
from dwas import register_managed_step
from dwas.predefined import docformatter
Expand Down
4 changes: 2 additions & 2 deletions tests/predefined/test_isort.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from .mixins import BaseFormatterTest
from .mixins import BaseLinterWithAutofixTest


class TestIsort(BaseFormatterTest):
class TestIsort(BaseLinterWithAutofixTest):
dwasfile = """\
from dwas import register_managed_step
from dwas.predefined import isort
Expand Down
44 changes: 44 additions & 0 deletions tests/predefined/test_ruff.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import pytest

from .mixins import BaseLinterWithAutofixTest


class TestRuffCheck(BaseLinterWithAutofixTest):
dwasfile = """\
from dwas import register_managed_step
from dwas.predefined import ruff
register_managed_step(ruff())
register_managed_step(
ruff(additional_arguments=["check", "--fix"]),
name="ruff:fix",
run_by_default=False,
)
"""
invalid_file = """\
from pathlib import Path
import os
"""
valid_file = '"""This is a token file"""\n'
autofix_step = "ruff:fix"


class TestRuffFormat(BaseLinterWithAutofixTest):
dwasfile = """\
from dwas import register_managed_step
from dwas.predefined import ruff
register_managed_step(ruff(additional_arguments=["format", "--diff"]))
register_managed_step(
ruff(additional_arguments=["format"]),
name="ruff:fix",
run_by_default=False,
)
"""
autofix_step = "ruff:fix"
invalid_file = "x = 1"
valid_file = "x = 1\n"

@pytest.mark.skip("ruff format does not support colored output")
def test_respects_color_settings(self):
pass # pragma: nocover
4 changes: 2 additions & 2 deletions tests/predefined/test_unimport.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from .mixins import BaseFormatterTest
from .mixins import BaseLinterWithAutofixTest


class TestUnimport(BaseFormatterTest):
class TestUnimport(BaseLinterWithAutofixTest):
dwasfile = """\
from dwas import register_managed_step
from dwas.predefined import unimport
Expand Down

0 comments on commit 23c06cf

Please sign in to comment.