Skip to main content

How to Check if a File is Open or Closed in Python

Managing file resources is critical to prevent memory leaks and data corruption. Leaving files open can lock them, preventing other processes from accessing them and potentially causing data loss.

Using the .closed Attribute

Every Python file object has a read-only .closed attribute:

  • Returns True if the file is closed
  • Returns False if the file is currently open
# Open a file manually
f = open("example.txt", "w")
print(f"Is closed? {f.closed}") # False

f.close()
print(f"Is closed? {f.closed}") # True

Checking Before Operations

Prevent errors by checking the file state before operations:

def safe_read(file_obj) -> str | None:
"""Safely read from a file, checking if it's open first."""
if file_obj.closed:
print("Warning: Cannot read from a closed file.")
return None
return file_obj.read()


def safe_write(file_obj, content: str) -> bool:
"""Safely write to a file, checking if it's open first."""
if file_obj.closed:
print("Warning: Cannot write to a closed file.")
return False
file_obj.write(content)
return True

Using Context Managers (Best Practice)

In modern Python, you should rarely need to check .closed. The with statement guarantees automatic cleanup, even if an error occurs:

with open("data.json", "r") as f:
# File is open inside the block
data = f.read()
print(f"Inside block - closed: {f.closed}") # False

# File is automatically closed after the block
print(f"Outside block - closed: {f.closed}") # True
Why Use Context Managers

The with statement follows the RAII (Resource Acquisition Is Initialization) pattern:

  • File is opened when entering the block
  • File is guaranteed to close when exiting, even if an exception occurs
  • No need to remember to call .close()
  • No need to use try/finally blocks

Handling Multiple Files

Context managers handle multiple files elegantly:

# Python 3.10+ syntax
with (
open("input.txt", "r") as infile,
open("output.txt", "w") as outfile
):
content = infile.read()
outfile.write(content.upper())

# Both files are now closed
print(f"Input closed: {infile.closed}") # True
print(f"Output closed: {outfile.closed}") # True
# Compatible with older Python versions
with open("input.txt", "r") as infile:
with open("output.txt", "w") as outfile:
content = infile.read()
outfile.write(content.upper())

Common Errors with Closed Files

Attempting operations on closed files raises ValueError:

f = open("test.txt", "w")
f.close()

try:
f.write("Hello")
except ValueError as e:
print(f"Error: {e}")
# Error: I/O operation on closed file.

Creating a Safe File Wrapper

class SafeFile:
"""Wrapper that prevents operations on closed files."""

def __init__(self, filepath: str, mode: str = "r"):
self._file = open(filepath, mode)
self._filepath = filepath

def read(self) -> str | None:
if self._file.closed:
raise RuntimeError(f"File '{self._filepath}' is already closed")
return self._file.read()

def write(self, content: str) -> None:
if self._file.closed:
raise RuntimeError(f"File '{self._filepath}' is already closed")
self._file.write(content)

def close(self) -> None:
if not self._file.closed:
self._file.close()

@property
def closed(self) -> bool:
return self._file.closed

def __enter__(self):
return self

def __exit__(self, exc_type, exc_val, exc_tb):
self.close()
return False

Checking File Status in Functions

When functions receive file objects as parameters:

def process_file(file_obj) -> dict:
"""Process a file object, validating its state first."""
# Validate file object
if not hasattr(file_obj, 'read'):
raise TypeError("Expected a file-like object")

if file_obj.closed:
raise ValueError("Cannot process a closed file")

if not file_obj.readable():
raise ValueError("File is not readable")

# Process the file
content = file_obj.read()
return {
"length": len(content),
"lines": content.count("\n") + 1
}


# Usage
with open("data.txt", "r") as f:
result = process_file(f)
print(result)

File Object Attributes Reference

with open("example.txt", "w+") as f:
print(f"Name: {f.name}") # example.txt
print(f"Mode: {f.mode}") # w+
print(f"Closed: {f.closed}") # False
print(f"Readable: {f.readable()}") # True
print(f"Writable: {f.writable()}") # True
print(f"Seekable: {f.seekable()}") # True

Debugging File Leaks

Track unclosed files during development:

import atexit
import warnings

_open_files = []


def tracked_open(*args, **kwargs):
"""Open that tracks file objects for debugging."""
f = open(*args, **kwargs)
_open_files.append(f)
return f


def report_unclosed():
"""Report any files that weren't closed."""
unclosed = [f for f in _open_files if not f.closed]
if unclosed:
warnings.warn(f"Unclosed files detected: {[f.name for f in unclosed]}")


# Register cleanup check
atexit.register(report_unclosed)

Quick Reference

Attribute/MethodTypeDescription
.closedPropertyTrue if file is closed
.close()MethodCloses the file
.readable()MethodTrue if file can be read
.writable()MethodTrue if file can be written
with statementKeywordAuto-closes files (recommended)

Summary

  • Use .closed to check file state when needed for validation.
  • Always prefer with statements: they guarantee cleanup even during exceptions.
  • Attempting I/O on closed files raises ValueError.
  • Check .readable() and .writable() to verify permitted operations.
  • For production code, rely on context managers rather than manual .close() calls.