fixhunt

ruff RUF030

labelled unknown
Runs / 2026-10-17T21-43-53-372Z-demw-ruff-RUF030ruff 0.17.12026-10-17 21:43 UTC8 programs × 2 round(s)16 programs, 12 fired, 0 fixed, 0 findings, 0 rejectedmodel cost $0.0034

Analysis Nemotron 3 Super

The rule flags assert statements where the second argument is a call to print(), which results in None being passed as the AssertionError message while printing to stdout.

Fix: Replace the print() call in the assert statement's second argument with its string literal argument, removing the print call and using the message directly as the assertion error message.

10 hazards
  • The print call may have side effects beyond stdout, such as flushing buffers or triggering I/O errors, which are removed by the fix.
  • If print is called with non-string arguments (e.g., print(obj)), the fix incorrectly assumes a string literal, losing the original object's representation.
  • When print uses keyword arguments like sep, end, or file, the fix drops these formatting controls, altering output behavior.
  • If the print call is part of a larger expression (e.g., print(a) or print(b)), the fix is invalid as the rule only matches simple print calls, but the shape detection might misfire.
  • The fix removes any stdout output from the print call, which may be relied upon for logging, debugging, or test output capture.
  • In rare cases where print is overridden or mocked (e.g., in unit tests), the fix breaks test expectations by removing the call entirely.
  • If the print call appears in a lambda or comprehension within the assert (though unlikely given rule constraints), the fix would incorrectly rewrite it.
  • Comments attached to the print call or surrounding whitespace may be lost during the rewrite, depending on the linter's formatting preservation.
  • If the assertion message is intended to be None (via print()), the fix changes it to a string, altering the AssertionError's args.
  • The fix assumes the print call has exactly one argument; if it has zero or multiple arguments, the rewrite is syntactically invalid or semantically wrong.

Programs

