diff options
author | Jason R. Coombs <jaraco@jaraco.com> | 2020-10-03 14:58:39 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-10-03 14:58:39 (GMT) |
commit | ebbe8033b1c61854c4b623aaf9c3e170d179f875 (patch) | |
tree | 97b9d2a5967e0b24108119340e14b4f005473408 | |
parent | c111355480ff51b50a671679c5099f534cb01cae (diff) | |
download | cpython-ebbe8033b1c61854c4b623aaf9c3e170d179f875.zip cpython-ebbe8033b1c61854c4b623aaf9c3e170d179f875.tar.gz cpython-ebbe8033b1c61854c4b623aaf9c3e170d179f875.tar.bz2 |
bpo-40564: Avoid copying state from extant ZipFile. (GH-22371)
bpo-40564: Avoid copying state from extant ZipFile.
-rw-r--r-- | Lib/test/test_zipfile.py | 33 | ||||
-rw-r--r-- | Lib/zipfile.py | 23 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2020-09-23-03-33-37.bpo-40564.iXQqMq.rst | 1 |
3 files changed, 51 insertions, 6 deletions
diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py index 687e43d..3bb9ce9 100644 --- a/Lib/test/test_zipfile.py +++ b/Lib/test/test_zipfile.py @@ -2889,6 +2889,33 @@ class TestPath(unittest.TestCase): data = strm.read() assert data == "content of a" + def test_open_write(self): + """ + If the zipfile is open for write, it should be possible to + write bytes or text to it. + """ + zf = zipfile.Path(zipfile.ZipFile(io.BytesIO(), mode='w')) + with zf.joinpath('file.bin').open('wb') as strm: + strm.write(b'binary contents') + with zf.joinpath('file.txt').open('w') as strm: + strm.write('text file') + + def test_open_extant_directory(self): + """ + Attempting to open a directory raises IsADirectoryError. + """ + zf = zipfile.Path(add_dirs(build_alpharep_fixture())) + with self.assertRaises(IsADirectoryError): + zf.joinpath('b').open() + + def test_open_missing_directory(self): + """ + Attempting to open a missing directory raises FileNotFoundError. + """ + zf = zipfile.Path(add_dirs(build_alpharep_fixture())) + with self.assertRaises(FileNotFoundError): + zf.joinpath('z').open() + def test_read(self): for alpharep in self.zipfile_alpharep(): root = zipfile.Path(alpharep) @@ -2986,6 +3013,12 @@ class TestPath(unittest.TestCase): data = ['/'.join(string.ascii_lowercase + str(n)) for n in range(10000)] zipfile.CompleteDirs._implied_dirs(data) + def test_read_does_not_close(self): + for alpharep in self.zipfile_ondisk(): + with zipfile.ZipFile(alpharep) as file: + for rep in range(2): + zipfile.Path(file, 'a.txt').read_text() + if __name__ == "__main__": unittest.main() diff --git a/Lib/zipfile.py b/Lib/zipfile.py index 816f858..da3e40e 100644 --- a/Lib/zipfile.py +++ b/Lib/zipfile.py @@ -2197,13 +2197,12 @@ class CompleteDirs(ZipFile): if not isinstance(source, ZipFile): return cls(source) - # Only allow for FastPath when supplied zipfile is read-only + # Only allow for FastLookup when supplied zipfile is read-only if 'r' not in source.mode: cls = CompleteDirs - res = cls.__new__(cls) - vars(res).update(vars(source)) - return res + source.__class__ = cls + return source class FastLookup(CompleteDirs): @@ -2292,17 +2291,29 @@ class Path: __repr = "{self.__class__.__name__}({self.root.filename!r}, {self.at!r})" def __init__(self, root, at=""): + """ + Construct a Path from a ZipFile or filename. + + Note: When the source is an existing ZipFile object, + its type (__class__) will be mutated to a + specialized type. If the caller wishes to retain the + original type, the caller should either create a + separate ZipFile object or pass a filename. + """ self.root = FastLookup.make(root) self.at = at - def open(self, mode='r', *args, **kwargs): + def open(self, mode='r', *args, pwd=None, **kwargs): """ Open this entry as text or binary following the semantics of ``pathlib.Path.open()`` by passing arguments through to io.TextIOWrapper(). """ - pwd = kwargs.pop('pwd', None) + if self.is_dir(): + raise IsADirectoryError(self) zip_mode = mode[0] + if not self.exists() and zip_mode == 'r': + raise FileNotFoundError(self) stream = self.root.open(self.at, zip_mode, pwd=pwd) if 'b' in mode: if args or kwargs: diff --git a/Misc/NEWS.d/next/Library/2020-09-23-03-33-37.bpo-40564.iXQqMq.rst b/Misc/NEWS.d/next/Library/2020-09-23-03-33-37.bpo-40564.iXQqMq.rst new file mode 100644 index 0000000..0855347 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-09-23-03-33-37.bpo-40564.iXQqMq.rst @@ -0,0 +1 @@ +In ``zipfile.Path``, mutate the passed ZipFile object type instead of making a copy. Prevents issues when both the local copy and the caller’s copy attempt to close the same file handle.
\ No newline at end of file |