Checking Random Access in Python: Verifying Streams with seekable()

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.

1. Method Syntax & Signature 🔝

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.

2. Seekable vs. Unseekable Streams Matrix 🔝

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.

3. Defensive Programming: Preventing Illegal Seek Exceptions 🔝

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:

Pre-Flight Seekability Guard
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))

4. In-Memory Buffering Adapter for Unseekable Streams 🔝

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:

Adapter Pattern: Making Any Stream Seekable
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

5. Closed Stream Handling: The ValueError Trap 🔝

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

6. Real-World Engineering Recipes 🔝

Recipe 1: Multi-Pass Data Validator Supporting Files & Pipes

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}")

7. Common Pitfalls & Boundary Traps 🔝

Trap 1: Assuming Append Mode ('a') Is Not Seekable

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.

Trap 2: Attempting seek() in Web/HTTP Response Streams

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.

8. Production Test Suite & Self-Checking Assertions 🔝

Run this complete test harness verifying seekability across disk files, memory buffers, and closed stream safeguards:

SeekableVerifier Test Harness
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!")

Practice in Google Colab & GitHub 🔝

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.




Subscribe to our YouTube Channel here



plus2net.com







Python Video Tutorials
Python SQLite Video Tutorials
Python MySQL Video Tutorials
Python Tkinter Video Tutorials
✖
We use cookies to improve your browsing experience. . Learn more
HTML MySQL PHP JavaScript ASP Photoshop Articles Contact us
© 2000-2026 plus2net.com All rights reserved worldwide Privacy Policy Disclaimer