[3.6] bpo-28230: Document the pathlib support in tarfile and add tests. (#559)

diff --git a/Doc/library/tarfile.rst b/Doc/library/tarfile.rst
index d8f8097..337c061 100644
--- a/Doc/library/tarfile.rst
+++ b/Doc/library/tarfile.rst
@@ -146,6 +146,10 @@
    .. versionchanged:: 3.5
       The ``'x'`` (exclusive creation) mode was added.
 
+   .. versionchanged:: 3.6
+      The *name* parameter accepts a :term:`path-like object`.
+
+
 .. class:: TarFile
 
    Class for reading and writing tar archives. Do not use this class directly:
@@ -266,7 +270,8 @@
    All following arguments are optional and can be accessed as instance attributes
    as well.
 
-   *name* is the pathname of the archive. It can be omitted if *fileobj* is given.
+   *name* is the pathname of the archive. *name* may be a :term:`path-like object`.
+   It can be omitted if *fileobj* is given.
    In this case, the file object's :attr:`name` attribute is used if it exists.
 
    *mode* is either ``'r'`` to read from an existing archive, ``'a'`` to append
@@ -319,6 +324,10 @@
    .. versionchanged:: 3.5
       The ``'x'`` (exclusive creation) mode was added.
 
+   .. versionchanged:: 3.6
+      The *name* parameter accepts a :term:`path-like object`.
+
+
 .. classmethod:: TarFile.open(...)
 
    Alternative constructor. The :func:`tarfile.open` function is actually a
@@ -390,14 +399,17 @@
    .. versionchanged:: 3.5
       Added the *numeric_owner* parameter.
 
+   .. versionchanged:: 3.6
+      The *path* parameter accepts a :term:`path-like object`.
+
 
 .. method:: TarFile.extract(member, path="", set_attrs=True, *, numeric_owner=False)
 
    Extract a member from the archive to the current working directory, using its
    full name. Its file information is extracted as accurately as possible. *member*
    may be a filename or a :class:`TarInfo` object. You can specify a different
-   directory using *path*. File attributes (owner, mtime, mode) are set unless
-   *set_attrs* is false.
+   directory using *path*. *path* may be a :term:`path-like object`.
+   File attributes (owner, mtime, mode) are set unless *set_attrs* is false.
 
    If *numeric_owner* is :const:`True`, the uid and gid numbers from the tarfile
    are used to set the owner/group for the extracted files. Otherwise, the named
@@ -418,6 +430,10 @@
    .. versionchanged:: 3.5
       Added the *numeric_owner* parameter.
 
+   .. versionchanged:: 3.6
+      The *path* parameter accepts a :term:`path-like object`.
+
+
 .. method:: TarFile.extractfile(member)
 
    Extract a member from the archive as a file object. *member* may be a filename
@@ -464,7 +480,8 @@
 
    Create a :class:`TarInfo` object from the result of :func:`os.stat` or
    equivalent on an existing file.  The file is either named by *name*, or
-   specified as a :term:`file object` *fileobj* with a file descriptor.  If
+   specified as a :term:`file object` *fileobj* with a file descriptor.
+   *name* may be a :term:`path-like object`.  If
    given, *arcname* specifies an alternative name for the file in the
    archive, otherwise, the name is taken from *fileobj*’s
    :attr:`~io.FileIO.name` attribute, or the *name* argument.  The name
@@ -478,6 +495,9 @@
    The :attr:`~TarInfo.name` may also be modified, in which case *arcname*
    could be a dummy string.
 
+   .. versionchanged:: 3.6
+      The *name* parameter accepts a :term:`path-like object`.
+
 
 .. method:: TarFile.close()
 
diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py
index 619cbc0..fc79055 100644
--- a/Lib/test/test_tarfile.py
+++ b/Lib/test/test_tarfile.py
@@ -4,6 +4,7 @@
 from hashlib import md5
 from contextlib import contextmanager
 from random import Random
+import pathlib
 
 import unittest
 import unittest.mock
@@ -440,6 +441,22 @@
                 self.assertIsInstance(tar.name, bytes)
                 self.assertEqual(tar.name, os.path.abspath(fobj.name))
 
+    def test_pathlike_name(self):
+        tarname = pathlib.Path(self.tarname)
+        with tarfile.open(tarname, mode=self.mode) as tar:
+            self.assertIsInstance(tar.name, str)
+            self.assertEqual(tar.name, os.path.abspath(os.fspath(tarname)))
+        with self.taropen(tarname) as tar:
+            self.assertIsInstance(tar.name, str)
+            self.assertEqual(tar.name, os.path.abspath(os.fspath(tarname)))
+        with tarfile.TarFile.open(tarname, mode=self.mode) as tar:
+            self.assertIsInstance(tar.name, str)
+            self.assertEqual(tar.name, os.path.abspath(os.fspath(tarname)))
+        if self.suffix == '':
+            with tarfile.TarFile(tarname, mode='r') as tar:
+                self.assertIsInstance(tar.name, str)
+                self.assertEqual(tar.name, os.path.abspath(os.fspath(tarname)))
+
     def test_illegal_mode_arg(self):
         with open(tmpname, 'wb'):
             pass
@@ -582,6 +599,26 @@
         finally:
             support.rmtree(DIR)
 
+    def test_extractall_pathlike_name(self):
+        DIR = pathlib.Path(TEMPDIR) / "extractall"
+        with support.temp_dir(DIR), \
+             tarfile.open(tarname, encoding="iso8859-1") as tar:
+            directories = [t for t in tar if t.isdir()]
+            tar.extractall(DIR, directories)
+            for tarinfo in directories:
+                path = DIR / tarinfo.name
+                self.assertEqual(os.path.getmtime(path), tarinfo.mtime)
+
+    def test_extract_pathlike_name(self):
+        dirtype = "ustar/dirtype"
+        DIR = pathlib.Path(TEMPDIR) / "extractall"
+        with support.temp_dir(DIR), \
+             tarfile.open(tarname, encoding="iso8859-1") as tar:
+            tarinfo = tar.getmember(dirtype)
+            tar.extract(tarinfo, path=DIR)
+            extracted = DIR / dirtype
+            self.assertEqual(os.path.getmtime(extracted), tarinfo.mtime)
+
     def test_init_close_fobj(self):
         # Issue #7341: Close the internal file object in the TarFile
         # constructor in case of an error. For the test we rely on
@@ -1092,6 +1129,17 @@
         finally:
             support.rmdir(path)
 
+    def test_gettarinfo_pathlike_name(self):
+        with tarfile.open(tmpname, self.mode) as tar:
+            path = pathlib.Path(TEMPDIR) / "file"
+            with open(path, "wb") as fobj:
+                fobj.write(b"aaa")
+            tarinfo = tar.gettarinfo(path)
+            tarinfo2 = tar.gettarinfo(os.fspath(path))
+            self.assertIsInstance(tarinfo.name, str)
+            self.assertEqual(tarinfo.name, tarinfo2.name)
+            self.assertEqual(tarinfo.size, 3)
+
     @unittest.skipUnless(hasattr(os, "link"),
                          "Missing hardlink implementation")
     def test_link_size(self):
@@ -1528,6 +1576,34 @@
         self.assertEqual(len(names), 1)
         self.assertIn("spameggs42", names[0])
 
+    def test_create_pathlike_name(self):
+        with tarfile.open(pathlib.Path(tmpname), self.mode) as tobj:
+            self.assertIsInstance(tobj.name, str)
+            self.assertEqual(tobj.name, os.path.abspath(tmpname))
+            tobj.add(pathlib.Path(self.file_path))
+            names = tobj.getnames()
+        self.assertEqual(len(names), 1)
+        self.assertIn('spameggs42', names[0])
+
+        with self.taropen(tmpname) as tobj:
+            names = tobj.getnames()
+        self.assertEqual(len(names), 1)
+        self.assertIn('spameggs42', names[0])
+
+    def test_create_taropen_pathlike_name(self):
+        with self.taropen(pathlib.Path(tmpname), "x") as tobj:
+            self.assertIsInstance(tobj.name, str)
+            self.assertEqual(tobj.name, os.path.abspath(tmpname))
+            tobj.add(pathlib.Path(self.file_path))
+            names = tobj.getnames()
+        self.assertEqual(len(names), 1)
+        self.assertIn('spameggs42', names[0])
+
+        with self.taropen(tmpname) as tobj:
+            names = tobj.getnames()
+        self.assertEqual(len(names), 1)
+        self.assertIn('spameggs42', names[0])
+
 
 class GzipCreateTest(GzipTest, CreateTest):
     pass