Skip to content

Bugfix: readinto/readline/readlines SubFile methods EOF behavior#41

Merged
pressler-vsc merged 1 commit intomainfrom
bugfix/subfile-methods
Feb 2, 2026
Merged

Bugfix: readinto/readline/readlines SubFile methods EOF behavior#41
pressler-vsc merged 1 commit intomainfrom
bugfix/subfile-methods

Conversation

@pressler-vsc
Copy link
Contributor

Description

While trying to work with an image segment as a SubFile, I found that certain methods did not behave as I expected (e.g. they were going beyond the SubFile's length). To demonstrate this, I split out the readinto, readline, and readlines checks into their own tests and then run in main:

run new tests in main
$ pytest test/test_core.py
=================================================================================================================================================================== test session starts ====================================================================================================================================================================
platform linux -- Python 3.14.0, pytest-8.4.2, pluggy-1.6.0
rootdir: /home/vscuser/git/glweb/software/third-party/jbpy
configfile: pyproject.toml
collected 38 items                                                                                                                                                                                                                                                                                                                                         

test/test_core.py ...................................FFF                                                                                                                                                                                                                                                                                             [100%]

========================================================================================================================================================================= FAILURES =========================================================================================================================================================================
__________________________________________________________________________________________________________________________________________________________________ test_subfile_readinto ___________________________________________________________________________________________________________________________________________________________________

    def test_subfile_readinto():
        all_data = bytearray(
            "".join(random.choices(string.ascii_letters + string.digits, k=1000)).encode()
        )
        start = 400
        length = 400
        f_all = io.BytesIO(all_data)
        f_io = io.BytesIO(all_data[start : start + length])
        f_jb = jbpy.core.SubFile(f_all, start, length)
    
        # read small piece
        b_io = bytearray(50)
        b_jb = bytearray(50)
        assert f_jb.readinto(b_jb) == f_io.readinto(b_io)
        assert b_jb == b_io
    
        # read rest of subfile into too large of a buffer
        b_io.resize(length * 2)
        b_jb.resize(length * 2)
>       assert f_jb.readinto(b_jb) == f_io.readinto(b_io)
E       AssertionError: assert 550 == 350
E        +  where 550 = readinto(bytearray(b'savX7XNsqHGVo46G9DgtyA95ICwdVB7fYsd3eXDyj0UQ6Lhgyz3qyAclZpG8STajKvmyLjAFKm2PVB5RRw13a8ElNmna1Rh2Xcz6qVhHWE...0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'))
E        +    where readinto = <jbpy.core.SubFile object at 0x7f492a333b10>.readinto
E        +  and   350 = <built-in method readinto of _io.BytesIO object at 0x7f4929c23d80>(bytearray(b'savX7XNsqHGVo46G9DgtyA95ICwdVB7fYsd3eXDyj0UQ6Lhgyz3qyAclZpG8STajKvmyLjAFKm2PVB5RRw13a8ElNmna1Rh2Xcz6qVhHWE...0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'))
E        +    where <built-in method readinto of _io.BytesIO object at 0x7f4929c23d80> = <_io.BytesIO object at 0x7f4929c23d80>.readinto

test/test_core.py:809: AssertionError
__________________________________________________________________________________________________________________________________________________________________ test_subfile_readline ___________________________________________________________________________________________________________________________________________________________________

    def test_subfile_readline():
        all_data = bytearray(
            "".join(random.choices(string.ascii_letters + string.digits, k=1000)).encode()
        )
        newline_positions = (500, 550, 600, 650, 700)
        for pos in newline_positions:
            all_data[pos] = ord("\n")
    
        start = 400
        length = 400
        f_all = io.BytesIO(all_data)
        f_io = io.BytesIO(all_data[start : start + length])
        f_jb = jbpy.core.SubFile(f_all, start, length)
    
        # Hit size limit, don't read full line
        assert f_jb.readline(3) == f_io.readline(3) == all_data[start : start + 3]
        # Set limit way too high, hit first newline
        assert (
            f_jb.readline(3 * length)
            == f_io.readline(3 * length)
            == all_data[start + 3 : 501]
        )
        # Read the rest of the lines
        for _ in range(len(newline_positions)):
>           assert f_jb.readline() == (b := f_io.readline())
E           AssertionError: assert b'f9Yb2BS58hi...fXgTsYfZLfFJh' == b''
E             
E             Use -v to get more diff

test/test_core.py:844: AssertionError
__________________________________________________________________________________________________________________________________________________________________ test_subfile_readlines __________________________________________________________________________________________________________________________________________________________________

    def test_subfile_readlines():
        all_data = bytearray(
            "".join(random.choices(string.ascii_letters + string.digits, k=1000)).encode()
        )
        newline_positions = (500, 550, 600, 650, 700)
        for pos in newline_positions:
            all_data[pos] = ord("\n")
    
        start = 400
        length = 400
        f_all = io.BytesIO(all_data)
        f_io = io.BytesIO(all_data[start : start + length])
        f_jb = jbpy.core.SubFile(f_all, start, length)
    
        # Hit size limit, only read first line
        assert (
            f_jb.readlines(1)
            == f_io.readlines(1)
            == [all_data[start : newline_positions[0] + 1]]
        )
        # Hit size limit, read next two lines
        assert f_jb.readlines(70) == (l_io := f_io.readlines(70))
        assert len(l_io) == 2
        # Read the rest
>       assert f_jb.readlines() == (l_io := f_io.readlines())
E       AssertionError: assert [b'goyJSeNhK2...LkPpp9juFT8k'] == []
E         
E         Left contains 3 more items, first extra item: b'goyJSeNhK21TkFog8R9vcIzSXjkeOCtnht7NAHB8rM2XkpsY3\n'
E         Use -v to get more diff

test/test_core.py:875: AssertionError
================================================================================================================================================================= short test summary info ==================================================================================================================================================================
FAILED test/test_core.py::test_subfile_readinto - AssertionError: assert 550 == 350
FAILED test/test_core.py::test_subfile_readline - AssertionError: assert b'f9Yb2BS58hi...fXgTsYfZLfFJh' == b''
FAILED test/test_core.py::test_subfile_readlines - AssertionError: assert [b'goyJSeNhK2...LkPpp9juFT8k'] == []
=============================================================================================================================================================== 3 failed, 35 passed in 0.76s ===============================================================================================================================================================
                                                                                                                                                                                                                                                                                                                                                            

@pressler-vsc pressler-vsc merged commit 0729c7d into main Feb 2, 2026
5 checks passed
@pressler-vsc pressler-vsc deleted the bugfix/subfile-methods branch February 2, 2026 15:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants