]> granicus.if.org Git - python/commitdiff
Issue #6856: Add a filter keyword argument to TarFile.add().
authorLars Gustäbel <lars@gustaebel.de>
Sat, 12 Sep 2009 10:28:15 +0000 (10:28 +0000)
committerLars Gustäbel <lars@gustaebel.de>
Sat, 12 Sep 2009 10:28:15 +0000 (10:28 +0000)
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 29968396e01a76e16410032fc268781f701496da..fa6cca7cc2703daf9ca18c09547131e031b06a80 100644 (file)
@@ -389,7 +389,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
@@ -397,11 +397,22 @@ 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:: 2.6
       Added the *exclude* parameter.
 
+   .. versionchanged:: 2.7
+      Added the *filter* parameter.
+
+   .. deprecated:: 2.7
+      The *exclude* parameter is deprecated, please use the *filter* parameter
+      instead.
+
 
 .. method:: TarFile.addfile(tarinfo, fileobj=None)
 
@@ -653,6 +664,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 54a26062ed902468c4f70359414429aa426a0a97..a87fe225d17f63c51a9c78b2282b0ca65a84d705 100644 (file)
@@ -1918,13 +1918,16 @@ class TarFile(object):
                     print "link to", tarinfo.linkname,
             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")
 
@@ -1932,9 +1935,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:
@@ -1950,6 +1957,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")
@@ -1960,7 +1974,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 8c9b0ec3dd749b619800c8d08f598aa15dad162e..1240e1a97fcd5c7afd7bfc41997b7eb71b17cb82 100644 (file)
@@ -660,6 +660,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 4c382181483b04af000ad9f31df10d7b08046ee9..734bddf001bc5816df006dabd0548dc09101f77f 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -366,6 +366,8 @@ Core and Builtins
 Library
 -------
 
+- Issue #6856: Add a filter keyword argument to TarFile.add().
+
 - Issue #6163: Fixed HP-UX runtime library dir options in
   distutils.unixcompiler. Initial patch by Sridhar Ratnakumar and
   Michael Haubenwallner.