Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions pygit2/blob.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,20 @@ def readinto(self, b, /):

def close(self) -> None:
try:
self._ready.wait()
self._writer_closed.wait()
while self._queue is not None and not self._queue.empty():
self._queue.get()
# The writer thread may be blocked in queue.put() because the
# queue (maxsize=1) still holds a chunk that was never consumed
# (e.g. the reader stopped before reaching EOF). Draining the
# queue must happen *before* (not after) waiting for
# `_writer_closed`, otherwise the writer can never make progress
# to reach its close callback and this would deadlock.
while True:
self._ready.wait()
while self._queue is not None and not self._queue.empty():
self._queue.get()
if self._writer_closed.is_set():
# Done
break
self._ready.clear()
self._thread.join()
except KeyboardInterrupt:
pass
Expand Down
6 changes: 6 additions & 0 deletions test/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ def testrepo(tmp_path: Path) -> Generator[Repository, None, None]:
yield pygit2.Repository(path)


@pytest.fixture
def bigrepo(tmp_path: Path) -> Generator[Repository, None, None]:
with utils.TemporaryRepository('bigrepo.zip', tmp_path) as path:
yield pygit2.Repository(path)


@pytest.fixture
def testrepo_path(tmp_path: Path) -> Generator[tuple[Repository, Path], None, None]:
with utils.TemporaryRepository('testrepo.zip', tmp_path) as path:
Expand Down
Binary file added test/data/bigrepo.zip
Binary file not shown.
13 changes: 13 additions & 0 deletions test/test_blob.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,3 +306,16 @@ def test_blob_write_to_queue_invalid_commit_id_str(testrepo: Repository) -> None
flags=BlobFilter.ATTRIBUTES_FROM_COMMIT,
commit_id='not-a-valid-oid', # type: ignore[arg-type]
)


def test_blob_partial_read(bigrepo: Repository) -> None:
blob_oid = bigrepo.create_blob_fromworkdir('big.txt')
blob = bigrepo[blob_oid]
assert isinstance(blob, pygit2.Blob)
reader = pygit2.BlobIO(blob)
# Read only a few lines then break early
for i, line in enumerate(reader):
if i >= 3:
break
reader.close()
assert not reader.raw._thread.is_alive() # type: ignore[attr-defined]
Loading