Complete guide to Python's os.closerange function covering file descriptor management, resource cleanup, and practical examples.
Last modified April 11, 2025
This comprehensive guide explores Python’s os.closerange function, which efficiently closes a range of file descriptors. We’ll cover its parameters, use cases, and practical examples for resource management.
The os.closerange function closes all file descriptors from fd_low (inclusive) to fd_high (exclusive). It ignores errors during closing.
Key parameters: fd_low (first descriptor to close), fd_high (stop before this descriptor). Returns None. Part of Python’s os module for OS interactions.
This basic example demonstrates how to close descriptors 3 through 9. The function will attempt to close descriptors 3, 4, 5, 6, 7, and 8.
basic_closerange.py
import os
files = [open(f"file_{i}.txt", “w”) for i in range(10)]
fds = [f.fileno() for f in files]
print(f"Open descriptors before: {fds}")
os.closerange(3, 9)
remaining_fds = [f.fileno() for f in files[:3] + files[9:]] print(f"Open descriptors after: {remaining_fds}")
for f in files[:3] + files[9:]: f.close()
This creates 10 files, then closes descriptors 3 through 8 using closerange. The first 3 and last file remain open until explicitly closed.
Note that file objects should still be properly closed even after their underlying descriptors are closed with closerange.
A common use case is closing all descriptors above a certain number, often used after fork() to clean up unwanted file descriptors.
close_above_threshold.py
import os import resource
max_fd = resource.getrlimit(resource.RLIMIT_NOFILE)[0]
files = [open(f"temp_{i}.txt", “w”) for i in range(5)] fds = [f.fileno() for f in files]
print(f"Current open descriptors: {fds}")
os.closerange(3, max_fd)
try: for fd in fds: if fd >= 3: os.fstat(fd) # Will raise OSError if closed except OSError as e: print(f"Descriptor check failed: {e}")
for f in files: if not f.closed: f.close()
This example closes all descriptors from 3 up to the system’s maximum. The resource module helps determine the upper bound for the close operation.
The try-except block demonstrates how to verify descriptors were actually closed by attempting operations on them.
os.closerange silently ignores errors when closing descriptors. This example shows how to implement custom error handling around it.
error_handling.py
import os
def safe_closerange(fd_low, fd_high): “““Close range with basic error reporting””” open_fds = []
# Check which descriptors in range are actually open
for fd in range(fd_low, fd_high):
try:
os.fstat(fd)
open_fds.append(fd)
except OSError:
pass
# Perform the close
os.closerange(fd_low, fd_high)
# Verify which descriptors were successfully closed
closed = []
remaining = []
for fd in open_fds:
try:
os.fstat(fd)
remaining.append(fd)
except OSError:
closed.append(fd)
return closed, remaining
closed, remaining = safe_closerange(3, 10) print(f"Successfully closed: {closed}") print(f"Still open: {remaining}")
This wrapper function provides basic error reporting by checking descriptor status before and after the closerange operation.
While not foolproof due to potential race conditions, it offers more visibility than the standard closerange behavior.
Daemon processes often use closerange to clean up file descriptors after forking. This example demonstrates a simple daemonization pattern.
daemon_example.py
import os import sys import time
def daemonize(): # First fork try: pid = os.fork() if pid > 0: sys.exit(0) # Exit parent except OSError as e: sys.stderr.write(f"First fork failed: {e}\n") sys.exit(1)
# Create new session
os.setsid()
# Second fork
try:
pid = os.fork()
if pid > 0:
sys.exit(0)
except OSError as e:
sys.stderr.write(f"Second fork failed: {e}\n")
sys.exit(1)
# Close all open file descriptors
max_fd = os.sysconf('SC_OPEN_MAX')
if max_fd == -1: # Default to 1024 if unknown
max_fd = 1024
os.closerange(0, max_fd)
# Redirect standard file descriptors to /dev/null
devnull = os.open('/dev/null', os.O_RDWR)
for fd in (0, 1, 2): # stdin, stdout, stderr
os.dup2(devnull, fd)
# Daemon main loop
while True:
with open("/tmp/daemon.log", "a") as f:
f.write(f"Daemon running at {time.ctime()}\n")
time.sleep(5)
if name == “main”: daemonize()
This shows a standard daemonization process where closerange helps clean up all file descriptors before the daemon begins its work.
The example includes double-forking, session creation, and proper handling of standard I/O descriptors.
Sometimes you need to close most descriptors but preserve a few. This example shows how to combine closerange with selective preservation.
preserve_descriptors.py
import os import sys
def isolate_process(preserve_fds=[]): “““Close all descriptors except those specified””” max_fd = 1024 # Default reasonable maximum
# Close lower range (0 to first preserved fd)
if preserve_fds:
os.closerange(0, min(preserve_fds))
# Close ranges between preserved fds
sorted_fds = sorted(preserve_fds)
for i in range(len(sorted_fds) - 1):
os.closerange(sorted_fds[i] + 1, sorted_fds[i + 1])
# Close upper range (last preserved fd to max)
if sorted_fds:
os.closerange(sorted_fds[-1] + 1, max_fd)
else:
os.closerange(0, max_fd)
log_fd = os.open(“preserved.log”, os.O_WRONLY | os.O_CREAT) sock_fd = os.open("/dev/tty", os.O_RDWR)
print(f"Preserving descriptors: {log_fd}, {sock_fd}") isolate_process(preserve_fds=[log_fd, sock_fd])
try: os.write(log_fd, b"Still open\n") os.write(sock_fd, b"Still open\n") print(“Preserved descriptors still functional”) except OSError as e: print(f"Descriptor error: {e}")
os.close(log_fd) os.close(sock_fd)
This function closes all descriptors except those explicitly listed in preserve_fds. It handles ranges efficiently around the preserved descriptors.
The example preserves a log file and terminal descriptor while closing all others in the assumed range.
This example shows a more portable approach to descriptor management that works across different Unix-like systems with varying maximum descriptor values.
portable_closerange.py
import os import resource import sys
def portable_descriptor_cleanup(keep_fds=[]): “““Portable descriptor cleanup across Unix systems””” try: # Try to get system maximum max_fd = resource.getrlimit(resource.RLIMIT_NOFILE)[0] if max_fd == resource.RLIM_INFINITY: max_fd = 1024 # Fallback value except (AttributeError, ValueError): max_fd = 1024 # Default fallback
# Get actually open descriptors
open_fds = set()
for fd in range(max_fd):
try:
os.fstat(fd)
open_fds.add(fd)
except OSError:
pass
# Close all not in keep_fds
for fd in open_fds:
if fd not in keep_fds:
try:
os.close(fd)
except OSError:
pass # Already closed or invalid
important_fd = os.open(“critical.log”, os.O_WRONLY | os.O_CREAT)
print(f"Preserving descriptor: {important_fd}") portable_descriptor_cleanup(keep_fds=[important_fd, 0, 1, 2])
try: os.write(important_fd, b"Still working\n") print(“Preserved descriptor functional”) except OSError as e: print(f"Descriptor error: {e}")
os.close(important_fd)
This approach is more robust than simple closerange as it first detects which descriptors are actually open before attempting to close them.
It preserves standard descriptors (0,1,2) by default along with any explicitly listed descriptors, making it suitable for daemon and security applications.
Resource leaks: Failing to close descriptors can lead to leaks
Security risks: Unintentionally open descriptors pose risks
Portability: Maximum descriptor values vary across systems
Race conditions: Descriptors can be opened between check and close
Silent failures: closerange doesn’t report which closes failed
Use in daemons: Essential for proper daemonization
Combine with dup2: Redirect standard descriptors first
Consider context: May not be needed in managed environments
Document preserved fds: Clearly note which descriptors stay open
Test thoroughly: Verify behavior on target platforms
My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.
List all Python tutorials.