diff options
-rw-r--r-- | Doc/library/tarfile.rst | 27 | ||||
-rw-r--r-- | Lib/tarfile.py | 27 | ||||
-rw-r--r-- | Lib/test/test_tarfile.py | 28 | ||||
-rw-r--r-- | Misc/NEWS | 2 |
4 files changed, 76 insertions, 8 deletions
diff --git a/Doc/library/tarfile.rst b/Doc/library/tarfile.rst index 8cf95dc..0002143 100644 --- a/Doc/library/tarfile.rst +++ b/Doc/library/tarfile.rst @@ -357,7 +357,7 @@ object, see :ref:`tarinfo-objects` for details. and :meth:`close`, and also supports iteration over its lines. -.. method:: TarFile.add(name, arcname=None, recursive=True, exclude=None) +.. method:: TarFile.add(name, arcname=None, recursive=True, exclude=None, filter=None) Add the file *name* to the archive. *name* may be any type of file (directory, fifo, symbolic link, etc.). If given, *arcname* specifies an alternative name @@ -365,7 +365,18 @@ object, see :ref:`tarinfo-objects` for details. can be avoided by setting *recursive* to :const:`False`. If *exclude* is given, it must be a function that takes one filename argument and returns a boolean value. Depending on this value the respective file is either excluded - (:const:`True`) or added (:const:`False`). + (:const:`True`) or added (:const:`False`). If *filter* is specified it must + be a function that takes a :class:`TarInfo` object argument and returns the + changed TarInfo object. If it instead returns :const:`None` the TarInfo + object will be excluded from the archive. See :ref:`tar-examples` for an + example. + + .. versionchanged:: 3.2 + Added the *filter* parameter. + + .. deprecated:: 3.2 + The *exclude* parameter is deprecated, please use the *filter* parameter + instead. .. method:: TarFile.addfile(tarinfo, fileobj=None) @@ -598,6 +609,18 @@ How to read a gzip compressed tar archive and display some member information:: print("something else.") tar.close() +How to create an archive and reset the user information using the *filter* +parameter in :meth:`TarFile.add`:: + + import tarfile + def reset(tarinfo): + tarinfo.uid = tarinfo.gid = 0 + tarinfo.uname = tarinfo.gname = "root" + return tarinfo + tar = tarfile.open("sample.tar.gz", "w:gz") + tar.add("foo", filter=reset) + tar.close() + .. _tar-formats: diff --git a/Lib/tarfile.py b/Lib/tarfile.py index ebaffd9..7b7c25e 100644 --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -1898,13 +1898,16 @@ class TarFile(object): print("link to", tarinfo.linkname, end=' ') print() - def add(self, name, arcname=None, recursive=True, exclude=None): + def add(self, name, arcname=None, recursive=True, exclude=None, filter=None): """Add the file `name' to the archive. `name' may be any type of file (directory, fifo, symbolic link, etc.). If given, `arcname' specifies an alternative name for the file in the archive. Directories are added recursively by default. This can be avoided by setting `recursive' to False. `exclude' is a function that should - return True for each filename to be excluded. + return True for each filename to be excluded. `filter' is a function + that expects a TarInfo object argument and returns the changed + TarInfo object, if it returns None the TarInfo object will be + excluded from the archive. """ self._check("aw") @@ -1912,9 +1915,13 @@ class TarFile(object): arcname = name # Exclude pathnames. - if exclude is not None and exclude(name): - self._dbg(2, "tarfile: Excluded %r" % name) - return + if exclude is not None: + import warnings + warnings.warn("use the filter argument instead", + DeprecationWarning, 2) + if exclude(name): + self._dbg(2, "tarfile: Excluded %r" % name) + return # Skip if somebody tries to archive the archive... if self.name is not None and os.path.abspath(name) == self.name: @@ -1930,6 +1937,13 @@ class TarFile(object): self._dbg(1, "tarfile: Unsupported type %r" % name) return + # Change or exclude the TarInfo object. + if filter is not None: + tarinfo = filter(tarinfo) + if tarinfo is None: + self._dbg(2, "tarfile: Excluded %r" % name) + return + # Append the tar header and data to the archive. if tarinfo.isreg(): f = bltn_open(name, "rb") @@ -1940,7 +1954,8 @@ class TarFile(object): self.addfile(tarinfo) if recursive: for f in os.listdir(name): - self.add(os.path.join(name, f), os.path.join(arcname, f), recursive, exclude) + self.add(os.path.join(name, f), os.path.join(arcname, f), + recursive, exclude, filter) else: self.addfile(tarinfo) diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py index 83f4a8c..e916879 100644 --- a/Lib/test/test_tarfile.py +++ b/Lib/test/test_tarfile.py @@ -659,6 +659,34 @@ class WriteTest(WriteTestBase): finally: shutil.rmtree(tempdir) + def test_filter(self): + tempdir = os.path.join(TEMPDIR, "filter") + os.mkdir(tempdir) + try: + for name in ("foo", "bar", "baz"): + name = os.path.join(tempdir, name) + open(name, "wb").close() + + def filter(tarinfo): + if os.path.basename(tarinfo.name) == "bar": + return + tarinfo.uid = 123 + tarinfo.uname = "foo" + return tarinfo + + tar = tarfile.open(tmpname, self.mode, encoding="iso8859-1") + tar.add(tempdir, arcname="empty_dir", filter=filter) + tar.close() + + tar = tarfile.open(tmpname, "r") + for tarinfo in tar: + self.assertEqual(tarinfo.uid, 123) + self.assertEqual(tarinfo.uname, "foo") + self.assertEqual(len(tar.getmembers()), 3) + tar.close() + finally: + shutil.rmtree(tempdir) + # Guarantee that stored pathnames are not modified. Don't # remove ./ or ../ or double slashes. Still make absolute # pathnames relative. @@ -70,6 +70,8 @@ C-API Library ------- +- Issue #6856: Add a filter keyword argument to TarFile.add(). + - Issue #6888: pdb's alias command was broken when no arguments were given. - Issue #6857: Default format() alignment should be '>' for Decimal |