fixhunt

ruff RUF032

labelled unknown
Runs / 2026-10-17T21-44-36-096Z-gpvl-ruff-RUF032ruff 0.17.12026-10-17 21:44 UTC8 programs × 2 round(s)16 programs, 1 fired, 0 fixed, 0 findings, 0 rejectedmodel cost $0.0038

Analysis Nemotron 3 Super

Flags Decimal() constructor calls where the argument is a floating-point literal.

Fix: Replace the floating-point literal argument with its exact string representation using repr() to preserve the original float's value.

12 hazards
  • If the float argument is a variable (e.g., Decimal(x)), the fix incorrectly replaces it with a string literal, altering semantics and potentially causing TypeError.
  • If the float argument is an expression (e.g., Decimal(1.0 + 2.0)), the fix replaces the entire expression with a string, changing runtime behavior.
  • If the float is a special value like float('nan') or float('inf'), repr() produces strings like 'nan' or 'inf', which Decimal() accepts but may propagate differently in downstream calculations.
  • If the code relies on the exact binary approximation of the float (e.g., for bit-level reproduction), converting to string then to Decimal yields a different rational number than the original float-to-Decimal conversion.
  • If the Decimal call is inside a larger expression (e.g., Decimal(1.2) + 3), the fix only affects the constructor argument, but surrounding code may still behave differently due to altered precision.
  • If the float literal is part of a tuple or list unpacked via *args (e.g., Decimal(*[1.2])), the fix does not apply and Ruff may not flag it, but if it did, rewriting would break unpacking.
  • If the code uses a custom Decimal subclass that overrides __init__ to expect a float, replacing with a string breaks initialization.
  • If the float literal appears in a comment or docstring near the call (e.g., # value: 1.2), the fix does not touch comments, but the semantic change may invalidate the comment's accuracy.
  • If the float is the result of a function call with side effects (e.g., Decimal(get_price())), the fix would incorrectly replace the call with a string, eliminating side effects.
  • If the code runs under a strict typing regime (e.g., mypy) that expects Decimal(float), the rewritten Decimal(str) may trigger false-positive type errors.
  • If the float literal is syntactically identical to an integer literal but typed as float (e.g., Decimal(2.0)), the fix produces '2.0', which is semantically equivalent but may affect code generation tools that distinguish float vs int origins.
  • If the code is parsed by a tool that relies on the AST node type being a constant float, replacing it with a string constant alters the AST and may break downstream tooling.

Programs

idresulttitlehazard
r1-p001silent prediction offVariable as float argumentIf the float argument is a variable (e.g., Decimal(x)), the fix incorrectly replaces it with a string literal, altering semantics and potentially causing TypeError.
r1-p002silent prediction offExpression as float argumentIf the float argument is an expression (e.g., Decimal(1.0 + 2.0)), the fix replaces the entire expression with a string, changing runtime behavior.
r1-p003silent prediction offSpecial float values (inf, nan)If the float is a special value like float('nan') or float('inf'), repr() produces strings like 'nan' or 'inf', which Decimal() accepts but may propagate differently in downstream calculations.
r1-p004silent prediction offInt/float/bool coercion and arithmeticint/float/bool coercion, Decimal, division and rounding
r1-p005silent prediction offKeyword-only and defaultskeyword-only, positional-only, *args/**kwargs forwarding, defaults evaluated once
r1-p006silent prediction offFunction call with side effectsIf the float is the result of a function call with side effects (e.g., Decimal(get_price())), the fix would incorrectly replace the call with a string, eliminating side effects.
r1-p007silentContext managers (non-file)context managers that are not files, objects with write() but no close()
r1-p008silentpathlib vs os.pathpathlib vs os.path behavior on relative paths, symlinks, missing files, bytes paths
r2-p001silent prediction offExpression as float argumentReplaces entire expression with string, changing runtime behavior.
r2-p002fired, no fixAST node type changed from constant float to string constantBreaks downstream tooling that inspects AST nodes expecting float constants.
r2-p003silent prediction offString formatting edge cases: % formatting, f-strings, repr vs strAlters formatting behavior when the Decimal is used in string operations.
r2-p004silent prediction offDict ordering, duplicate keys, __hash__/__eq__ overrides, setsChanges hash/equality behavior affecting dict/set membership and ordering.
r2-p005silent prediction offType annotations evaluated at runtime, typing-only importsAlters runtime evaluation of type annotations involving the Decimal.
r2-p006silent prediction offSpecial float values: inf, nanrepr() produces 'inf'/'nan' strings which Decimal() accepts but may propagate differently.
r2-p007silent prediction offFile modes and truncation, partial writes, non-str data passed to writeChanges behavior when writing Decimal to file due to altered __str__ or type.
r2-p008silent prediction offExceptions raised inside rewritten code, finally/else clauses, exception chainingAlters exception flow when Decimal construction is used in try-except-finally.

Sources

r1-p001 · Variable as float argument
1x = 1.5
2print(type(Decimal(x)))
3# After fix: Decimal(str(1.5)) -> Decimal('1.5')
r1-p002 · Expression as float argument
1result = Decimal(1.0 + 2.0)
2print(result)
3# After fix: Decimal('3.0') instead of Decimal(3.0) -> same value but different internal handling; test via equality with string-made Decimal
4print(result == Decimal('3.0'))
r1-p003 · Special float values (inf, nan)
1import math
2inf_val = float('inf')
3an_val = float('nan')
4dec_inf = Decimal(inf_val)
5dec_nan = Decimal(nan_val)
6print(f'inf comparison: {dec_inf == Decimal("inf")}')
7print(f'nan comparison: {dec_nan != dec_nan}') # NaN != NaN
8# After fix: Decimal('inf'), Decimal('nan') -> same behavior? But test if operations differ
9print(f'inf + 1: {dec_inf + 1}')
10print(f'nan + 1: {dec_nan + 1}')
r1-p004 · Int/float/bool coercion and arithmetic
1a = Decimal(1.0) # triggers
2b = Decimal(2) # does not trigger (int)
3c = Decimal(True) # does not trigger (bool, but 1.0 -> might trigger? bool is subclass of int, but Decimal(True) is allowed)
4# Actually, Decimal(True) is valid and equals Decimal(1). Rule does not fire on bool because it's not a float literal.
5# So adjust: use 1.0 * True -> still 1.0
6val = 1.0 * True
7d = Decimal(val) # triggers because val is float 1.0
8print(f'a: {a}, b: {b}, c: {c}, d: {d}')
9print(f'a == d: {a == d}') # Should be True
10# After fix: d becomes Decimal('1.0') -> same value
11# Test rounding context
12from decimal import getcontext
13getcontext().prec = 2
14e = Decimal(1.0 / 3.0) # triggers
15print(f'e: {e}')
16# After fix: Decimal(str(1.0/3.0)) -> Decimal('0.3333333333333333') vs original Decimal(0.3333333333333333) -> same
r1-p005 · Keyword-only and defaults
1def func(x, *, y=1.0): # y default is float
2 return Decimal(x) + Decimal(y)
3print(func(2)) # uses y=1.0 -> triggers on Decimal(y)
4# After fix: Decimal(str(1.0)) -> Decimal('1.0')
5# But note: default is evaluated once at definition, so replacement would be in the function's bytecode?
6# However, the fix would change the source: Decimal(y) -> Decimal(str(1.0)) only if y is a float literal, but y is a variable.
7# Wait, the rule fires on Decimal(y) only if y is a float literal, which it's not.
8# So this does not trigger. Need to adjust.
9# Instead, make the call have a float literal in keyword argument:
10print(func(2, y=1.0)) # now Decimal(1.0) in keyword argument -> triggers
r1-p006 · Function call with side effects
1side_effect_log = []
2def get_price():
3 side_effect_log.append('called')
4 return 99.9
5try:
6 d = Decimal(get_price()) # triggers
7 print(f'Decimal: {d}, log: {side_effect_log}')
8except Exception as e:
9 print(f'{type(e).__name__}: {e}')
10# After fix: Decimal(str(99.9)) -> side effect log remains empty
NameError: name 'Decimal' is not defined
r1-p007 · Context managers (non-file)
1class LogCtx:
2 def __enter__(self):
3 self.log = []
4 return self
5 def __exit__(self, *exc):
6 pass
7 def write(self, s):
8 self.log.append(s)
9with LogCtx() as ctx:
10 ctx.write('start')
11 x = 1.5
12 d = Decimal(x) # does not trigger because x is variable
13 ctx.write(f'd={d}')
14print(''.join(ctx.log))
r1-p008 · pathlib vs os.path
1import os
2import pathlib
3# Create a temporary file
4pathlib.Path('test.txt').write_text('hello')
5try:
6 # These do not involve Decimal, so rule does not fire
7 p1 = pathlib.Path('test.txt')
8 p2 = os.path.path('test.txt') # AttributeError: module 'os' has no attribute 'path'
9except AttributeError as e:
10 print(f'{type(e).__name__}: {e}')
11finally:
12 try:
13 os.remove('test.txt')
14 except FileNotFoundError:
15 pass
AttributeError: module 'posixpath' has no attribute 'path'
r2-p001 · Expression as float argument
1from decimal import Decimal
2x = 1.0
3y = 2.0
4z = Decimal(x + y)
5print(z)
3
r2-p002 · AST node type changed from constant float to string constant
1import ast
2from decimal import Decimal
3node = ast.parse('Decimal(1.5)', mode='eval')
4const_val = node.body.value
5if isinstance(const_val, ast.Constant):
6 print(f'AST const type: {type(const_val.value)}, value: {const_val.value}')
7else:
8 print('Not a Constant')
9d = Decimal(1.5)
10print(f'Decimal value: {d}')
r2-p003 · String formatting edge cases: % formatting, f-strings, repr vs str
1from decimal import Decimal
2val = Decimal(1.0 + 2.0)
3s1 = '%s' % val
4s2 = f'{val}'
5s3 = str(val)
6s4 = repr(val)
7print(f'%s: {s1!r}, f-string: {s2!r}, str(): {s3!r}, repr(): {s4!r}')
%s: '3', f-string: '3', str(): '3', repr(): "Decimal('3')"
r2-p004 · Dict ordering, duplicate keys, __hash__/__eq__ overrides, sets
1from decimal import Decimal
2d = {}
3d[Decimal(1.0 + 2.0)] = 'first'
4d[Decimal('3.0')] = 'second'
5print(f'Dict length: {len(d)}')
6for k, v in d.items():
7 print(f'key: {k!r} ({type(k)}), value: {v!r}')
8s = {Decimal(1.0 + 2.0), Decimal('3.0')}
9print(f'Set length: {len(s)}')
Dict length: 1
key: Decimal('3') (<class 'decimal.Decimal'>), value: 'second'
Set length: 1
r2-p005 · Type annotations evaluated at runtime, typing-only imports
1from decimal import Decimal
2from typing import get_type_hints
3def func() -> Decimal:
4 return Decimal(1.0 + 2.0)
5hints = get_type_hints(func)
6print(f'Return type hint: {hints.get("return")}')
7val = func()
8print(f'Returned value: {val}')
Return type hint: <class 'decimal.Decimal'>
Returned value: 3
r2-p006 · Special float values: inf, nan
1from decimal import Decimal, InvalidOperation
2try:
3 d1 = Decimal(float('inf'))
4 print(f'inf Decimal: {d1}')
5except Exception as e:
6 print(f'inf error: {type(e).__name__}: {e}')
7try:
8 d2 = Decimal(float('nan'))
9 print(f'nan Decimal: {d2}')
10except Exception as e:
11 print(f'nan error: {type(e).__name__}: {e}')
12print(f'Decimal("Inf"): {Decimal("Inf")}')
13print(f'Decimal("NaN"): {Decimal("NaN")}')
inf Decimal: Infinity
nan Decimal: NaN
Decimal("Inf"): Infinity
Decimal("NaN"): NaN
r2-p007 · File modes and truncation, partial writes, non-str data passed to write
1from decimal import Decimal
2import os
3filename = 'test_output.txt'
4if os.path.exists(filename):
5 os.remove(filename)
6with open(filename, 'w') as f:
7 f.write(str(Decimal(1.0 + 2.0)))
8with open(filename, 'r') as f:
9 content = f.read()
10print(f'File content: {content!r}')
11if os.path.exists(filename):
12 os.remove(filename)
File content: '3'
r2-p008 · Exceptions raised inside rewritten code, finally/else clauses, exception chaining
1from decimal import Decimal
2try:
3 result = Decimal(1.0 + 2.0)
4 print(f'Success: {result}')
5except Exception as e:
6 print(f'Exception: {type(e).__name__}: {e}')
7else:
8 print('Else block executed')
9finally:
10 print('Finally block executed')
Success: 3
Else block executed
Finally block executed