Skip to content
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

Use Ruff for formatting #87

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
47 changes: 47 additions & 0 deletions docs/decisions/0005-ruff-formatting.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# 0000 Architectural Decision Record Template

- Date: 2023-11-09
- Author(s): [Jonathan Moss][jmoss]
- Status: `Active`

## Decision

Dropping [black][black] and [isort][isort] in favour of [ruff][ruff]

## Context

We have run a series of experiments to assess the benefits and drawbacks of adopting
the tool `ruff` in order to replace `black` and `isort`. Whislt we do not have any major
issues with the 2 existing tools, they do tend to get a little slower on large code
bases. Ruff does have a _slightly_ different position on some formatting choices which
can impact existing code bases. This is similar to the impact that adding `black` to an
existing product can have. For new code bases the impact is minimal.

There is also value in reducing the number of tools and therefore moving parts within
our standard development environment. Here we are able to remove: `isort`, `black`,
`flake8-black`, and `flake8-isort` from our list of dependencies in return for adding
the single new dependency `ruff`. Ultimately this results in less dependencies to keep
on top of.

The use of [invoke][invoke] to wrap our common local development workflows, and re-use
them in our github action flows pay dividends here. The adoption of `ruff` for code
formatting and import sorting only required an update to the `format` task for it to be
the default behaviour locally and for our Continuous Integration (CI) checks.

## Implications

Developers will need to ensure that any IDE's they use are re-configured to use `ruff`
instead of `black` and `isort`.

Ruff is primarily a linting tool. We have not chosen to switch away from
[flake8][flake8] for linting yet as this requires a lot more configuration to match our
current setup. However, the opportunity to reduce our development tooling further by
doing this is a future possibility.

<!-- Links -->
[jmoss]: mailto:jonathan.moss@ackama.com
[black]: https://black.readthedocs.io/en/stable/
[isort]: https://pycqa.github.io/isort/
[ruff]: https://docs.astral.sh/ruff/
[invoke]: https://www.pyinvoke.org/
[flake8]: https://flake8.pycqa.org/en/latest/
269 changes: 172 additions & 97 deletions template/poetry.lock

Large diffs are not rendered by default.

8 changes: 1 addition & 7 deletions template/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,14 @@ httptools = "^0.6.4"

[tool.poetry.group.dev.dependencies]
bandit = "^1.8.0"
black = "^24.10.0"
django-stubs = "^5.1.0"
factory-boy = "^3.3.0"
flake8 = "^7.1.0"
flake8-bandit = "^4.1.1"
flake8-black = "^0.3.6"
flake8-bugbear = "^24.12.0"
flake8-builtins = "^2.5.0"
flake8-isort = "^6.1.0"
invoke = "^2.2.0"
ipython = "^8.31.0"
isort = "^5.13.0"
mkdocs = "^1.6.0"
mkdocs-material = "^9.5.0"
mkdocstrings = "^0.27.0"
Expand All @@ -44,6 +40,7 @@ pytest-cov = "^6.0.0"
pytest-django = "^4.9.0"
pytest-playwright = "^0.6.0"
pytest-spec = "^4.0.0"
ruff = "^0.9.8"

[build-system]
build-backend = "poetry.core.masonry.api"
Expand All @@ -60,9 +57,6 @@ module = "environ"
[tool.django-stubs]
django_settings_module = "{{ project_name }}.main.settings"

[tool.isort]
profile = "black"

[tool.pytest.ini_options]
DJANGO_SETTINGS_MODULE = "{{ project_name }}.main.settings"
addopts = "--rootdir src --spec"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@


class Migration(migrations.Migration):

initial = True

dependencies = [
Expand Down
1 change: 0 additions & 1 deletion template/src/project_name/accounts/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,5 @@


class User(AbstractUser):

def __str__(self) -> str:
return self.username
1 change: 1 addition & 0 deletions template/src/project_name/manage.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""

import os
import sys

Expand Down
23 changes: 14 additions & 9 deletions template/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@
# GETTING STARTED #
###################


@invoke.task
def help(ctx):
def help(ctx): # noqa: A001
"""
Displays help text
"""
ctx.run("inv -l", pty=True)


@invoke.task()
def install(ctx, skip_install_playwright: bool = False):
"""
Expand All @@ -37,24 +39,24 @@ def install(ctx, skip_install_playwright: bool = False):
ctx.run("poetry run playwright install --with-deps")



#####################
# QUALITY ASSURANCE #
#####################


@invoke.task
def format(ctx, check: bool = False) -> None:
def format(ctx, check: bool = False) -> None: # noqa: A001
"""
Apply automatic code formatting tools

By default, this modifies files to match coding style guidelines.
When `check` is True, it performs a dry-run to identify non-compliant
files without applying changes.
"""
_title("Applying code formatters ")
ctx.run(f"poetry run black src{' --check' if check else ''}")
ctx.run(f"poetry run isort src{' --check' if check else ''}")
suffix = " (check only)" if check else ""
_title(f"Applying code formatters{suffix}")
ctx.run(f"poetry run ruff format src{' --check' if check else ''}")
ctx.run(f"poetry run ruff check --select I{' --fix' if not check else ''} src")


@invoke.task
Expand Down Expand Up @@ -225,6 +227,7 @@ def build_image(ctx, tag=None, pty=True):
echo=True,
)


@invoke.task
def build_docs(ctx):
"""
Expand Down Expand Up @@ -268,7 +271,7 @@ def run_image(
f"--network {network}",
f"-e DATABASE_URL={database_url}",
f"--env-file {env_file}",
f"{PACKAGE}:{tag}"
f"{PACKAGE}:{tag}",
]

if command:
Expand All @@ -284,6 +287,7 @@ def run_docs(ctx):
"""
ctx.run("poetry run mkdocs serve")


###########
# HELPERS #
###########
Expand All @@ -310,9 +314,10 @@ def tag(self) -> str:

def _build_data(ctx) -> BuildData:
"""
Retrieves the current git branch, commit hash and commit time for using during builds
Retrieves the current git branch, commit hash and time for using during builds

Also provides a `tag` which is suitable for use as a Docker image tag based on these values
Also provides a `tag` which is suitable for use as a Docker image tag based on these
values
"""
max_tag_length = 128
branch = ctx.run("git rev-parse --abbrev-ref HEAD", hide="stdout").stdout.strip()
Expand Down