]> granicus.if.org Git - python/commitdiff
Merged revisions 74750 via svnmerge from
authorLars Gustäbel <lars@gustaebel.de>
Sat, 12 Sep 2009 10:44:00 +0000 (10:44 +0000)
committerLars Gustäbel <lars@gustaebel.de>
Sat, 12 Sep 2009 10:44:00 +0000 (10:44 +0000)
svn+ssh://pythondev@svn.python.org/python/trunk

........
  r74750 | lars.gustaebel | 2009-09-12 12:28:15 +0200 (Sat, 12 Sep 2009) | 9 lines

  Issue #6856: Add a filter keyword argument to TarFile.add().

  The filter argument must be a function that takes a TarInfo
  object argument, changes it and returns it again. If the
  function returns None the TarInfo object will be excluded
  from the archive.
  The exclude argument is deprecated from now on, because it
  does something similar but is not as flexible.
........

Doc/library/tarfile.rst
Lib/tarfile.py
Lib/test/test_tarfile.py
Misc/NEWS

index 8cf95dc0f96c50389710a8c160e52405e40e787a..0002143341592f0f16da0a79cba00a80beb0d575 100644 (file)
@@ -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:
 
index ebaffd99a7686131e7fa4af6c973963d767d5865..7b7c25e831a7625ac6d8129fee7f48175ec36aea 100644 (file)
@@ -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)
index 83f4a8cd58034ead18531425ea328e6bf3151ea0..e916879ba4be39ad8af0906679d863db54fadce9 100644 (file)
@@ -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.
index fe856ae83d0a79ccdebbf0d42636f4e4c477bccf..e1733e0fe8f06319b320dcd16fc4dcef5ec480b6 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -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