Skip to content

Commit 15b3555

Browse files
authored
gh-116931: Add fileobj parameter check for Tarfile.addfile (GH-117988)
Tarfile.addfile now throws an ValueError when the user passes in a non-zero size tarinfo but does not provide a fileobj, instead of writing an incomplete entry.
1 parent 3e7d990 commit 15b3555

File tree

4 files changed

+25
-9
lines changed

4 files changed

+25
-9
lines changed

Doc/library/tarfile.rst

+7-3
Original file line numberDiff line numberDiff line change
@@ -637,11 +637,15 @@ be finalized; only the internally used file object will be closed. See the
637637

638638
.. method:: TarFile.addfile(tarinfo, fileobj=None)
639639

640-
Add the :class:`TarInfo` object *tarinfo* to the archive. If *fileobj* is given,
641-
it should be a :term:`binary file`, and
642-
``tarinfo.size`` bytes are read from it and added to the archive. You can
640+
Add the :class:`TarInfo` object *tarinfo* to the archive. If *tarinfo* represents
641+
a non zero-size regular file, the *fileobj* argument should be a :term:`binary file`,
642+
and ``tarinfo.size`` bytes are read from it and added to the archive. You can
643643
create :class:`TarInfo` objects directly, or by using :meth:`gettarinfo`.
644644

645+
.. versionchanged:: 3.13
646+
647+
*fileobj* must be given for non-zero-sized regular files.
648+
645649

646650
.. method:: TarFile.gettarinfo(name=None, arcname=None, fileobj=None)
647651

Lib/tarfile.py

+7-4
Original file line numberDiff line numberDiff line change
@@ -2214,13 +2214,16 @@ def add(self, name, arcname=None, recursive=True, *, filter=None):
22142214
self.addfile(tarinfo)
22152215

22162216
def addfile(self, tarinfo, fileobj=None):
2217-
"""Add the TarInfo object `tarinfo' to the archive. If `fileobj' is
2218-
given, it should be a binary file, and tarinfo.size bytes are read
2219-
from it and added to the archive. You can create TarInfo objects
2220-
directly, or by using gettarinfo().
2217+
"""Add the TarInfo object `tarinfo' to the archive. If `tarinfo' represents
2218+
a non zero-size regular file, the `fileobj' argument should be a binary file,
2219+
and tarinfo.size bytes are read from it and added to the archive.
2220+
You can create TarInfo objects directly, or by using gettarinfo().
22212221
"""
22222222
self._check("awx")
22232223

2224+
if fileobj is None and tarinfo.isreg() and tarinfo.size != 0:
2225+
raise ValueError("fileobj not provided for non zero-size regular file")
2226+
22242227
tarinfo = copy.copy(tarinfo)
22252228

22262229
buf = tarinfo.tobuf(self.format, self.encoding, self.errors)

Lib/test/test_tarfile.py

+10-2
Original file line numberDiff line numberDiff line change
@@ -1612,6 +1612,12 @@ def write(self, data):
16121612
pax_headers={'non': 'empty'})
16131613
self.assertFalse(f.closed)
16141614

1615+
def test_missing_fileobj(self):
1616+
with tarfile.open(tmpname, self.mode) as tar:
1617+
tarinfo = tar.gettarinfo(tarname)
1618+
with self.assertRaises(ValueError):
1619+
tar.addfile(tarinfo)
1620+
16151621

16161622
class GzipWriteTest(GzipTest, WriteTest):
16171623
pass
@@ -3283,7 +3289,8 @@ def test_add(self):
32833289
tar = tarfile.open(fileobj=bio, mode='w', format=tarformat)
32843290
tarinfo = tar.gettarinfo(tarname)
32853291
try:
3286-
tar.addfile(tarinfo)
3292+
with open(tarname, 'rb') as f:
3293+
tar.addfile(tarinfo, f)
32873294
except Exception:
32883295
if tarformat == tarfile.USTAR_FORMAT:
32893296
# In the old, limited format, adding might fail for
@@ -3298,7 +3305,8 @@ def test_add(self):
32983305
replaced = tarinfo.replace(**{attr_name: None})
32993306
with self.assertRaisesRegex(ValueError,
33003307
f"{attr_name}"):
3301-
tar.addfile(replaced)
3308+
with open(tarname, 'rb') as f:
3309+
tar.addfile(replaced, f)
33023310

33033311
def test_list(self):
33043312
# Change some metadata to None, then compare list() output
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add parameter *fileobj* check for :func:`tarfile.addfile()`

0 commit comments

Comments
 (0)