fixhunt

ruff RUF070

labelled unknown
Runs / 2026-10-17T21-49-01-368Z-odu8-ruff-RUF070ruff 0.17.12026-10-17 21:49 UTC8 programs × 2 round(s)16 programs, 6 fired, 0 fixed, 0 findings, 0 rejectedmodel cost $0.0033

Analysis Nemotron 3 Super

Flags redundant variable assignments directly before yielding the same variable.

Fix: Replace 'x = expr\n yield x' with 'yield expr', preserving indentation and removing the assignment line while keeping all other code unchanged.

12 hazards
  • Removing the assignment eliminates the local variable binding, altering the output of locals() inspection during generator suspension.
  • If the variable is referenced in a debugger or introspection tool at the yield point, the reference will become undefined after the fix.
  • The assignment may contain a side-effect (e.g., incrementing a counter via a property setter) that would be omitted by the fix.
  • If the assigned expression is used elsewhere in the function via closure capture, removing the assignment could break the closure by eliminating the variable reference.
  • The assignment line may carry a trailing comment that would be deleted by the rewrite, altering source documentation.
  • If the variable name is shadowed in an inner scope, removing the outer assignment could change which variable is yielded due to altered resolution.
  • The assignment may be part of a larger statement (e.g., walrus operator) that the rule does not handle, but if misapplied could break syntax.
  • If the expression is a mutable object and the variable is later mutated (though not yielded), the fix does not affect this, but the hazard is misdiagnosis—however, the rule only fires when unused, so this is mitigated; still, if the variable is used in a lambda defined later but not executed, the lambda's closure could break.
  • In rare cases, the assignment might be syntactically indistinguishable from a type annotation in stub files or conditional code, leading to incorrect removal.
  • If the yield is inside a try block and the assignment contains a finally-dependent expression, moving the expression into the yield could change exception timing.
  • The fix may interfere with instrumented code (e.g., coverage tools) that relies on the assignment line being executable.
  • If the assignment uses the walrus operator in a way that creates a variable used only in the yield, the rule may not fire due to current limitations, but if incorrectly extended, it could break.

Programs

idresulttitlehazard
r1-p001silent prediction offMutable object mutation after assignmentIf the expression is a mutable object and the variable is later mutated (though not yielded), the fix does not affect this, but the hazard is misdiagnosis—however, the rule only fires when unused, so
r1-p002fired, no fixProperty descriptor with side effectsclass attributes vs instance attributes, descriptors, properties with side effects
r1-p003silent prediction offClosure capture in nested lambdaIf the assigned expression is used elsewhere in the function via closure capture, removing the assignment could break the closure by eliminating the variable reference.
r1-p004silent prediction offVariable shadowing in inner scopeIf the variable name is shadowed in an inner scope, removing the outer assignment could change which variable is yielded due to altered resolution.
r1-p005fired, no fix prediction offStar-import and __all__ interactionstar-imports, module-level side effects, __all__
r1-p006silent prediction offNumeric coercion and precisionint/float/bool coercion, Decimal, division and rounding
r1-p007silent prediction offLocals() inspection during generator suspensionRemoving the assignment eliminates the local variable binding, altering the output of locals() inspection during generator suspension.
r1-p008fired, no fix prediction offWalrus operator in assignment (near-miss)If the assignment uses the walrus operator in a way that creates a variable used only in the yield, the rule may not fire due to current limitations, but if incorrectly extended, it could break.
r2-p001silent prediction offType annotation confusionIn rare cases, the assignment might be syntactically indistinguishable from a type annotation in stub files or conditional code, leading to incorrect removal.
r2-p002silent prediction offEvaluation order and side effectsEvaluation order and side effects when expressions are merged, duplicated or deleted
r2-p003silentWalrus operator interactionThe assignment may be part of a larger statement (e.g., walrus operator) that the rule does not handle, but if misapplied could break syntax.
r2-p004fired, no fixRuntime type annotationsType annotations evaluated at runtime, typing-only imports
r2-p005fired, no fixClass vs instance attributesClass attributes vs instance attributes, descriptors, properties with side effects
r2-p006fired, no fixException handling interferenceExceptions raised inside the rewritten code, finally/else clauses, exception chaining
r2-p007silent prediction offLocals() inspection during suspensionRemoving the assignment eliminates the local variable binding, altering the output of locals() inspection during generator suspension.
r2-p008silentMisapplied walrus extensionIf the assignment uses the walrus operator in a way that creates a variable used only in the yield, the rule may not fire due to current limitations, but if incorrectly extended, it could break.

Sources

