List of commits:
Subject Hash Author Date (UTC)
bpo-28231: The zipfile module now accepts path-like objects for external paths. (#511) 8606e9524a7a4065042f7f228dc57eb74f88e4d3 Serhiy Storchaka 2017-03-08 12:37:51
bpo-28331: fix impl-detail label is removed when content is translated. (GH-195) c351ce6a2c923c5016e48ecbf7b1e4833031d154 INADA Naoki 2017-03-08 10:07:13
bpo-28230: Document the pathlib support in tarfile and add tests. (#512) c45cd167d403d7d98078d5fc4a37b16195dc7a35 Serhiy Storchaka 2017-03-08 08:32:44
Revert "bpo-29571: Use correct locale encoding in test_re (#149)" (#554) 21a74312f2d1ddee71fade709af49d078085ec30 Benjamin Peterson 2017-03-08 06:48:09
allow the first call to wcsxfrm to return ERANGE (#536) ad4a0cc519a5bb14204324e0e32e976318f9e6ce Benjamin Peterson 2017-03-08 06:24:44
make the glibc alias table take precedence over the X11 one (#422) 02371e0ed1ee82ec73e7d363bcf2ed40cde1397a Benjamin Peterson 2017-03-08 06:03:13
bpo-29568: Disable any characters between two percents for escaped percent "%%" in the format string for classic string formatting. (GH-513) 9f8ad3f39e0a92ed37d012b9dd237399524f0d51 Serhiy Storchaka 2017-03-08 03:51:19
bpo-24329: allow __qualname__ and __classcell__ in __slots__ (GH-495) c393ee858932f79bd6dabf31550f9a53ea90bc68 Xiang Zhang 2017-03-08 03:18:49
bpo-26915: Test identity first in membership operation in index() and count() methods of collections.abc.Sequence (GH-503) d5d3249e8a37936d32266fa06ac20017307a1f70 Xiang Zhang 2017-03-08 03:04:24
bpo-28682: Added support for bytes paths in os.fwalk(). (#489) 8f6b344d368c15c3fe56c65c2f2776e7766fef55 Serhiy Storchaka 2017-03-07 12:33:21
PCbuild: Add -q option to svn export (GH-535) 8886d5f39286dffa7d9337857b151e7fb4af23fd INADA Naoki 2017-03-07 06:34:38
bpo-29676: fix lsprof can't profile C method call. (GH523) 93fac8dd358cd0e85e7b59115db226ce685d3f6f INADA Naoki 2017-03-07 05:24:37
bpo-28728: clarify possible test failure due to ISP (GH-412) d36a71637cefdddc02efd884f1b2c204f370afaa Xiang Zhang 2017-03-07 03:06:09
Exclude myself from mention-bot (#529) fea967658d2dff1e2afc45311e1ee6a40e3176c2 Victor Stinner 2017-03-07 01:51:47
bpo-29737: Optimize concatenating with empty tuple. (#524) 98e80c2babac0003182c3482c6c5437ea111e795 Serhiy Storchaka 2017-03-06 21:39:35
bpo-15954: Check return code of wcsxfrm(). (#508) be487a65f18e1be5fde03e2977fff4be53cc2fbf Serhiy Storchaka 2017-03-06 19:21:41
bpo-29695: Fixed tests after removing keyword args support in some basic type constructors. (GH-520) d908fd9ee1c307f7066023eb2031c0f509036cbc Serhiy Storchaka 2017-03-06 19:08:59
Ignore What's New for MentionBot (GH-521) 0f5f1c3055e4c5b3d6165f56507bae6e16af7ca8 Matthias Bussonnier 2017-03-06 18:56:58
The mention-bot is too exuberant for my taste. (#522) 4e0f612a1fc8444fe4fef3231f6934379e302a26 Stefan Krah 2017-03-06 17:28:29
bpo-29695: Remove bad keyword parameters in int(), bool(), float(), list() and tuple(). (#518) 2e5642422f6234fd8d0c082142b27340e588f96e Serhiy Storchaka 2017-03-06 15:01:06
Commit 8606e9524a7a4065042f7f228dc57eb74f88e4d3 - bpo-28231: The zipfile module now accepts path-like objects for external paths. (#511)
Author: Serhiy Storchaka
Author date (UTC): 2017-03-08 12:37
Committer name: GitHub
Committer date (UTC): 2017-03-08 12:37
Parent(s): c351ce6a2c923c5016e48ecbf7b1e4833031d154
Signer:
Signing key:
Signing status: N
Tree: 84f0e487deb92a304f8a584e59217d3b25351f98
File Lines added Lines deleted
Doc/library/zipfile.rst 22 2
Lib/test/test_zipfile.py 102 15
Lib/zipfile.py 16 4
Misc/NEWS 3 0
File Doc/library/zipfile.rst changed (mode: 100644) (index a0de10cae3..4cde1dd76a)
... ... ZipFile Objects
132 132
133 133 .. class:: ZipFile(file, mode='r', compression=ZIP_STORED, allowZip64=True) .. class:: ZipFile(file, mode='r', compression=ZIP_STORED, allowZip64=True)
134 134
135 Open a ZIP file, where *file* can be either a path to a file (a string) or a
136 file-like object. The *mode* parameter should be ``'r'`` to read an existing
135 Open a ZIP file, where *file* can be a path to a file (a string), a
136 file-like object or a :term:`path-like object`.
137 The *mode* parameter should be ``'r'`` to read an existing
137 138 file, ``'w'`` to truncate and write a new file, ``'a'`` to append to an file, ``'w'`` to truncate and write a new file, ``'a'`` to append to an
138 139 existing file, or ``'x'`` to exclusively create and write a new file. existing file, or ``'x'`` to exclusively create and write a new file.
139 140 If *mode* is ``'x'`` and *file* refers to an existing file, If *mode* is ``'x'`` and *file* refers to an existing file,
 
... ... ZipFile Objects
183 184 Previously, a plain :exc:`RuntimeError` was raised for unrecognized Previously, a plain :exc:`RuntimeError` was raised for unrecognized
184 185 compression values. compression values.
185 186
187 .. versionchanged:: 3.6.2
188 The *file* parameter accepts a :term:`path-like object`.
189
186 190
187 191 .. method:: ZipFile.close() .. method:: ZipFile.close()
188 192
 
... ... ZipFile Objects
284 288 Calling :meth:`extract` on a closed ZipFile will raise a Calling :meth:`extract` on a closed ZipFile will raise a
285 289 :exc:`ValueError`. Previously, a :exc:`RuntimeError` was raised. :exc:`ValueError`. Previously, a :exc:`RuntimeError` was raised.
286 290
291 .. versionchanged:: 3.6.2
292 The *path* parameter accepts a :term:`path-like object`.
293
287 294
288 295 .. method:: ZipFile.extractall(path=None, members=None, pwd=None) .. method:: ZipFile.extractall(path=None, members=None, pwd=None)
289 296
 
... ... ZipFile Objects
304 311 Calling :meth:`extractall` on a closed ZipFile will raise a Calling :meth:`extractall` on a closed ZipFile will raise a
305 312 :exc:`ValueError`. Previously, a :exc:`RuntimeError` was raised. :exc:`ValueError`. Previously, a :exc:`RuntimeError` was raised.
306 313
314 .. versionchanged:: 3.6.2
315 The *path* parameter accepts a :term:`path-like object`.
316
307 317
308 318 .. method:: ZipFile.printdir() .. method:: ZipFile.printdir()
309 319
 
... ... ZipFile Objects
403 413
404 414 The following data attributes are also available: The following data attributes are also available:
405 415
416 .. attribute:: ZipFile.filename
417
418 Name of the ZIP file.
406 419
407 420 .. attribute:: ZipFile.debug .. attribute:: ZipFile.debug
408 421
 
... ... The :class:`PyZipFile` constructor takes the same parameters as the
488 501 .. versionadded:: 3.4 .. versionadded:: 3.4
489 502 The *filterfunc* parameter. The *filterfunc* parameter.
490 503
504 .. versionchanged:: 3.6.2
505 The *pathname* parameter accepts a :term:`path-like object`.
506
491 507
492 508 .. _zipinfo-objects: .. _zipinfo-objects:
493 509
 
... ... file:
514 530
515 531 .. versionadded:: 3.6 .. versionadded:: 3.6
516 532
533 .. versionchanged:: 3.6.2
534 The *filename* parameter accepts a :term:`path-like object`.
535
536
517 537 Instances have the following methods and attributes: Instances have the following methods and attributes:
518 538
519 539 .. method:: ZipInfo.is_dir() .. method:: ZipInfo.is_dir()
File Lib/test/test_zipfile.py changed (mode: 100644) (index 0a19d76f42..f3d993608f)
... ... import contextlib
2 2 import io import io
3 3 import os import os
4 4 import importlib.util import importlib.util
5 import pathlib
5 6 import posixpath import posixpath
6 7 import time import time
7 8 import struct import struct
 
... ... from tempfile import TemporaryFile
13 14 from random import randint, random, getrandbits from random import randint, random, getrandbits
14 15
15 16 from test.support import script_helper from test.support import script_helper
16 from test.support import (TESTFN, findfile, unlink, rmtree, temp_dir,
17 from test.support import (TESTFN, findfile, unlink, rmtree, temp_dir, temp_cwd,
17 18 requires_zlib, requires_bz2, requires_lzma, requires_zlib, requires_bz2, requires_lzma,
18 19 captured_stdout, check_warnings) captured_stdout, check_warnings)
19 20
 
... ... class AbstractTestsWithSourceFile:
148 149 for f in get_files(self): for f in get_files(self):
149 150 self.zip_open_test(f, self.compression) self.zip_open_test(f, self.compression)
150 151
152 def test_open_with_pathlike(self):
153 path = pathlib.Path(TESTFN2)
154 self.zip_open_test(path, self.compression)
155 with zipfile.ZipFile(path, "r", self.compression) as zipfp:
156 self.assertIsInstance(zipfp.filename, str)
157
151 158 def zip_random_open_test(self, f, compression): def zip_random_open_test(self, f, compression):
152 159 self.make_test_archive(f, compression) self.make_test_archive(f, compression)
153 160
 
... ... class PyZipFileTests(unittest.TestCase):
906 913 finally: finally:
907 914 rmtree(TESTFN2) rmtree(TESTFN2)
908 915
916 def test_write_pathlike(self):
917 os.mkdir(TESTFN2)
918 try:
919 with open(os.path.join(TESTFN2, "mod1.py"), "w") as fp:
920 fp.write("print(42)\n")
921
922 with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp:
923 zipfp.writepy(pathlib.Path(TESTFN2) / "mod1.py")
924 names = zipfp.namelist()
925 self.assertCompiledIn('mod1.py', names)
926 finally:
927 rmtree(TESTFN2)
928
909 929
910 930 class ExtractTests(unittest.TestCase): class ExtractTests(unittest.TestCase):
911 def test_extract(self):
931
932 def make_test_file(self):
912 933 with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp: with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp:
913 934 for fpath, fdata in SMALL_TEST_DATA: for fpath, fdata in SMALL_TEST_DATA:
914 935 zipfp.writestr(fpath, fdata) zipfp.writestr(fpath, fdata)
915 936
937 def test_extract(self):
938 with temp_cwd():
939 self.make_test_file()
940 with zipfile.ZipFile(TESTFN2, "r") as zipfp:
941 for fpath, fdata in SMALL_TEST_DATA:
942 writtenfile = zipfp.extract(fpath)
943
944 # make sure it was written to the right place
945 correctfile = os.path.join(os.getcwd(), fpath)
946 correctfile = os.path.normpath(correctfile)
947
948 self.assertEqual(writtenfile, correctfile)
949
950 # make sure correct data is in correct file
951 with open(writtenfile, "rb") as f:
952 self.assertEqual(fdata.encode(), f.read())
953
954 unlink(writtenfile)
955
956 def _test_extract_with_target(self, target):
957 self.make_test_file()
916 958 with zipfile.ZipFile(TESTFN2, "r") as zipfp: with zipfile.ZipFile(TESTFN2, "r") as zipfp:
917 959 for fpath, fdata in SMALL_TEST_DATA: for fpath, fdata in SMALL_TEST_DATA:
918 writtenfile = zipfp.extract(fpath)
960 writtenfile = zipfp.extract(fpath, target)
919 961
920 962 # make sure it was written to the right place # make sure it was written to the right place
921 correctfile = os.path.join(os.getcwd(), fpath)
963 correctfile = os.path.join(target, fpath)
922 964 correctfile = os.path.normpath(correctfile) correctfile = os.path.normpath(correctfile)
923
924 self.assertEqual(writtenfile, correctfile)
965 self.assertTrue(os.path.samefile(writtenfile, correctfile), (writtenfile, target))
925 966
926 967 # make sure correct data is in correct file # make sure correct data is in correct file
927 968 with open(writtenfile, "rb") as f: with open(writtenfile, "rb") as f:
 
... ... class ExtractTests(unittest.TestCase):
929 970
930 971 unlink(writtenfile) unlink(writtenfile)
931 972
932 # remove the test file subdirectories
933 rmtree(os.path.join(os.getcwd(), 'ziptest2dir'))
973 unlink(TESTFN2)
974
975 def test_extract_with_target(self):
976 with temp_dir() as extdir:
977 self._test_extract_with_target(extdir)
978
979 def test_extract_with_target_pathlike(self):
980 with temp_dir() as extdir:
981 self._test_extract_with_target(pathlib.Path(extdir))
934 982
935 983 def test_extract_all(self): def test_extract_all(self):
936 with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp:
937 for fpath, fdata in SMALL_TEST_DATA:
938 zipfp.writestr(fpath, fdata)
984 with temp_cwd():
985 self.make_test_file()
986 with zipfile.ZipFile(TESTFN2, "r") as zipfp:
987 zipfp.extractall()
988 for fpath, fdata in SMALL_TEST_DATA:
989 outfile = os.path.join(os.getcwd(), fpath)
990
991 with open(outfile, "rb") as f:
992 self.assertEqual(fdata.encode(), f.read())
939 993
994 unlink(outfile)
995
996 def _test_extract_all_with_target(self, target):
997 self.make_test_file()
940 998 with zipfile.ZipFile(TESTFN2, "r") as zipfp: with zipfile.ZipFile(TESTFN2, "r") as zipfp:
941 zipfp.extractall()
999 zipfp.extractall(target)
942 1000 for fpath, fdata in SMALL_TEST_DATA: for fpath, fdata in SMALL_TEST_DATA:
943 outfile = os.path.join(os.getcwd(), fpath)
1001 outfile = os.path.join(target, fpath)
944 1002
945 1003 with open(outfile, "rb") as f: with open(outfile, "rb") as f:
946 1004 self.assertEqual(fdata.encode(), f.read()) self.assertEqual(fdata.encode(), f.read())
947 1005
948 1006 unlink(outfile) unlink(outfile)
949 1007
950 # remove the test file subdirectories
951 rmtree(os.path.join(os.getcwd(), 'ziptest2dir'))
1008 unlink(TESTFN2)
1009
1010 def test_extract_all_with_target(self):
1011 with temp_dir() as extdir:
1012 self._test_extract_all_with_target(extdir)
1013
1014 def test_extract_all_with_target_pathlike(self):
1015 with temp_dir() as extdir:
1016 self._test_extract_all_with_target(pathlib.Path(extdir))
952 1017
953 1018 def check_file(self, filename, content): def check_file(self, filename, content):
954 1019 self.assertTrue(os.path.isfile(filename)) self.assertTrue(os.path.isfile(filename))
 
... ... class OtherTests(unittest.TestCase):
1188 1253 with open(TESTFN, "w") as fp: with open(TESTFN, "w") as fp:
1189 1254 fp.write("this is not a legal zip file\n") fp.write("this is not a legal zip file\n")
1190 1255 self.assertFalse(zipfile.is_zipfile(TESTFN)) self.assertFalse(zipfile.is_zipfile(TESTFN))
1256 # - passing a path-like object
1257 self.assertFalse(zipfile.is_zipfile(pathlib.Path(TESTFN)))
1191 1258 # - passing a file object # - passing a file object
1192 1259 with open(TESTFN, "rb") as fp: with open(TESTFN, "rb") as fp:
1193 1260 self.assertFalse(zipfile.is_zipfile(fp)) self.assertFalse(zipfile.is_zipfile(fp))
 
... ... class ZipInfoTests(unittest.TestCase):
2033 2100 zi = zipfile.ZipInfo.from_file(__file__) zi = zipfile.ZipInfo.from_file(__file__)
2034 2101 self.assertEqual(posixpath.basename(zi.filename), 'test_zipfile.py') self.assertEqual(posixpath.basename(zi.filename), 'test_zipfile.py')
2035 2102 self.assertFalse(zi.is_dir()) self.assertFalse(zi.is_dir())
2103 self.assertEqual(zi.file_size, os.path.getsize(__file__))
2104
2105 def test_from_file_pathlike(self):
2106 zi = zipfile.ZipInfo.from_file(pathlib.Path(__file__))
2107 self.assertEqual(posixpath.basename(zi.filename), 'test_zipfile.py')
2108 self.assertFalse(zi.is_dir())
2109 self.assertEqual(zi.file_size, os.path.getsize(__file__))
2110
2111 def test_from_file_bytes(self):
2112 zi = zipfile.ZipInfo.from_file(os.fsencode(__file__), 'test')
2113 self.assertEqual(posixpath.basename(zi.filename), 'test')
2114 self.assertFalse(zi.is_dir())
2115 self.assertEqual(zi.file_size, os.path.getsize(__file__))
2116
2117 def test_from_file_fileno(self):
2118 with open(__file__, 'rb') as f:
2119 zi = zipfile.ZipInfo.from_file(f.fileno(), 'test')
2120 self.assertEqual(posixpath.basename(zi.filename), 'test')
2121 self.assertFalse(zi.is_dir())
2122 self.assertEqual(zi.file_size, os.path.getsize(__file__))
2036 2123
2037 2124 def test_from_dir(self): def test_from_dir(self):
2038 2125 dirpath = os.path.dirname(os.path.abspath(__file__)) dirpath = os.path.dirname(os.path.abspath(__file__))
File Lib/zipfile.py changed (mode: 100644) (index 93171358e4..b5c16dbc12)
... ... class ZipInfo (object):
478 478 this will be the same as filename, but without a drive letter and with this will be the same as filename, but without a drive letter and with
479 479 leading path separators removed). leading path separators removed).
480 480 """ """
481 if isinstance(filename, os.PathLike):
482 filename = os.fspath(filename)
481 483 st = os.stat(filename) st = os.stat(filename)
482 484 isdir = stat.S_ISDIR(st.st_mode) isdir = stat.S_ISDIR(st.st_mode)
483 485 mtime = time.localtime(st.st_mtime) mtime = time.localtime(st.st_mtime)
 
... ... class ZipFile:
1069 1071 self._comment = b'' self._comment = b''
1070 1072
1071 1073 # Check if we were passed a file-like object # Check if we were passed a file-like object
1074 if isinstance(file, os.PathLike):
1075 file = os.fspath(file)
1072 1076 if isinstance(file, str): if isinstance(file, str):
1073 1077 # No, it's a filename # No, it's a filename
1074 1078 self._filePassed = 0 self._filePassed = 0
 
... ... class ZipFile:
1469 1473 as possible. `member' may be a filename or a ZipInfo object. You can as possible. `member' may be a filename or a ZipInfo object. You can
1470 1474 specify a different directory using `path'. specify a different directory using `path'.
1471 1475 """ """
1472 if not isinstance(member, ZipInfo):
1473 member = self.getinfo(member)
1474
1475 1476 if path is None: if path is None:
1476 1477 path = os.getcwd() path = os.getcwd()
1478 else:
1479 path = os.fspath(path)
1477 1480
1478 1481 return self._extract_member(member, path, pwd) return self._extract_member(member, path, pwd)
1479 1482
 
... ... class ZipFile:
1486 1489 if members is None: if members is None:
1487 1490 members = self.namelist() members = self.namelist()
1488 1491
1492 if path is None:
1493 path = os.getcwd()
1494 else:
1495 path = os.fspath(path)
1496
1489 1497 for zipinfo in members: for zipinfo in members:
1490 self.extract(zipinfo, path, pwd)
1498 self._extract_member(zipinfo, path, pwd)
1491 1499
1492 1500 @classmethod @classmethod
1493 1501 def _sanitize_windows_name(cls, arcname, pathsep): def _sanitize_windows_name(cls, arcname, pathsep):
 
... ... class ZipFile:
1508 1516 """Extract the ZipInfo object 'member' to a physical """Extract the ZipInfo object 'member' to a physical
1509 1517 file on the path targetpath. file on the path targetpath.
1510 1518 """ """
1519 if not isinstance(member, ZipInfo):
1520 member = self.getinfo(member)
1521
1511 1522 # build the destination pathname, replacing # build the destination pathname, replacing
1512 1523 # forward slashes to platform specific separators. # forward slashes to platform specific separators.
1513 1524 arcname = member.filename.replace('/', os.path.sep) arcname = member.filename.replace('/', os.path.sep)
 
... ... class PyZipFile(ZipFile):
1800 1811 If filterfunc(pathname) is given, it is called with every argument. If filterfunc(pathname) is given, it is called with every argument.
1801 1812 When it is False, the file or directory is skipped. When it is False, the file or directory is skipped.
1802 1813 """ """
1814 pathname = os.fspath(pathname)
1803 1815 if filterfunc and not filterfunc(pathname): if filterfunc and not filterfunc(pathname):
1804 1816 if self.debug: if self.debug:
1805 1817 label = 'path' if os.path.isdir(pathname) else 'file' label = 'path' if os.path.isdir(pathname) else 'file'
File Misc/NEWS changed (mode: 100644) (index a8c8ec7d58..503ed83e46)
... ... Extension Modules
270 270 Library Library
271 271 ------- -------
272 272
273 - bpo-28231: The zipfile module now accepts path-like objects for external
274 paths.
275
273 276 - bpo-26915: index() and count() methods of collections.abc.Sequence now - bpo-26915: index() and count() methods of collections.abc.Sequence now
274 277 check identity before checking equality when do comparisons. check identity before checking equality when do comparisons.
275 278
Hints:
Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://rocketgit.com/user/benf_wspdigital/cpython

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@ssh.rocketgit.com/user/benf_wspdigital/cpython

Clone this repository using git:
git clone git://git.rocketgit.com/user/benf_wspdigital/cpython

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a merge request:
... clone the repository ...
... make some changes and some commits ...
git push origin main