ruff FURB103
labelled unknown
Analysis Nemotron 3 Super
Flags manual file writes using open() and .write() when they can be replaced with pathlib.Path.write_text() or write_bytes().
Fix: Replaces the with-open-write block with a direct call to pathlib.Path(file_path).write_text(content) or write_bytes(data), preserving the file path and written content while removing the variable assignment and block structure.
10 hazards
- The rewrite removes the file handle variable (f), breaking any subsequent use of f after the write but before the block ends.
- If the write call is not the last statement in the block, the fix incorrectly truncates the block and drops later statements.
- The fix assumes the file is opened solely for writing; modes like 'r+' or 'a+' are not equivalent to write_text/write_bytes and may alter behavior.
- Comments attached to the 'with' line, 'as f:', or indentation are discarded during the rewrite, violating comment preservation expectations.
- The rewrite does not preserve custom openers, buffering, or errors parameters, which may affect I/O behavior in edge cases.
- If the file path expression has side effects (e.g., function call), duplicating it in Path() changes evaluation timing and frequency.
- When the written data is a complex expression, the fix may alter evaluation order relative to other expressions in the surrounding scope.
- The generated code lacks the implicit file closing guaranteed by the context manager, though write_text/write_bytes handle closure internally—this is safe only if no exceptions occur mid-write.
- If 'open' or 'Path' is shadowed by a local variable, the fix may bind to the wrong identifier, altering semantics.
- The fix introduces a new import dependency on pathlib; if the module does not already import Path, the rewrite may cause a NameError unless the tool adds the import.
Programs
| id | result | title | hazard |
|---|---|---|---|
| r1-p001 | fix ok | star_import_side_effect_all | FURB103 triggered inside a loop over __all__; after star import shadows Path causing mis-resolution |
| r1-p002 | silent prediction off | shadowed_builtins_redeclared | Shadowing open then restoring it lets second use show if first was rewritten incorrectly |
Sources
r1-p001 · star_import_side_effect_all
1import os
2from pathlib import *
3def main():
4 __all__ = ['p']
5 p = lambda x: None
6 txt = b'data'
7 for name in __all__:
8 if name == 'p':
9 continue
10 try:
11 with open('tmp.bin', 'wb') as f:
12 f.write(txt)
13 break
14 except TypeError as e:
15 print(e)
16 try:
17 v = Path('tmp.bin').read_bytes()
18 print(v)
19 except Exception as e:
20 print(type(e).__name__, e)
21 finally:
22 if os.path.exists('tmp.bin'):
23 os.remove('tmp.bin')
24main()
--- orig.py 2026-10-22 23:46:49.092196632 -0400 +++ fixed.py 2026-10-22 23:46:51.210216049 -0400 @@ -1,5 +1,6 @@ import os from pathlib import * +import pathlib def main(): __all__ = ['p'] p = lambda x: None @@ -8,8 +9,7 @@ if name == 'p': continue try: - with open('tmp.bin', 'wb') as f: - f.write(txt) + pathlib.Path('tmp.bin').write_bytes(txt) break except TypeError as e: print(e)
FileNotFoundError [Errno 2] No such file or directory: 'tmp.bin'
r1-p002 · shadowed_builtins_redeclared
1open = lambda *a, **k: (_ for _ in ()).throw(ValueError('bad'))
2txt = b'hi'
3with open('t.bin', 'wb') as f:
4 f.write(txt)
5del open
6with open('t.bin', 'wb') as f:
7 f.write(txt)
8with open('t.bin', 'rb') as f:
9 print(f.read())
10import os
11if os.path.exists('t.bin'):
12 os.remove('t.bin')