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

Hello everyone,

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

For instance, ruff format seems to be a good and quick alternative lately. Maybe these functions in Django 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_formatters, run_formatters), suggesting the possibility of supporting multiple formatters.

What are your thoughts on this?

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. :thinking:

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

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

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 could become cumbersome.

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”?

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.

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:

[tool.django]
formatter = "ruff format"

Or in settings.py:

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 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.

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.

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

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

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”.

To be honest, no. I thought about raising a question about that shim, but then I found this discussion about Alembic post-write hooks. 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? :smile:)

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:

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

to something like this:

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?

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.