idresulttitlehazard
r1-p001fired, no fixbytes vs str encodingWhen fixing `assert condition, print(b'data')`, replacing `print(b'data')` with raw `b'data'` loses the decoding step needed to match downstream string expectations.
r1-p002fired, no fixnon-string print argReplacing `print(obj)` with raw `obj` in `assert cond, print(obj)` changes behavior when obj has custom __str__ or side effects during printing.
r1-p003fired, no fixmutable default aliasingRewriting `assert cond, print(default_arg)` where default_arg is a mutable default causes shared state across calls if the print is removed and the default is reused.
r1-p004silentlambda in assertIf print were inside a lambda in assert (unlikely but testing edge), replacing it would break lambda semantics; though rule doesn't trigger, we simulate risk via side effect in arg.
r1-p005fired, no fixstring formatting edge casesFixing `assert cond, print(f'{x}')` to `f'{x}'` loses the print-side effect and may misrepresent formatting when x has __format__ that does I/O.
r1-p006fired, no fixclass vs instance attributeRewriting `assert cond, print(cls.attr)` where attr is a class attribute accessed via instance may break if fix uses raw value and attr is a property with setter.
r1-p007fired, no fixtruthiness and boolWhen `assert cond, print(msg)` is fixed, if `cond` relies on an object with __bool__ that has side effects, removing print may change evaluation timing in loops.
r1-p008silent prediction offkeyword-only argsRewriting `assert cond, print(log(msg, file=f))` where log has keyword-only args may lose the file=sys.stderr intent if fix strips print and assumes string.
r2-p001fired, no fixOverridden print mockIn rare cases where print is overridden or mocked (e.g., in unit tests), the fix breaks test expectations by removing the call entirely.
r2-p002fired, no fixCustom truthinesstruthiness of custom __bool__/__len__, None vs falsy, 'is' vs '=='
r2-p003silent prediction offKeyword-only argskeyword-only, positional-only, *args/**kwargs forwarding, defaults evaluated once
r2-p004fired, no fixSide effects beyond stdoutThe print call may have side effects beyond stdout, such as flushing buffers or triggering I/O errors, which are removed by the fix.
r2-p005silent prediction offExceptions and finallyexceptions raised inside the rewritten code, finally/else clauses, exception chaining
r2-p006fired, no fixString formatting edge casesstring formatting edge cases: %, format, f-strings, repr vs str, nested quotes
r2-p007fired, no fixNon-string arguments to printIf print is called with non-string arguments (e.g., print(obj)), the fix incorrectly assumes a string literal, losing the original object's representation.
r2-p008fired, no fixZero or multiple print argsThe fix assumes the print call has exactly one argument; if it has zero or multiple arguments, the rewrite is syntactically invalid or semantically wrong.

Sources

r1-p001 · bytes vs str encoding
1import sys
2buf = bytearray()
3for i in range(2):
4 ok = i == 0
5 try:
6 assert ok, print(b'Hello')
7 except AssertionError as e:
8 msg = str(e.args[0]) if e.args else ''
9 buf.extend(msg.encode('utf-8'))
10 else:
11 buf.extend(b'no assert')
12sys.stdout.write(buf.decode() + '\n')
b'Hello'
no assertNone
r1-p002 · non-string print arg
1class Obj:
2 def __init__(self, v): self.v = v
3 def __str__(self): return f'<{self.v}>'
4 def __repr__(self): return self.__str__()
5out = []
6for v in [1, 2]:
7 o = Obj(v)
8 try:
9 assert v < 2, print(o)
10 except AssertionError as e:
11 out.append(str(e))
12 else:
13 out.append('no raise')
14print('|'.join(out))
<2>
no raise|None
r1-p003 · mutable default aliasing
1def f(x, lst=[]):
2 lst.append(x)
3 return lst
4results = []
5for i in [1, 2]:
6 try:
7 assert i < 2, print(f(i))
8 except AssertionError as e:
9 results.append(str(e))
10 else:
11 results.append('no raise')
12print('|'.join(results))
[2]
no raise|None
r1-p004 · lambda in assert
1calls = []
2g = lambda: calls.append(print('side')) or 42
3try:
4 assert True, g()
5except AssertionError as e:
6 pass
7print(len(calls))
0
r1-p005 · string formatting edge cases
1class Fmt:
2 def __init__(self, v): self.v = v
3 def __format__(self, spec):
4 print(f'FORMATTING {self.v}') # side effect
5 return str(self.v)
6out = []
7for v in ['A', 'B']:
8 f = Fmt(v)
9 try:
10 assert v == 'A', print(f'{f}')
11 except AssertionError as e:
12 out.append(str(e))
13 else:
14 out.append('no raise')
15print('|'.join(out))
FORMATTING B
B
no raise|None
r1-p006 · class vs instance attribute
1class C:
2 attr = 'class'
3 @property
4 def prop(self): return self._prop
5 @prop.setter
6 def prop(self, v): self._prop = v.upper()
7c = C()
8c.prop = 'set'
9try:
10 assert c.prop == 'SET', print(c.attr)
11except AssertionError as e:
12 print(type(e).__name__, ':', e)
13else:
14 print('no raise')
no raise
r1-p007 · truthiness and bool
1class BoolObj:
2 def __init__(self, flag): self.flag = flag
3 def __bool__(self):
4 print('BOOL_CALLED') # side effect
5 return self.flag
6calls = []
7for i in [0, 1]:
8 o = BoolObj(bool(i))
9 try:
10 assert o, print('fail')
11 except AssertionError as e:
12 calls.append(1)
13 else:
14 calls.append(0)
15print(sum(calls))
BOOL_CALLED
fail
BOOL_CALLED
1
r1-p008 · keyword-only args
1import sys
2output = []
3def log(m, *, file=sys.stdout):
4 file.write(m + '\n')
5 return m
6for dest in [sys.stdout, sys.stderr]:
7 try:
8 assert False, log('error', file=dest)
9 except AssertionError as e:
10 output.append(str(e))
11 else:
12 output.append('no raise')
13print('|'.join(output))
error
error|error
r2-p001 · Overridden print mock
1import sys
2print_calls = []
3def my_print(*a, **k):
4 print_calls.append((a, k))
5 return 'mocked'
6sys.modules['builtins'].__dict__['print'] = my_print
7x = 5
8assert x > 0, print('value', x)
9try:
10 assert False
11except AssertionError as e:
12 print('AssertionError msg:', e)
13print('print_calls:', print_calls)
r2-p002 · Custom truthiness
1class A:
2 def __bool__(self): return False
3 def __len__(self): return 1
4a = A()
5print('bool(a):', bool(a))
6print('len(a):', len(a))
7assert a, print('a is truthy?')
8try:
9 if not a:
10 raise AssertionError('expected')
11except AssertionError as e:
12 print('Caught:', e)
bool(a): False
len(a): 1
a is truthy?
r2-p003 · Keyword-only args
1def log(msg, *, file=None):
2 if file:
3 file.write(msg + '\n')
4 return msg
5f = open('tmp.txt', 'w')
6try:
7 x = 1
8 assert x > 0, log('hello', file=f)
9finally:
10 f.close()
11with open('tmp.txt') as f:
12 print('file content:', f.read().strip())
file content:
r2-p004 · Side effects beyond stdout
1import io, sys
2old = sys.stdout
3sys.stdout = io.StringIO()
4try:
5 x = -1
6 assert x >= 0, print('neg', flush=True)
7except AssertionError as e:
8 pass
9finally:
10 sys.stdout = old
11print('stdout buffer:', sys.stdout.getvalue().strip())
r2-p005 · Exceptions and finally
1def bad():
2 raise ValueError('from print')
3x = 0
4try:
5 assert x > 0, bad()
6except AssertionError as ae:
7 print('AssertionError:', ae)
8 if ae.__cause__:
9 print('Cause:', ae.__cause__)
10except Exception as e:
11 print('Other:', type(e).__name__, e)
Other: ValueError from print
r2-p006 · String formatting edge cases
1name = "Bob"
2assert False, print(f'Hi {name!r}')
3try:
4 assert False
5except AssertionError as e:
6 print('Message:', e)
Hi 'Bob'
r2-p007 · Non-string arguments to print
1class Obj:
2 def __str__(self): return 'str'
3 def __repr__(self): return 'Obj()'
4o = Obj()
5assert False, print(o)
6try:
7 assert False
8except AssertionError as e:
9 print('Message:', e)
str
r2-p008 · Zero or multiple print args
1assert False, print('a', 'b')
2try:
3 assert False
4except AssertionError as e:
5 print('Message:', e)
a b