Skip to content

Formatting a long f-string requires two passes to reach a stable result #24807

@stephenwade

Description

@stephenwade

Summary

Today, I encountered a case where ruff format needs to be run twice in order to reach a stable result.

Here's the lines that reproduce the issue:

import os
import subprocess

def clean_unnecessary_objects(objects, dest_path, keep_dot_git):
    objects_to_remove = set([".git"]) if objects == "all" else set(os.listdir(dest_path)) - set(objects)
    subprocess.check_call(f"rm -rf {' '.join(map(lambda object_name: os.path.join(dest_path, object_name), objects_to_remove))}", shell=True)

After running ruff format test.py, the file looks like this:

import os
import subprocess


def clean_unnecessary_objects(objects, dest_path, keep_dot_git):
    objects_to_remove = (
        set([".git"]) if objects == "all" else set(os.listdir(dest_path)) - set(objects)
    )
    subprocess.check_call(
        f"rm -rf {' '.join(map(lambda object_name: os.path.join(
                    dest_path, object_name
                ), objects_to_remove))}",
        shell=True,
    )

After running ruff format --no-cache test.py (to force the formatter to run again on the same file), the file looks like this:

import os
import subprocess


def clean_unnecessary_objects(objects, dest_path, keep_dot_git):
    objects_to_remove = (
        set([".git"]) if objects == "all" else set(os.listdir(dest_path)) - set(objects)
    )
    subprocess.check_call(
        f"rm -rf {
            ' '.join(
                map(
                    lambda object_name: os.path.join(dest_path, object_name),
                    objects_to_remove,
                )
            )
        }",
        shell=True,
    )

Further runs of ruff format --no-cache test.py don't change the file.

My .ruff.toml config is very simple:

target-version = "py312"

[format]
line-ending = "lf"

Playground links:

Version

ruff 0.15.11

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingformatterRelated to the formattergreat writeupA wonderful example of a quality contribution

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions