The file.seekable() method returns a boolean True if the file stream supports random access positioning via seek() and tell(), and False if the stream is strictly sequential.
Sequential Stream Protection: Attempting to call seek() on non-seekable streams — such as network sockets, standard input pipes (sys.stdin), or subprocess pipes — raises an OSError: [Errno 29] Illegal seek. Querying stream.seekable() prevents unexpected pipeline crashes.
The standard CPython signature for file.seekable() is:
file_object.seekable()
| Component | Type | Parameters? | Behavior & Description |
|---|---|---|---|
| Arguments | None | None | seekable() takes no arguments. Passing arguments raises TypeError. |
| Return Value | bool |
— | Returns True if the stream supports random access via seek(); returns False for sequential-only streams. |
| Exceptions | ValueError |
— | Raised with I/O operation on closed file if called on an already closed stream. |
In Python, various stream sources provide different navigation capabilities:
| Stream Type | seekable() Return | Underlying I/O Architecture | Notes & Capabilities |
|---|---|---|---|
| Regular Disk Files | True | Local filesystem block storage | All modes ('r', 'w', 'a', 'r+') support random pointer jumps. |
In-Memory Buffers (io.BytesIO, io.StringIO) |
True | RAM byte/character array | Fast arbitrary in-memory cursor navigation. |
Standard Input (sys.stdin) |
False | Terminal / OS pipe buffer | Cannot rewind stdin; input must be consumed sequentially. |
| Network Sockets | False | TCP / UDP network socket buffer | Data arrives over network packets; cannot jump backward. |
Process Pipes (subprocess.PIPE, FIFOs) |
False | OS inter-process communication pipe | Unidirectional data flow without addressable seek offsets. |
When developing CLI utilities or library functions that accept either a file path, an open file, or standard input (e.g. cat data.csv | python script.py), check seekable() before attempting stream resets:
import sys
def inspect_stream_header(stream) -> str:
header = stream.readline()
if stream.seekable():
# Reset cursor so caller can process full file from beginning
stream.seek(0)
else:
print("Notice: Stream is sequential; cannot rewind cursor.")
return header.strip()
# Verification with regular disk file
with open("data.txt", "r", encoding="utf-8") as disk_file:
print("Disk file header:", inspect_stream_header(disk_file))
If your parsing library strictly requires random access (e.g. reading ZIP archives or parsing PDF / EXIF binary headers), convert unseekable streams into seekable memory buffers using io.BytesIO:
import io
def ensure_seekable_binary_stream(stream) -> io.BytesIO | any:
if stream.seekable():
return stream
# Ingest unseekable stream into RAM buffer
buffer = io.BytesIO(stream.read())
buffer.seek(0)
return buffer
# Demonstration: Converting simulated sequential socket
seekable_stream = ensure_seekable_binary_stream(io.BytesIO(b"DATA"))
print("Stream is now guaranteed seekable:", seekable_stream.seekable()) # True
Just like readable() and writable(), calling seekable() on an already closed stream raises a ValueError exception:
f = open("data.txt", "r", encoding="utf-8")
f.close()
try:
f.seekable()
except ValueError as err:
print("Closed stream raises ValueError:", err)
# Safe idiom: check closed property first
is_safe = not f.closed and f.seekable()
print("Safe seekable verification:", is_safe) # False
Create a resilient parser that validates record counts in pass 1 and ingests data in pass 2, supporting both files and stdin:
def process_multi_pass_data(input_source):
if not input_source.seekable():
# Buffer into memory for multiple passes
input_source = io.StringIO(input_source.read())
# Pass 1: Count rows
total_rows = sum(1 for _ in input_source)
# Rewind for Pass 2
input_source.seek(0)
first_row = input_source.readline().strip()
return total_rows, first_row
with open("data.txt", "r", encoding="utf-8") as f:
rows, first = process_multi_pass_data(f)
print(f"Processed {rows} rows. First row: {first}")
A common misconception is that files opened in append mode ('a' or 'a+') are not seekable. In fact, disk files in append mode do return seekable() == True because their underlying file descriptors support seeking. However, under POSIX semantics, write operations are always relocated to EOF immediately before writing.
Objects returned by urllib.request.urlopen() or streaming requests sessions do not support seeking. Calling seek() raises an io.UnsupportedOperation error unless buffered into io.BytesIO.
Run this complete test harness verifying seekability across disk files, memory buffers, and closed stream safeguards:
import tempfile
import os
import io
class SeekableVerifier:
@staticmethod
def is_seekable_stream(stream) -> bool:
try:
return not stream.closed and stream.seekable()
except (ValueError, AttributeError):
return False
# Verification unit assertions
with tempfile.TemporaryDirectory() as td:
v = SeekableVerifier()
test_file = os.path.join(td, "verify.txt")
with open(test_file, "w", encoding="utf-8") as f:
f.write("Random access test\n")
# Test 1: Disk file is seekable
with open(test_file, "r", encoding="utf-8") as f:
assert v.is_seekable_stream(f) is True
# Test 2: In-memory buffers are seekable
mem_b = io.BytesIO(b"123")
assert v.is_seekable_stream(mem_b) is True
# Test 3: Closed streams return False via guard
f_closed = open(test_file, "r", encoding="utf-8")
f_closed.close()
assert v.is_seekable_stream(f_closed) is False
print("All file.seekable() unit assertions passed successfully!")
Open in Google Colab View on GitHub
Run all file seekable exercises interactively, test seekable stream verification, build in-memory buffering adapters, and test defensive guards in Google Colab.
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.