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.
This commit is contained in:
lyc8503 2024-04-19 19:41:51 +08:00 committed by GitHub
parent 3e7d990a09
commit 15b3555e4a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 25 additions and 9 deletions

View file

@ -637,11 +637,15 @@ be finalized; only the internally used file object will be closed. See the
.. method:: TarFile.addfile(tarinfo, fileobj=None) .. method:: TarFile.addfile(tarinfo, fileobj=None)
Add the :class:`TarInfo` object *tarinfo* to the archive. If *fileobj* is given, Add the :class:`TarInfo` object *tarinfo* to the archive. If *tarinfo* represents
it should be a :term:`binary file`, and a non zero-size regular file, the *fileobj* argument should be a :term:`binary file`,
``tarinfo.size`` bytes are read from it and added to the archive. You can and ``tarinfo.size`` bytes are read from it and added to the archive. You can
create :class:`TarInfo` objects directly, or by using :meth:`gettarinfo`. create :class:`TarInfo` objects directly, or by using :meth:`gettarinfo`.
.. versionchanged:: 3.13
*fileobj* must be given for non-zero-sized regular files.
.. method:: TarFile.gettarinfo(name=None, arcname=None, fileobj=None) .. method:: TarFile.gettarinfo(name=None, arcname=None, fileobj=None)

View file

@ -2214,13 +2214,16 @@ class TarFile(object):
self.addfile(tarinfo) self.addfile(tarinfo)
def addfile(self, tarinfo, fileobj=None): def addfile(self, tarinfo, fileobj=None):
"""Add the TarInfo object `tarinfo' to the archive. If `fileobj' is """Add the TarInfo object `tarinfo' to the archive. If `tarinfo' represents
given, it should be a binary file, and tarinfo.size bytes are read a non zero-size regular file, the `fileobj' argument should be a binary file,
from it and added to the archive. You can create TarInfo objects and tarinfo.size bytes are read from it and added to the archive.
directly, or by using gettarinfo(). You can create TarInfo objects directly, or by using gettarinfo().
""" """
self._check("awx") self._check("awx")
if fileobj is None and tarinfo.isreg() and tarinfo.size != 0:
raise ValueError("fileobj not provided for non zero-size regular file")
tarinfo = copy.copy(tarinfo) tarinfo = copy.copy(tarinfo)
buf = tarinfo.tobuf(self.format, self.encoding, self.errors) buf = tarinfo.tobuf(self.format, self.encoding, self.errors)

View file

@ -1612,6 +1612,12 @@ class WriteTest(WriteTestBase, unittest.TestCase):
pax_headers={'non': 'empty'}) pax_headers={'non': 'empty'})
self.assertFalse(f.closed) self.assertFalse(f.closed)
def test_missing_fileobj(self):
with tarfile.open(tmpname, self.mode) as tar:
tarinfo = tar.gettarinfo(tarname)
with self.assertRaises(ValueError):
tar.addfile(tarinfo)
class GzipWriteTest(GzipTest, WriteTest): class GzipWriteTest(GzipTest, WriteTest):
pass pass
@ -3283,7 +3289,8 @@ class NoneInfoTests_Misc(unittest.TestCase):
tar = tarfile.open(fileobj=bio, mode='w', format=tarformat) tar = tarfile.open(fileobj=bio, mode='w', format=tarformat)
tarinfo = tar.gettarinfo(tarname) tarinfo = tar.gettarinfo(tarname)
try: try:
tar.addfile(tarinfo) with open(tarname, 'rb') as f:
tar.addfile(tarinfo, f)
except Exception: except Exception:
if tarformat == tarfile.USTAR_FORMAT: if tarformat == tarfile.USTAR_FORMAT:
# In the old, limited format, adding might fail for # In the old, limited format, adding might fail for
@ -3298,7 +3305,8 @@ class NoneInfoTests_Misc(unittest.TestCase):
replaced = tarinfo.replace(**{attr_name: None}) replaced = tarinfo.replace(**{attr_name: None})
with self.assertRaisesRegex(ValueError, with self.assertRaisesRegex(ValueError,
f"{attr_name}"): f"{attr_name}"):
tar.addfile(replaced) with open(tarname, 'rb') as f:
tar.addfile(replaced, f)
def test_list(self): def test_list(self):
# Change some metadata to None, then compare list() output # Change some metadata to None, then compare list() output

View file

@ -0,0 +1 @@
Add parameter *fileobj* check for :func:`tarfile.addfile()`