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

FURB116 has false positives and unsafe fixes #16472

Open
dscorbett opened this issue Mar 3, 2025 · 0 comments
Open

FURB116 has false positives and unsafe fixes #16472

dscorbett opened this issue Mar 3, 2025 · 0 comments
Labels
bug Something isn't working fixes Related to suggested fixes for violations help wanted Contributions especially welcome

Comments

@dscorbett
Copy link

Summary

The fix for f-string-number-format (FURB116) only works on non-negative integers. If the argument isn’t an int or bool literal (or a variable that was set to one of those, or any other expression that Ruff can prove is a non-negative integer), the rule should be marked unsafe. The fix can also introduce a syntax error for certain complex expressions that the rule was probably not intended to handle.

The fix changes behavior for negative integers.

$ cat >furb116_1.py <<'# EOF'
x = -1
print(bin(x)[2:])
# EOF

$ python furb116_1.py
b1

$ ruff --isolated check --preview --select FURB116 furb116_1.py --fix
Found 1 error (1 fixed, 0 remaining).

$ cat furb116_1.py
x = -1
print(f"{x:b}")

$ python furb116_1.py
-1

FURB116 applies to all number literals, but float and complex literals are false positives which the rule should ignore.

$ cat >furb116_2.py <<'# EOF'
bin(1.0)[2:]
# EOF

$ python furb116_2.py 2>&1 | tail -n 1
TypeError: 'float' object cannot be interpreted as an integer

$ ruff --isolated check --preview --select FURB116 furb116_2.py --fix
Found 1 error (1 fixed, 0 remaining).

$ python furb116_2.py 2>&1 | tail -n 1
ValueError: Unknown format code 'b' for object of type 'float'

In general, the fix should be marked unsafe, except when it can be proved to be safe. Otherwise, the fix changes behavior, sometimes by changing the error type as in the previous example, and sometimes by changing behavior in other ways.

$ cat >furb116_3.py <<'# EOF'
import datetime
d = datetime.datetime.now(tz=datetime.UTC)
print(bin(d)[2:])
# EOF

$ python furb116_3.py 2>&1 | tail -n 1
TypeError: 'datetime.datetime' object cannot be interpreted as an integer

$ ruff --isolated check --preview --select FURB116 furb116_3.py --fix
Found 1 error (1 fixed, 0 remaining).

$ cat furb116_3.py
import datetime
d = datetime.datetime.now(tz=datetime.UTC)
print(f"{d:b}")

$ python furb116_3.py
b

The fix introduces a syntax error when the argument begins with a brace.

$ cat >furb116_4.py <<'# EOF'
print(bin({0: 1}[0].numerator)[2:])
# EOF

$ python furb116_4.py
1

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

The fix introduces a syntax error in Python 3.11 and earlier when the argument contains the same kind of quotation marks as the fix’s f-string.

$ cat >furb116_5.py <<'# EOF'
print(bin(len("xyz").numerator)[2:])
# EOF

$ python3.11 furb116_5.py
11

$ ruff --isolated check --target-version py311 --preview --select FURB116 furb116_5.py --fix
Found 1 error (1 fixed, 0 remaining).

$ cat furb116_5.py
print(f"{len("xyz").numerator:b}")

$ python3.11 furb116_5.py 2>&1 | tail -n 1
SyntaxError: f-string: unmatched '('

The fix introduces a syntax error in Python 3.11 and earlier when the argument contains a backslash.

$ cat >furb116_6.py <<'# EOF'
print(bin(ord("\\").numerator)[2:])
# EOF

$ python3.11 furb116_6.py
1011100

$ ruff --isolated check --target-version py311 --preview --select FURB116 furb116_6.py --fix
Found 1 error (1 fixed, 0 remaining).

$ cat furb116_6.py
print(f"{ord("\\").numerator:b}")

$ python3.11 furb116_6.py 2>&1 | tail -n 1
SyntaxError: unexpected character after line continuation character

The fix introduces a syntax error in Python 3.11 and earlier when the argument spans multiple lines.

$ cat >furb116_7.py <<'# EOF'
import sys
print(hex(sys
.maxunicode)[2:])
# EOF

$ python3.11 furb116_7.py
10ffff

$ ruff --isolated check --target-version py311 --preview --select FURB116 furb116_7.py --fix
Found 1 error (1 fixed, 0 remaining).

$ cat furb116_7.py
import sys
print(f"{sys
.maxunicode:x}")

$ python3.11 furb116_7.py 2>&1 | tail -n 1
SyntaxError: unterminated string literal (detected at line 2)

Version

ruff 0.9.9 (091d0af 2025-02-28)

@ntBre ntBre added bug Something isn't working fixes Related to suggested fixes for violations help wanted Contributions especially welcome labels Mar 3, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working fixes Related to suggested fixes for violations help wanted Contributions especially welcome
Projects
None yet
Development

No branches or pull requests

2 participants