tarfile: extractfile(member).read() of a sparse member is slow due to repeated bytes concatenation
Maintainers usually reply within 1 day
@devdavidejesus is already working on this.
Since Oct 29, 2026.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 75/100
Research direction
Read _FileInFile.read() in Lib/tarfile.py and trace how it appends data segments and sparse holes. Check the existing tarfile tests for sparse members, then add coverage for extractfile().read() and chunked reads against the expected content. Done means both read patterns return the correct content and the full read avoids repeated bytes concatenation.
Written by the indexing model from the issue text.
Description
Bug report
_FileInFile.read() in Lib/tarfile.py builds its result with buf += b for each data segment and buf += NUL * length for each hole of a sparse member. Each bytes concatenation copies the whole accumulated buffer, so reading a sparse member with TarFile.extractfile(member).read() takes time proportional to the member size times the number of sparse segments.
Holes take no space in the archive, so a small archive is enough to make this noticeable. extract(), extractall() and reading the member in small chunks (for example with shutil.copyfileobj()) are not affected.
Reproducer
import io, tarfile, time
def build(n, blk=4096):
# GNU sparse 0.1 (pax) member: n data blocks separated by holes.
t = tarfile.TarInfo("GNUSparseFile.0/a.img")
t.size = n * blk
t.pax_headers = {
"GNU.sparse.map": ",".join(f"{2 * i * blk},{blk}" for i in range(n)),
"GNU.sparse.name": "a.img",
"GNU.sparse.realsize": str(2 * n * blk),
}
bio = io.BytesIO()
with tarfile.open(fileobj=bio, mode="w", format=tarfile.PAX_FORMAT) as tf:
tf.addfile(t, io.BytesIO(b"D" * t.size))
return bio.getvalue()
def read_time(n):
with tarfile.open(fileobj=io.BytesIO(build(n))) as tf:
f = tf.extractfile(tf.next())
t0 = time.perf_counter()
f.read()
return time.perf_counter() - t0
for n in (1000, 2000, 4000):
print(f"{n} segments: {read_time(n):.2f}s")
On a release build (macOS, Apple M3) the read of a sparse member with 4000 segments takes several seconds; the time roughly quadruples when the segment count doubles. With the change below each read is a fraction of a second.
Suggested fix
Accumulate the pieces in a list and join once, instead of repeated concatenation:
- buf = b""
+ chunks = []
while size > 0:
...
- buf += b
+ chunks.append(b)
else:
- buf += NUL * length
+ chunks.append(NUL * length)
size -= length
self.position += length
- return buf
+ return b"".join(chunks)
I have a branch with this change and a regression test that reads a multi-segment sparse member through extractfile().read() and in chunks, and compares both to the expected content. I can open a PR if this looks right.
- Dominant language
- Python
- Stars
- 77.8k
- Forks
- 37.7k
- Avg merge
- 11h 25m
- Merged PRs (30d)
- 723
Getting set up
Starts the project's dev container in your browser, under your own GitHub account.
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from python/cpython
-
Null dereferencing of a pointer in initconfig.cPossibly taken @lazerg claimed this today. Openinterpreter-core topic-C-API type-bug
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
python/cpython#160962 · 4 comments ·
Maintainers usually reply within 1 day
-
tests topic-profiling type-bug
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
python/cpython#160881 · 1 comment ·
Maintainers usually reply within 1 day
-
3.16 build pending type-bug
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
python/cpython#160829 · 5 comments ·
Maintainers usually reply within 1 day
-
dependencies infra topic-ensurepip
Difficulty 2/5 1-3 hours Newbie friendliness 66/100
Maintainers usually reply within 1 day
-
symtable: repr() of a Symbol raises TypeError under -X lazy_imports=allPossibly taken @anistark claimed this 2 days ago. Openstdlib topic-lazy-imports type-bug
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
python/cpython#160810 · 4 comments ·
Maintainers usually reply within 1 day
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
vllm-project/vllm#64845 ·
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
dekobon/big-code-analysis#1729 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
pydantic/pydantic-ai#12091 ·
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100