# Alternative formatters for generated code, like \`ruff format\`?

**URL:** <https://forum.djangoproject.com/t/alternative-formatters-for-generated-code-like-ruff-format/26127>\
**Category:** Django Internals\
**Created:** [December 13, 2023, 10:55am UTC](https://forum.djangoproject.com/t/alternative-formatters-for-generated-code-like-ruff-format/26127 "2023-12-13T10:55:20Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![ddoroshev](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/ddoroshev/32/17757_2.png) [@ddoroshev](https://forum.djangoproject.com/u/ddoroshev)\
**Post date:** [December 13, 2023, 10:55am UTC](https://forum.djangoproject.com/t/alternative-formatters-for-generated-code-like-ruff-format/26127/1 "2023-12-13T10:55:20Z")

</div>

Hello everyone,

I was wondering if there’s been any discussion about supporting different code formatters in Django, besides `black`?

For instance, [`ruff format`](https://docs.astral.sh/ruff/formatter/) seems to be a good and quick alternative lately. Maybe [these functions in Django](https://github.com/django/django/blob/ed656106537fda0b0ad7e3de1e960817fc9561a5/django/core/management/utils.py#L160-L175) could be updated to support more than just `black_path` in the future?

I know that if you prefer a different formatter than `black`, you can use it in a pre-commit hook. However, having `black` run automatically on `makemigrations` has been really helpful for me and I’ve appreciated it. Also, the function names end with `-s` (like find\_formatter **s** , run\_formatter **s** ), suggesting the possibility of supporting multiple formatters.

What are your thoughts on this?

---

<div class="post-metadata">

**Author:** ![carltongibson](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/carltongibson/32/267_2.png) [@carltongibson](https://forum.djangoproject.com/u/carltongibson)\
**Post date:** [December 13, 2023, 11:14am UTC](https://forum.djangoproject.com/t/alternative-formatters-for-generated-code-like-ruff-format/26127/2 "2023-12-13T11:14:41Z")

</div>

If I wanted to use ruff here I’d write a wrapper script (shim) called `black` and put that on my path.

It’s a bit sad if we do need to add options to Django here. It took something like 3years to get Black agreed to and in, and the promise was “No more discussions about formatting”. Having to handle implementation fragmentation seems quite a lot like the old can of worms. So, I guess I’d see how far we could lean into _“Just use a shim”_ before wanting to add options here.

The other approach would be _all the options_, but that adds a lot of complexity to Django, largely duplicating what’s already possible with shell tools. 🤔

---

<div class="post-metadata">

**Author:** ![adamchainz](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/adamchainz/32/26_2.png) [@adamchainz](https://forum.djangoproject.com/u/adamchainz)\
**Post date:** [December 13, 2023, 4:01pm UTC](https://forum.djangoproject.com/t/alternative-formatters-for-generated-code-like-ruff-format/26127/3 "2023-12-13T16:01:53Z")

</div>

I agree with Carlton. I would hope ruff provides such a shim as an installable package.

---

<div class="post-metadata">

**Author:** ![ddoroshev](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/ddoroshev/32/17757_2.png) [@ddoroshev](https://forum.djangoproject.com/u/ddoroshev)\
**Post date:** [December 14, 2023, 11:18am UTC](https://forum.djangoproject.com/t/alternative-formatters-for-generated-code-like-ruff-format/26127/4 "2023-12-14T11:18:28Z")

</div>

> [@carltongibson](#):
>
> If I wanted to use ruff here I’d write a wrapper script (shim) called `black` and put that on my path.

Thanks for this interesting workaround. I hadn’t considered that.

> [@carltongibson](#):
>
> It’s a bit sad if we do need to add options to Django here.

I completely understand that from a Django maintainer’s perspective, uniformity is key. However, for a regular developer working on multiple Django projects, each with different formatting requirements, the situation becomes more challenging. Dealing with shims or [manipulating the `PATH`](https://docs.djangoproject.com/en/5.0/ref/django-admin/#black-formatting) could become cumbersome.

> [@carltongibson](#):
>
> the promise was “No more discussions about formatting”

That’s an interesting point. I might have missed something in the DEPs and related discussions, but could you provide some context regarding this promise? Was it specifically about the formatting of the Django codebase, or did it also encompass “user space”?

> [@adamchainz](#):
>
> I would hope ruff provides such a shim as an installable package.

That would be ideal. However, there’s an issue. The current implementation in Django hardcodes the `black --fast` command. Since `ruff format` doesn’t have a `--fast` option (it’s designed to be fast by default), any shim would need to either adapt to or ignore this parameter. So I doubt that such a shim will ever be provided by the `ruff` maintainers themselves.

> [@carltongibson](#):
>
> The other approach would be _all the options_, but that adds a lot of complexity to Django.

In response, I’d be satisfied with a simple `FORMATTER` setting, `"black --fast"` by default, which could be executed as is. It could be also overridden in a hypothetical `pyproject.toml` section:

```toml
[tool.django]
formatter = "ruff format"

```

Or in `settings.py`:

```python
FORMATTER = "ruff format"

```

These options might deviate from the current ideology of tweaking Django’s setup. However, as far as I can see, environment variables are [utilized](https://grep.app/search?q=os.environ%28%5C.get%7C%5C%5B%29&regexp=true&filter%5Brepo%5D%5B0%5D=django/django&filter%5Bpath%5D%5B0%5D=django/) in some parts of the codebase to adjust the behavior of CLI commands, so having it in a `.env` file or passing it inline (`FORMATTER="ruff format" ./manage.py makemigrations`) seems legitimate.

---

<div class="post-metadata">

**Author:** ![adamchainz](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/adamchainz/32/26_2.png) [@adamchainz](https://forum.djangoproject.com/u/adamchainz)\
**Post date:** [December 14, 2023, 10:09pm UTC](https://forum.djangoproject.com/t/alternative-formatters-for-generated-code-like-ruff-format/26127/5 "2023-12-14T22:09:37Z")

</div>

It’s only a nicety that Django calls Black to format certain files. The first way to run a formatter is always via global execution with e.g. pre-commit, and maybe an editor integration. I think we’d only want to consider any way of supporting `ruff format` when it’s not alpha and there’s clear community demand.

> [@ddoroshev](#):
>
> So I doubt that such a shim will ever be provided by the `ruff` maintainers themselves.

Did you ask? Also sounds like quite a small thing, you could try making it and offering for adoption.

> [@ddoroshev](#):
>
> It could be also overridden in a hypothetical `pyproject.toml` section:

Django does not read `pyproject.toml` and I wouldn’t like it to start doing so.

---

<div class="post-metadata">

**Author:** ![ddoroshev](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/ddoroshev/32/17757_2.png) [@ddoroshev](https://forum.djangoproject.com/u/ddoroshev)\
**Post date:** [December 15, 2023, 7:05pm UTC](https://forum.djangoproject.com/t/alternative-formatters-for-generated-code-like-ruff-format/26127/6 "2023-12-15T19:05:34Z")

</div>

> [@adamchainz](#):
>
> I think we’d only want to consider any way of supporting `ruff format` when it’s not alpha and there’s clear community demand.

Makes sense. There’s definitely not much demand for `ruff format` right now.  
Actually, I started thinking about this after setting up a new project and encountering a failure with `./manage.py makemigrations` due to a broken `black` shim in my global environment, linked to a no longer existing Python 3.7 installation. Of course, it’s a problem of my local environment, but that was the point when “nicety” became a “nasty default side effect”.

> [@adamchainz](#):
>
> Did you ask?

To be honest, no. I thought about raising a question about that shim, but then I found [this discussion](https://github.com/astral-sh/ruff/discussions/8949) about [Alembic post-write hooks](https://alembic.sqlalchemy.org/en/latest/autogenerate.html#basic-post-processor-configuration). Learning about this approach made me reconsider my initial thought about requesting a shim for `ruff`.

Actually, the reason I brought up this topic is because of the `find_formatters`/`run_formatters` naming in the codebase, which seems to suggest the possibility of extensions. (Otherwise, they might have been named `find_black`/`run_black`, right? 😄)

So, if we imagine a future where `ruff format` becomes popular among Django developers, how do you think Django might adapt to this change? For example, could the `run_formatters` function be altered to support multiple formatters, evolving from its current form:

```python
def run_formatters(written_files, black_path=(sentinel := object())):
    ...

```

to something like this:

```python
def run_formatters(
    written_files,
    black_path=(sentinel := object()),
    ruff_format_path=(sentinel := object()),
):
    ...

```

with various `if` statements to manage the different formatters? Or perhaps Django could adopt a method similar to Alembic, not running any formatter by default but allowing developers to define their own hooks?

---

<div class="post-metadata">

**Author:** ![adamchainz](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/adamchainz/32/26_2.png) [@adamchainz](https://forum.djangoproject.com/u/adamchainz)\
**Post date:** [December 17, 2023, 1:48pm UTC](https://forum.djangoproject.com/t/alternative-formatters-for-generated-code-like-ruff-format/26127/7 "2023-12-17T13:48:09Z")

</div>

I think it would be built in support or nothing. Adding a hook when there are two practical options seems like overkill.

I would say let’s see how popular ruff’s formatter ends up being, and when it is considered stable. We only adopted black in Django after its years of “beta” status, the same may happen here.

---

<div class="post-metadata">

**Author:** ![washeck](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/washeck/32/700_2.png) [@washeck](https://forum.djangoproject.com/u/washeck)\
**Post date:** [February 28, 2024, 8:38pm UTC](https://forum.djangoproject.com/t/alternative-formatters-for-generated-code-like-ruff-format/26127/8 "2024-02-28T20:38:26Z")

</div>

Hi, I’d like to reopen this topic. I don’t have any numbers on ruff formatter adoption, but it seems to me there are projects switching from black to ruff and having builtin support in Dajngo makes sense to me.

---

<div class="post-metadata">

**Author:** ![shangxiao](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/shangxiao/32/10313_2.png) [@shangxiao](https://forum.djangoproject.com/u/shangxiao)\
**Post date:** [February 29, 2024, 12:00am UTC](https://forum.djangoproject.com/t/alternative-formatters-for-generated-code-like-ruff-format/26127/9 "2024-02-29T00:00:58Z")

</div>

> [@carltongibson](#):
>
> If I wanted to use ruff here I’d write a wrapper script (shim) called `black` and put that on my path.

Don’t even need a wrapper script, just add an alias to your shell rc file `alias black=ruff` 🤔

---

<div class="post-metadata">

**Author:** ![washeck](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/washeck/32/700_2.png) [@washeck](https://forum.djangoproject.com/u/washeck)\
**Post date:** [February 29, 2024, 10:34am UTC](https://forum.djangoproject.com/t/alternative-formatters-for-generated-code-like-ruff-format/26127/10 "2024-02-29T10:34:09Z")

</div>

Django searches for `ruff` with `shutil.which` and I’m not sure it works with alias. More importantly, django calls `black` with `--fast` option not supported by `ruff format`, so the wrapper needs to remove this from args. This is the path we are going in our project but I find it quite strange to force people to have such a shim in their path.

---

<div class="post-metadata">

**Author:** ![adamchainz](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/adamchainz/32/26_2.png) [@adamchainz](https://forum.djangoproject.com/u/adamchainz)\
**Post date:** [February 29, 2024, 11:42am UTC](https://forum.djangoproject.com/t/alternative-formatters-for-generated-code-like-ruff-format/26127/11 "2024-02-29T11:42:13Z")

</div>

There’s no forcing going on here. You want to use a feature built for Black with an alternative, beta formatter.

Despite Riff’s growing popularity, I think we should at least wait until it leaves beta. Django’s slow and steady release cycle means we can’t quickly adapt to, say, a change in its CLI.

---

<div class="post-metadata">

**Author:** ![washeck](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/washeck/32/700_2.png) [@washeck](https://forum.djangoproject.com/u/washeck)\
**Post date:** [February 29, 2024, 12:20pm UTC](https://forum.djangoproject.com/t/alternative-formatters-for-generated-code-like-ruff-format/26127/12 "2024-02-29T12:20:22Z")

</div>

Sorry, I didn’t want to sound rude when using the word “force”. Anyway, ruff 0.3, soon to be released, is removing the “beta” label, see [Ruff v0.3 by MichaReiser · Pull Request #10151 · astral-sh/ruff · GitHub](https://github.com/astral-sh/ruff/pull/10151)

While I understand the conservative approach of Django (and you are right about the CLI stability), it feels to me `ruff` is a tool that makes python ecosystem so much better that it deserves first-class support (I’m so glad we got rid of the complexity of `flake8`, `isort`, `pyflake` and I don’t even remember all the tools and the slowness of them).

---

<div class="post-metadata">

**Author:** ![carltongibson](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/carltongibson/32/267_2.png) [@carltongibson](https://forum.djangoproject.com/u/carltongibson)\
**Post date:** [March 2, 2024, 9:48am UTC](https://forum.djangoproject.com/t/alternative-formatters-for-generated-code-like-ruff-format/26127/13 "2024-03-02T09:48:51Z")

</div>

I would still rather Django move slowly here and use established community tools.

Ruff is quicker. Is that speed difference even noticeable at the end of a `startproject` or `makemigrations` command? It’s not clear it really is. I don’t think _so much better_ is really the case.

---

<div class="post-metadata">

**Author:** ![shangxiao](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/shangxiao/32/10313_2.png) [@shangxiao](https://forum.djangoproject.com/u/shangxiao)\
**Post date:** [May 1, 2024, 6:18am UTC](https://forum.djangoproject.com/t/alternative-formatters-for-generated-code-like-ruff-format/26127/14 "2024-05-01T06:18:46Z")

</div>

I’ve found myself today wishing that `run_formatters()` was extendable:

I tried to see how to get Django to run isort on generated migrations and it looks like the only option was to shim “black” or to monkey patch `run_formatters()` ?

---

<div class="post-metadata">

**Author:** ![carltongibson](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/carltongibson/32/267_2.png) [@carltongibson](https://forum.djangoproject.com/u/carltongibson)\
**Post date:** [May 1, 2024, 7:17am UTC](https://forum.djangoproject.com/t/alternative-formatters-for-generated-code-like-ruff-format/26127/15 "2024-05-01T07:17:53Z")

</div>

Yeah, you’d have to do that currently.

Exposing API there would be a new feature.

---

<div class="post-metadata">

**Author:** ![adamchainz](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/adamchainz/32/26_2.png) [@adamchainz](https://forum.djangoproject.com/u/adamchainz)\
**Post date:** [May 1, 2024, 11:47am UTC](https://forum.djangoproject.com/t/alternative-formatters-for-generated-code-like-ruff-format/26127/16 "2024-05-01T11:47:53Z")

</div>

Yeah. I’d rather we treated this feature as a “nice to have” cleanup of auto-generated code, than a way to run loads of tools to enforce your project’s standards. But I guess a new overridable API is probably the cleanest, most future-proof solution.

---

<div class="post-metadata">

**Author:** ![carltongibson](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/carltongibson/32/267_2.png) [@carltongibson](https://forum.djangoproject.com/u/carltongibson)\
**Post date:** [May 1, 2024, 11:54am UTC](https://forum.djangoproject.com/t/alternative-formatters-for-generated-code-like-ruff-format/26127/17 "2024-05-01T11:54:11Z")

</div>

Yes, it was only added because we moved to Black for Django itself.

I’m half-inclined towards not adding anything here, instead pointing folks to pre-commit and the like, as the appropriate place to run your linters/formatters.

(I don’t feel too strongly either way, but what game are we in, is at least worth thinking about.)

---

<div class="post-metadata">

**Author:** ![apollo13](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/apollo13/32/30_2.png) [@apollo13](https://forum.djangoproject.com/u/apollo13)\
**Post date:** [May 1, 2024, 12:09pm UTC](https://forum.djangoproject.com/t/alternative-formatters-for-generated-code-like-ruff-format/26127/18 "2024-05-01T12:09:06Z")

</div>

I also don’t like the idea of maintaining an API here (I rather rip out black support again). As Carlton said this is something pre-commit would fix for you anyways.

---

<div class="post-metadata">

**Author:** ![shangxiao](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/shangxiao/32/10313_2.png) [@shangxiao](https://forum.djangoproject.com/u/shangxiao)\
**Post date:** [May 2, 2024, 2:54am UTC](https://forum.djangoproject.com/t/alternative-formatters-for-generated-code-like-ruff-format/26127/19 "2024-05-02T02:54:38Z")

</div>

I was almost going to suggest adding an API (whether it be via settings / signal / whatever) but I thought: you know what? monkey patching isn’t so bad here as it’s not a critical feature merely a bolt on to personal dev workflow.

PS: I detest commit hooks 😛

---

<div class="post-metadata">

**Author:** ![adamchainz](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/adamchainz/32/26_2.png) [@adamchainz](https://forum.djangoproject.com/u/adamchainz)\
**Post date:** [May 2, 2024, 8:24pm UTC](https://forum.djangoproject.com/t/alternative-formatters-for-generated-code-like-ruff-format/26127/20 "2024-05-02T20:24:07Z")

</div>

> [@shangxiao](#):
>
> PS: I detest commit hooks 😛

You _can_ use pre-commit as a tool manager, without the commit hook feature. Then you’d run `pre-commit run --all` or similar any time you want to format.

And anyway, there are other options too for managing code quality tools.

> [@carltongibson](#):
>
> I’m half-inclined towards not adding anything here, instead pointing folks to pre-commit and the like, as the appropriate place to run your linters/formatters.

Yeah, I get that too. I would be happy with that world as well. Also, so many devs run formatters in their text editors already, so that’s another path.

[Next page](https://forum.djangoproject.com/t/alternative-formatters-for-generated-code-like-ruff-format/26127.md?page=2)
