fixhunt

ruff RET503

labelled unknown
Runs / 2026-10-17T20-32-08-036Z-r6g6-ruff-RET503ruff 0.17.12026-10-17 20:32 UTC8 programs × 2 round(s)16 programs, 9 fired, 0 fixed, 0 findings, 1 rejectedmodel cost $0.0031

Analysis Nemotron 3 Super

Flags functions lacking an explicit return statement when they contain any return expression.

Fix: Adds an explicit 'return None' statement at the end of the function body, after all existing statements.

8 hazards
  • Adding 'return None' after a try-except-finally block may suppress exceptions raised in the finally clause, as the explicit return will propagate before the finally block completes.
  • If the function contains a yield statement elsewhere, adding 'return None' creates a syntax error because return with a value inside a generator is only allowed in Python 3.3+ and changes the generator's behavior.
  • Inserting 'return None' after a with block may skip execution of context manager's __exit__ method if an exception occurs in the with block and is not handled, altering cleanup semantics.
  • When the function is part of a class hierarchy and overridden in a subclass, adding 'return None' may violate Liskov substitution if the override expects a different return type.
  • If the function is decorated with a decorator that inspects or modifies the return value (e.g., @property, @cached_property), inserting 'return None' may break the decorator's assumptions.
  • In async functions, adding 'return None' after an await expression may alter control flow if the await raises an exception, as the explicit return prevents propagation.
  • If the function's last line is a comment-only line, inserting 'return None' after it may detach trailing comments from the preceding statement, affecting readability or tooling that relies on comment attachment.
  • When the function contains a nested function with a return statement, adding 'return None' in the outer function may cause unintended early termination if control flows through the nested function's definition (which is inert) but the outer function now returns None unconditionally.

Programs

idresulttitlehazard
r1-p001fired, no fixContext manager without closecontext managers that are not files, objects with write() but no close()
r1-p002silent prediction offAsync function with awaitIn async functions, adding 'return None' after an await expression may alter control flow if the await raises an exception, as the explicit return prevents propagation.
r1-p003silent prediction offWith block exception skipping __exit__Inserting 'return None' after a with block may skip execution of context manager's __exit__ method if an exception occurs in the with block and is not handled, altering cleanup semantics.
r1-p004fired, no fixNested function definitionWhen the function contains a nested function with a return statement, adding 'return None' in the outer function may cause unintended early termination if control flows through the nested function's d
r1-p005fired, no fixType annotations evaluated at runtimetype annotations evaluated at runtime, typing-only imports
r1-p006fired, no fixEvaluation order and side effectsevaluation order and side effects when expressions are merged, duplicated or deleted
r1-p007silentPathlib vs os.path behaviorpathlib vs os.path behavior on relative paths, symlinks, missing files, bytes paths
r1-p008silent prediction offFile modes and truncationfile modes and truncation, partial writes, non-str data passed to write
r2-p001fired, no fixclass attribute descriptor interactionclass attributes vs instance attributes, descriptors, properties with side effects
r2-p002silent prediction offwith block exception skipping __exit__Inserting 'return None' after a with block may skip execution of context manager's __exit__ method if an exception occurs in the with block and is not handled, altering cleanup semantics.
r2-p003fired, no fixnested function definition inert but outer return addedWhen the function contains a nested function with a return statement, adding 'return None' in the outer function may cause unintended early termination if control flows through the nested function's d
r2-p004invalidstar-import module side effects and __all__star-imports, module-level side effects, __all__
r2-p005fired, no fixbytes vs str encoding newline handlingbytes vs str, encoding and newline handling, text vs binary modes
r2-p006silent prediction offcomment-only last line detaching trailing commentsIf the function's last line is a comment-only line, inserting 'return None' after it may detach trailing comments from the preceding statement, affecting readability or tooling that relies on comment
r2-p007fired, no fixint/float/bool coercion Decimal division roundingint/float/bool coercion, Decimal, division and rounding
r2-p008fired, no fixgenerators vs lists iterators consumed once lazy eagergenerators vs lists, iterators consumed once, lazy vs eager evaluation

Sources