r1-p001 · Mutable object mutation after assignment
1def gen():
2 x = []
3 yield x
4 # Lambda captures x but not executed until after yield
5 f = lambda: len(x)
6 x.append(1) # Mutation after yield
7 yield f() # Should reflect mutation
8
9print(list(gen()))
[[1], 1]
r1-p002 · Property descriptor with side effects
1class A:
2 @property
3 def prop(self):
4 print('prop accessed')
5 return 42
6
7def gen():
8 a = A()
9 x = a.prop # Triggers property access
10 yield x
11 return
12
13list(gen()) # Should print 'prop accessed' once
prop accessed
r1-p003 · Closure capture in nested lambda
1def gen():
2 x = 10
3 yield x
4 # Lambda captures x, called after first yield
5 f = lambda: x * 2
6 yield f() # Should be 20
7
8print(list(gen()))
[10, 20]
r1-p004 · Variable shadowing in inner scope
1def gen():
2 x = 99
3 def inner():
4 x = 88 # Shadows outer x
5 return x
6 yield x # Should yield 99 (outer)
7 yield inner() # Should yield 88
8
9print(list(gen()))
[99, 88]
r1-p005 · Star-import and __all__ interaction
1# This file tests star-import safety
2# We create a temporary module to isolate
3import tempfile
4import os
5
6code = '''
7__all__ = ['value']
8value = 123
9'''
10
11with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as f:
12 f.write(code)
13 modname = f.name[:-3]
14
15try:
16 # Import the module
17 import importlib.util
18 spec = importlib.util.spec_from_file_location(modname, f.name)
19 module = importlib.util.module_from_spec(spec)
20 spec.loader.exec_module(module)
21
22 def gen():
23 x = module.value # Assignment from imported module
24 yield x
25 # No further use
26
27 print(list(gen()))
28finally:
29 os.unlink(f.name)
[123]
r1-p006 · Numeric coercion and precision
1from decimal import Decimal, getcontext
2getcontext().prec = 10
3
4def gen():
5 x = Decimal('1') / Decimal('3') # Precision-sensitive
6 yield x
7 # Later use in calculation (not yielded)
8 y = x * 3
9 # y should be 1 if precision preserved
10 yield (y - 1).copy_abs() # Should be near zero
11
12print(list(gen()))
[Decimal('0.3333333333'), Decimal('1E-10')]
r1-p007 · Locals() inspection during generator suspension
1def gen():
2 x = 42
3 frame = None
4 def capture_frame():
5 nonlocal frame
6 frame = frame # This is a trick: we want to capture the frame at yield point
7 # Instead, we inspect locals at first yield
8 pass
9 # We'll inspect locals by yielding a frame proxy
10 yield x
11 # Now yield locals() to see if 'x' is present
12 # But we can't access frame directly -> alternative: use sys._getframe
13 import sys
14 yield sys._getframe().f_locals.copy()
15
16# First value is 42, second is locals dict
17g = gen()
18first = next(g)
19second = next(g)
20print(f'First yielded: {first}')
21print(f"'x' in locals: {'x' in second}")
22print(f"Value of 'x' in locals: {second.get('x', 'MISSING')}")
First yielded: 42
'x' in locals: True
Value of 'x' in locals: 42
r1-p008 · Walrus operator in assignment (near-miss)
1def gen():
2 if (x := 5) > 0: # Walrus in condition
3 yield x # Uses x from walrus
4 # Rule should NOT fire here because assignment is not directly before yield
5 # (there's an if statement in between)
6
7 # Second part: direct assignment that SHOULD trigger
8 y = 99
9 yield y
10
11print(list(gen()))
[5, 99]
r2-p001 · Type annotation confusion
1def gen():
2 x: int = 42
3 yield x
4print(list(gen()))
[42]
r2-p002 · Evaluation order and side effects
1seq = []
2def side():
3 seq.append('called')
4 return 99
5def gen():
6 x = side()
7 yield x + sum(seq)
8print(list(gen()))
r2-p003 · Walrus operator interaction
1def gen():
2 if (x := 5) > 0:
3 yield x
4print(list(gen()))
[5]
r2-p004 · Runtime type annotations
1T = int
2def gen():
3 x = T(7)
4 yield x
5print(list(gen()))
[7]
r2-p005 · Class vs instance attributes
1class A:
2 val = 100
3 @property
4 def prop(self):
5 return self.val * 2
6def gen():
7 a = A()
8 x = a.prop
9 yield x
10print(list(gen()))
[200]
r2-p006 · Exception handling interference
1def gen():
2 try:
3 x = 1 / 0
4 except ZeroDivisionError as e:
5 x = e
6 yield x
7print(list(gen()))
[ZeroDivisionError('division by zero')]
r2-p007 · Locals() inspection during suspension
1def gen():
2 x = 42
3 yield locals().get('x', 'missing')
4print(list(gen()))
[42]
r2-p008 · Misapplied walrus extension
1def gen():
2 while (x := next(it)) != 0:
3 yield x
4it = iter([3, 2, 1, 0])
5print(list(gen()))
[3, 2, 1]