The file.writable() method returns True if the underlying file stream supports write operations, and False otherwise. It provides a defensive mechanism to verify stream capabilities before attempting to write data.
Companion Methods: To inspect reading and random-access capabilities of streams, use file.readable() and file.seekable().
The writable() method takes no arguments and is called directly on an open file object:
is_writable = file_object.writable()
| Parameter | Type | Required? | Description |
|---|---|---|---|
| (None) | — | No | The method accepts no parameters. Passing any arguments raises a TypeError. |
Return Value: Returns a boolean (bool):
True — if the file stream was opened in a mode that permits writing (such as 'w', 'a', 'x', 'r+', 'w+', 'a+').False — if the file stream is read-only (such as 'r' or 'rb').Let's open files in different access modes to observe the boolean output of writable():
# 1. Read-only mode ('r')
with open("data.txt", "w") as f_setup:
f_setup.write("Sample data\n")
with open("data.txt", "r") as f_read:
print("Read mode ('r'): ", f_read.writable()) # False
# 2. Write mode ('w')
with open("data.txt", "w") as f_write:
print("Write mode ('w'): ", f_write.writable()) # True
# 3. Append mode ('a')
with open("data.txt", "a") as f_append:
print("Append mode ('a'): ", f_append.writable()) # True
# 4. Read + Write mode ('r+')
with open("data.txt", "r+") as f_plus:
print("Read+Write ('r+'): ", f_plus.writable()) # True
Read mode ('r'): False
Write mode ('w'): True
Append mode ('a'): True
Read+Write ('r+'): True
Python's built-in open() function supports numerous mode combinations. Here is how each mode responds to writable() and readable():
| Mode | Operation Description | readable() |
writable() |
Initial Pointer Position |
|---|---|---|---|---|
'r' / 'rb' |
Read only (default) | True | False | Beginning of file (0) |
'w' / 'wb' |
Truncate & write only | False | True | Beginning of file (0) |
'a' / 'ab' |
Append only | False | True | End of file (EOF) |
'x' / 'xb' |
Exclusive creation & write | False | True | Beginning of file (0) |
'r+' / 'r+b' |
Read and write (no truncation) | True | True | Beginning of file (0) |
'w+' / 'w+b' |
Truncate, read, and write | True | True | Beginning of file (0) |
'a+' / 'a+b' |
Read and append | True | True | End of file (EOF) |
If your code calls write() on a stream that was opened in read-only mode, Python raises io.UnsupportedOperation: not writable (which is a subclass of OSError and ValueError):
import io
with open("data.txt", "r") as f:
try:
f.write("New Content")
except io.UnsupportedOperation as err:
print("Write failed:", err)
Write failed: not writable
Defensive Programming Pattern: Checking f.writable() before writing allows modular functions to fail early or fall back gracefully rather than crashing in the middle of processing:
def safe_append_log(stream, message):
if not stream.writable():
print("Warning: Stream is not writable. Log omitted.")
return False
stream.write(f"[LOG] {message}\n")
return True
Calling writable() on a file that has already been closed raises a ValueError:
f = open("data.txt", "w")
f.close()
try:
f.writable()
except ValueError as err:
print("Error caught:", err)
# Output: Error caught: I/O operation on closed file.
To safely inspect an uncertain file handle, check not f.closed and f.writable().
Python's io.StringIO and io.BytesIO memory streams implement the same file interface as disk files, and their writable() method returns True by default:
import io
mem_stream = io.StringIO("Initial text in memory")
print("Memory stream writable:", mem_stream.writable()) # True
print("Memory stream readable:", mem_stream.readable()) # True
Python standard library streams adhere to three core capability queries:
f.readable() — Determines if the stream supports read() and readline().f.writable() — Determines if the stream supports write() and writelines().f.seekable() — Determines if the stream supports random cursor jumps via seek() and position queries via tell(). (For instance, network sockets, named pipes, and standard input streams return False for seekable()).Let's build a robust stream dispatcher that inspects incoming file handles, writes audit events when permitted, and returns structured validation status, verified with unit assertions:
import io
import tempfile
def audit_and_write(stream, event_message):
# Defensive capability audit
if stream.closed:
return {"status": "ERROR", "reason": "CLOSED"}
if not stream.writable():
return {"status": "REJECTED", "reason": "NOT_WRITABLE"}
stream.write(f"AUDIT: {event_message}\n")
return {"status": "SUCCESS", "reason": None}
# Test Case 1: Writable in-memory stream
mem = io.StringIO()
res1 = audit_and_write(mem, "User login from IP 192.168.1.1")
assert res1["status"] == "SUCCESS"
assert mem.getvalue() == "AUDIT: User login from IP 192.168.1.1\n"
# Test Case 2: Read-only temporary file
with tempfile.NamedTemporaryFile(mode="w+") as temp:
temp.write("Existing data\n")
temp.flush()
with open(temp.name, "r") as read_only_f:
res2 = audit_and_write(read_only_f, "Should not be written")
assert res2["status"] == "REJECTED"
assert res2["reason"] == "NOT_WRITABLE"
# Test Case 3: Closed stream
closed_stream = io.StringIO()
closed_stream.close()
res3 = audit_and_write(closed_stream, "Attempt on closed")
assert res3["status"] == "ERROR"
assert res3["reason"] == "CLOSED"
print("All stream capability audit assertions passed successfully!")
Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.