Skip to content

RUF010 has false positives and false negatives #16325

Open
@dscorbett

Description

@dscorbett

Description

explicit-f-string-type-conversion (RUF010) has many errors in Ruff 0.9.7.

If the argument to ascii, repr, or str is a comprehension or display for a set or dictionary, the rule does not report it.

$ cat >ruf010_1.py <<'# EOF'
print(f"{ascii({})}")
# EOF

$ ruff --isolated check --select RUF010 ruf010_1.py
All checks passed!

That false negative is intentional, to fix #5530, but a better solution is to add a space as ruff format does.

print(f"{ {}!a}")

Skipping those comprehensions and displays does not fix that problem anyway. If the argument starts with one but continues further, the rule introduces a syntax error.

$ cat >ruf010_2.py <<'# EOF'
print(f"{ascii({} | {})}")
# EOF

$ ruff --isolated check --select RUF010 ruf010_2.py --diff 2>&1 | grep error:
error: Fix introduced a syntax error. Reverting all changes.

The fix is suppressed when the conversion function expression is not exactly ascii, repr, or str. Since the rule is able to detect equivalent expressions it should be able to fix them.

$ cat >ruf010_3.py <<'# EOF'
import builtins
print(f"{builtins.ascii(1)}")
print(f"{𝑎𝑠𝑐𝑖𝑖(1)}")
# EOF

$ ruff --isolated check --select RUF010 ruf010_3.py --output-format concise
ruf010_3.py:2:10: RUF010 Use explicit conversion flag
ruf010_3.py:3:10: RUF010 Use explicit conversion flag
Found 2 errors.

The fix for an expression followed by an equals sign is not safe, because it changes behavior. In that specific case, the fix should be marked unsafe or should be suppressed.

$ cat >ruf010_4.py <<'# EOF'
print(f"{ascii(1)=}")
# EOF

$ python ruf010_4.py
ascii(1)='1'

$ ruff --isolated check --select RUF010 ruf010_4.py --fix
Found 1 error (1 fixed, 0 remaining).

$ cat ruf010_4.py
print(f"{1=!a}")

$ python ruf010_4.py
1=1

RUF010 introduces a syntax error when the argument is an unparenthesized lambda expression. The fix should add parentheses around the lambda expression.

$ cat >ruf010_5.py <<'# EOF'
print(f"{ascii(lambda: 1)}")
# EOF

$ ruff --isolated check --select RUF010 ruf010_5.py --diff 2>&1 | grep error:
error: Fix introduced a syntax error. Reverting all changes.

When the argument is an unparenthesized assignment expression, the fix misinterprets the colon of the := as the sigil of a format specifier. The fix should add parentheses around the assignment expression.

$ cat >ruf010_6.py <<'# EOF'
x = 1
print(f"{ascii(x := 2)}")
# EOF

$ python ruf010_6.py
2

$ ruff --isolated check --select RUF010 ruf010_6.py --fix
Found 1 error (1 fixed, 0 remaining).

$ python ruf010_6.py
Traceback (most recent call last):
  File "ruf010_6.py", line 2, in <module>
    print(f"{x := 2!a}")
            ^^^^^^^^^^
ValueError: Invalid format specifier '= 2!a' for object of type 'int'

The fix deletes the star from a starred expression. RUF010 should be suppressed in that context.

$ cat >ruf010_7.py <<'# EOF'
args = (b"\xa1", "latin-1")
print(f"{str(*args)}")
# EOF

$ python ruf010_7.py
¡

$ ruff --isolated check --select RUF010 ruf010_7.py --fix
Found 1 error (1 fixed, 0 remaining).

$ cat ruf010_7.py 
args = (b"\xa1", "latin-1")
print(f"{args!s}")

$ python ruf010_7.py
(b'\xa1', 'latin-1')

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions