RustPython/RustPython · warning · ResourceWarning
unclosed file {repr}
Error message
unclosed file {repr} What it means
ResourceWarning emitted from FileIO's dealloc hook when the object is dropped while its fd is still >= 0 and closefd is true — i.e. the file was never close()d and cleanup fell to the garbage collector. File descriptors are a finite process-global resource, so both CPython and RustPython warn at destruction to make the leak visible. If raising the warning itself fails, it is routed through run_unraisable.
Source
Thrown at crates/vm/src/stdlib/_io.rs:6064
fn __getstate__(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult {
Err(vm.new_type_error(format!("cannot pickle '{}' instances", zelf.class().name())))
}
/// fileio_dealloc_warn in Modules/_io/fileio.c
#[pymethod(name = "_dealloc_warn")]
fn _dealloc_warn_method(zelf: &Py<Self>, source: PyObjectRef, vm: &VirtualMachine) {
Self::dealloc_warn(zelf, source, vm);
}
}
impl FileIO {
/// Issue ResourceWarning if fd is still open and closefd is true.
fn dealloc_warn(zelf: &Py<Self>, source: PyObjectRef, vm: &VirtualMachine) {
if zelf.fd.load() >= 0 && zelf.closefd.load() {
let repr = source
.repr(vm)
.map_or_else(|_| Wtf8Buf::from("<file>"), |s| s.as_wtf8().to_owned());
if let Err(e) = crate::stdlib::_warnings::warn(
vm.ctx.exceptions.resource_warning,
format!("unclosed file {repr}"),
1,
vm,
) {
vm.run_unraisable(e, None, zelf.as_object().to_owned());
}
}
}
}
impl Destructor for FileIO {
fn slot_del(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<()> {
if let Some(fileio) = zelf.downcast_ref::<Self>() {
fileio.finalizing.store(true);
}
iobase_finalize(zelf, vm);
Ok(())View on GitHub (pinned to 5dc83d997e)
Solutions
- Use a with statement: with open(p) as f: ...
- Use try/finally with close() when a with block does not fit the control flow
- Give every cached file object an owner responsible for closing it
- Run tests with python -X dev or -W error::ResourceWarning so leaks fail fast
Example fix
# before
f = open("log.txt", "rb")
data = f.read()
# after
with open("log.txt", "rb") as f:
data = f.read() Defensive patterns
Strategy: try-catch
Validate before calling
import gc, warnings
def assert_no_unclosed_files(stage):
gc.collect()
with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("always", ResourceWarning)
gc.collect()
leaks = [str(w.message) for w in caught if "unclosed file" in str(w.message)]
assert not leaks, f"{stage}: {leaks}" Try / catch
import warnings
with warnings.catch_warnings():
warnings.simplefilter("error", ResourceWarning)
code_under_test() # raises ResourceWarning as an exception at GC time Prevention
- Open files only via with statements in new code
- Give cached/stored file objects a designated owner that closes them
- Run test suites under python -X dev so leaks fail instead of warning quietly
When it happens
Trigger: open()/io.FileIO without close(); exceptions that skip the close() call; storing file objects in caches/containers and dropping them later; reference cycles that delay collection. Usually observed only when GC actually runs, or under -X dev / -Wd.
Common situations: Long-running servers gradually exhausting fds; scripts that open many files in a loop; test suites that fail intermittently with 'Too many open files' after warnings are enabled.
Related errors
- bool is used as a file descriptor
- codecs.open() is deprecated. Use open() instead.
- seeking backwards is not allowed
- line buffering (buffering=1) isn't supported in binary mode,
- 'encoding' argument not specified
AI-assisted analysis of RustPython/RustPython@5dc83d997e (2026-08-17).
Data as JSON: /api/errors/b7a49a5b7b83734b.
Report an issue: GitHub.