r1-p001 · Context manager without close
1class Ctx:
2 def __enter__(self):
3 return self
4 def __exit__(self, *exc):
5 print('exit', exc[0] is not None)
6 def write(self, s):
7 print('write:', s)
8def f():
9 with Ctx() as c:
10 if True:
11 return c.write('hello')
12print(f())
write: hello
exit False
None
r1-p002 · Async function with await
1async def g():
2 raise ValueError('fail')
3async def f():
4 await g()
5 return 42
6import asyncio
7try:
8 asyncio.run(f())
9except Exception as e:
10 print(type(e).__name__, str(e))
ValueError fail
r1-p003 · With block exception skipping __exit__
1class Ctx:
2 def __enter__(self):
3 return self
4 def __exit__(self, t, v, tb):
5 print('__exit__ called,', t is not None)
6 def write(self, s):
7 pass
8def f():
9 with Ctx() as c:
10 raise ValueError('bad')
11 return None
12try:
13 f()
14except Exception as e:
15 print(type(e).__name__, str(e))
__exit__ called, True
ValueError bad
r1-p004 · Nested function definition
1def f():
2 def inner():
3 return 42
4 if True:
5 return inner()
6print(f())
42
r1-p005 · Type annotations evaluated at runtime
1from typing import List
2def f():
3 if True:
4 return List[int]
5print(f())
typing.List[int]
r1-p006 · Evaluation order and side effects
1i = 0
2def next_i():
3 global i
4 i += 1
5 return i
6def f():
7 if next_i() == 1:
8 return next_i()
9print(f())
2
r1-p007 · Pathlib vs os.path behavior
1import os
2import tempfile
3with tempfile.TemporaryDirectory() as d:
4 p = os.path.join(d, 'link')
5 os.symlink('target', p)
6 if os.path.exists(p):
7 return os.readlink(p)
8print('no return')
r1-p008 · File modes and truncation
1import os
2def f():
3 with open('test.txt', 'w') as fp:
4 if True:
5 return fp.write(b'hello')
6 return None
7with open('test.txt', 'r') as fp:
8 print(repr(fp.read()))
9os.remove('test.txt')
r2-p001 · class attribute descriptor interaction
1class A:
2 @property
3 def x(self):
4 print('get')
5 return 1
6
7def f():
8 a = A()
9 if True:
10 return a.x
11
12print(f())
get
1
r2-p002 · with block exception skipping __exit__
1class Ctx:
2 def __enter__(self):
3 print('enter')
4 return self
5 def __exit__(self, exc_type, exc_val, exc_tb):
6 print('exit:', exc_type)
7 return False
8
9def f():
10 with Ctx():
11 raise ValueError('fail')
12
13try:
14 f()
15except ValueError as e:
16 print('caught:', e)
enter
exit: <class 'ValueError'>
caught: fail
r2-p003 · nested function definition inert but outer return added
1def f():
2 def g():
3 return 5
4 if True:
5 return g()
6
7print(f())
5
r2-p004 · star-import module side effects and __all__ (generated program is not deterministic)
1import tempfile
2import os
3mod = tempfile.mkstemp()[1]
4with open(mod, 'w') as f:
5 f.write('x = 42\n__all__ = [\"x\"]')
6os.close(int(mod.split(',')[0][1:])) if ',' in mod else os.close(int(mod))
7# Simulate import via exec
8ns = {}
9with open(mod, 'r') as f:
10 exec(f.read(), ns)
11def h():
12 if True:
13 return ns['x']
14print(h())
15os.remove(mod)
r2-p005 · bytes vs str encoding newline handling
1def f():
2 with open('test.bin', 'wb') as fp:
3 fp.write(b'hello\nworld')
4 with open('test.bin', 'rb') as fp:
5 data = fp.read()
6 if b'\n' in data:
7 return data.replace(b'\n', b' ')
8
9result = f()
10print(result)
11import os
12os.remove('test.bin')
b'hello world'
r2-p006 · comment-only last line detaching trailing comments
1def f():
2 x = 1 # important value
3 # end comment
4
5print(f())
None
r2-p007 · int/float/bool coercion Decimal division rounding
1from decimal import Decimal, getcontext
2getcontext().prec = 3
3def f():
4 if True:
5 return Decimal('1') / Decimal('3')
6
7print(f())
0.333
r2-p008 · generators vs lists iterators consumed once lazy eager
1def f():
2 def gen():
3 yield 1
4 yield 2
5 g = gen()
6 if True:
7 return list(g)
8
9print(f())
10print(f()) # second call redefines g, so same output
[1, 2]
[1, 2]